网站标题psd,网站怎么获得流量,wordpress安装地图代码,深圳网站建设推广论坛Redis高可用 文章目录 Redis高可用一#xff1a;主从复制 读写分离1#xff1a;主从复制的作用2#xff1a;主从复制原理2.1#xff1a;全量复制2.2#xff1a;增量复制#xff08;环形缓冲区#xff09; 3#xff1a;主从复制实际演示3.1#xff1a;基本流程准…Redis高可用 文章目录 Redis高可用一主从复制 读写分离1主从复制的作用2主从复制原理2.1全量复制2.2增量复制环形缓冲区 3主从复制实际演示3.1基本流程准备好两个redis服务器配置修改启动两个服务器查看主从状态主从配置指令测试下 3.2一些说明 4读写分离问题 二哨兵模式1基本工作流程1.1哨兵集群组建(消息pub/sub)1.2下线判断(客观下线)1.3主哨兵选举(raft半数通过)1.4新主库的选出(健康完整的)1.5故障转移(易主通知复制) 2哨兵模式实际演示2.1基本流程创建哨兵服务器修改哨兵服务器的配置文件启动哨兵服务器主节点挂掉测试要是哨兵也挂了咋办 2.2Jedis感知 一集群1集群的引入2设计目标3常用概念3.1哈希槽 hash_slot3.2hash_tags分片nodes属性3.3集群总线 4搭建实例4.1基本流程创建六个redis服务器配置修改全部启动主从集群分配 4.2其他说明节点信息让某一个主节点挂掉会怎么样主从都挂了怎么办Jedis 一主从复制 读写分离
主从复制主从复制是指将一台redis上的服务的数据复制到另外的redis上前者称为主节点后者称为从节点【单向性】
1主从复制的作用 数据冗余 主从复制实现了数据的热备份是持久化之外的一种数据冗余的方式 故障恢复 当主节点出现问题的时候可以由从节点提供服务实现快速的故障恢复实际上是一种服务的冗余。 负载均衡 在主从复制的基础上配合读写分离可以由主节点提供写服务从节点提供读服务分担服务器的负载尤其是在写少读多的情况下通过配置多个从节点分担读负载可以大大的提高redis服务器的并发量。 高可用基石 是哨兵模式和集群可以实现的基础因此可以说是高可用的基础。
2主从复制原理
和MongoDB的初始同步和复制很像
2.1全量复制
当我们启动多个redis实例的时候就可以使用replicaof(5.0以前是slaveof)形成主库和从库的关系
会按照三个步骤完成数据的第一次同步 确立主从关系 例如现在有实例 1ip172.16.19.3和实例 2ip172.16.19.5
我们在实例 2 上执行以下这个命令后实例 2 就变成了实例 1 的从库并从实例 1 上复制数据
replicaof 172.16.19.3 6379全量复制的三个阶段 第一步建立连接协商同步
主要是为全量复制做准备。在这一步从库和主库建立起连接并告诉主库即将进行同步主库确认回复后主从库间就可以开始同步了。具体来说从库给主库发送 psync 命令表示要进行数据同步主库根据这个命令的参数来启动复制。psync 命令包含了主库的 runID 和复制进度 offset 两个参数。runID是每个 Redis 实例启动时都会自动生成的一个随机 ID用来唯一标记这个实例。当从库和主库第一次复制时因为不知道主库的 runID所以将 runID 设为“”。offset此时设为 -1表示第一次复制。主库收到 psync 命令后会用 FULLRESYNC 响应命令带上两个参数主库 runID 和主库目前的复制进度 offset返回给从库。从库收到响应后会记录下这两个参数。注意FULLRESYNC 响应表示第一次复制采用的全量复制也就是说主库会把当前所有的数据都复制给从库。
第二步将主库所有同步数据给从库
从库收到数据后在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件具体来说主库执行 bgsave 命令生成 RDB 文件接着将文件发给从库。从库接收到 RDB 文件后会先清空当前数据库然后加载 RDB 文件。这是因为从库在通过 replicaof 命令开始和主库同步前可能保存了其他数据。为了避免之前数据的影响从库需要先把当前数据库清空。在主库将数据同步给从库的过程中主库不会被阻塞仍然可以正常接收请求。否则Redis 的服务就被中断了。但是这些请求中的写操作并没有记录到刚刚生成的 RDB 文件中。为了保证主从库的数据一致性主库会在内存中用专门的 replication buffer记录 RDB 文件生成后收到的所有写操作。
第三步主库发送新的写命令给从库
当主库完成 RDB 文件发送后就会把此时 replication buffer 中的修改操作发给从库从库再重新执行这些操作。这样一来主从库就实现同步了
2.2增量复制环形缓冲区
如果主从库在命令传播时出现了网络闪断那么从库就会和主库重新进行一次全量复制开销非常大。
从 Redis 2.8 开始网络断了之后主从库会采用增量复制的方式继续同步。 repl_backlog_buffer 它是为了从库断开之后如何找到主从差异数据而设计的环形缓冲区从而避免全量复制带来的性能开销
⚠️ 如果从库断开时间太久repl_backlog_buffer环形缓冲区被主库的写命令覆盖了那么从库连上主库后只能乖乖地进行一次全量复制
所以repl_backlog_buffer配置尽量大一些可以降低主从断开后全量复制的概率 repl_buffer 我们每个client连上Redis后Redis都会分配一个client buffer所有数据交互都是通过这个buffer进行的 注意点 一个从库如果和主库断连时间过长造成它在主库repl_backlog_buffer的slave_repl_offset位置上的数据已经被覆盖掉了此时从库和主库间将进行全量复制。
每个从库会记录自己的slave_repl_offset每个从库的复制进度也不一定相同。
在和主库重连进行恢复时从库会通过psync命令把自己记录的slave_repl_offset发给主库主库会根据从库各自的复制进度来决定这个从库可以进行增量复制还是全量复制
3主从复制实际演示
3.1基本流程
准备好两个redis服务器 配置修改 分别将端口号改成6001【redis-master】6002【redis-slave】 - 配置文件redis.windows.conf 启动两个服务器 查看主从状态
输入info replication命令来查看当前的主从状态可以看到默认的角色为master也就是说所有的服务器在启动之后都是主节点的状态。
主从配置指令
我们希望让6002作为从节点通过一个命令即可输入replicaof 127.0.0.1 6001 每次都去敲个命令配置主从太麻烦了我们可以直接在配置文件中配置这个命令 命令后就会将6001服务器作为主节点而当前节点作为6001的从节点并且角色也会变成slave 可以看到从节点信息中已经出现了6002服务器也就是说现在我们的6001和6002就形成了主从关系
主服务器和从服务器都会维护一个复制偏移量主服务器每次向从服务器中传递 N 个字节的时候会将自己的复制偏移量加上 N。从服务器中收到主服务器的 N 个字节的数据就会将自己额复制偏移量加上 N通过主从服务器的偏移量对比可以很清楚的知道主从服务器的数据是否处于一致如果不一致就需要进行增量同步了。
测试下 3.2一些说明
⚠️ 从节点压根就没办法进行数据插入节点的模式为只读模式 那么如果我们现在不想让6002作为6001的从节点了呢
可以在6002执行replicaof no one【我不是别人的从服务器】即可变回到master 全量复制和增量复制 接着我们再次让6002变成6001的从节点【或者在创建一个6003作为6001的从节点】
可以看到在连接之后也会直接同步主节点的数据因此无论是已经处于从节点状态还是刚刚启动完成的服务器都会从主节点同步数据实际上整个同步流程为
从节点执行replicaof ip port命令后从节点会保存主节点相关的地址信息。从节点通过每秒运行的定时任务发现配置了新的主节点后会尝试与该节点建立网络连接专门用于接收主节点发送的复制命令。连接成功后第一次会将主节点的数据进行全量复制之后采用增量复制持续将新来的写命令同步给从节点。 ⚠️ 当我们的主节点关闭后从节点依然可以读取数据但是从节点会提示报错信息 再次启动后恢复正常 除了作为Master节点的从节点外我们还可以将其作为从节点的从节点 采用这种方式优点肯定是显而易见的但是缺点也很明显整个传播链路一旦中途出现问题那么就会导致后面的从节点无法及时同步。
4读写分离问题 延迟与不一致问题 由于主从复制的命令传播是异步的延迟与数据的不一致不可避免。
如果应用对数据不一致的接受程度程度较低可能的优化措施包括
优化主从节点之间的网络环境如在同机房部署监控主从节点延迟通过offset判断如果从节点延迟过大通知应用不再通过该从节点读取数据使用集群同时扩展写负载和读负载等。 数据过期问题 在单机版Redis中存在两种删除策略
惰性删除服务器不会主动删除数据只有当客户端查询某个数据时服务器判断该数据是否过期如果过期则删除。定期删除服务器执行定时任务删除过期数据但是考虑到内存和CPU的折中
Redis 3.2中从节点在读取数据时增加了对数据是否过期的判断如果该数据已过期则不返回给客户端
将Redis升级到3.2可以解决数据过期问题。 故障切换问题 在没有使用哨兵的读写分离场景下应用针对读和写分别连接不同的Redis节点
当主节点或从节点出现问题而发生更改时需要及时修改应用程序读写Redis数据的连接
连接的切换可以手动进行或者自己写监控程序进行切换但前者响应慢、容易出错后者实现复杂成本都不算低 不持久化的主服务器自动重启非常危险 我们设置节点A为主服务器关闭持久化节点B和C从节点A复制数据。这时出现了一个崩溃但Redis具有自动重启系统重启了进程因为关闭了持久化节点重启后只有一个空的数据集。节点B和C从节点A进行复制现在节点A是空的所以节点B和C上的复制数据也会被删除。
二哨兵模式 哨兵作用 监控 - 哨兵会不断地检查主节点和从节点是否运作正常 故障自动转移 - 当主节点不能正常工作时哨兵会开始自动故障转移操作它会将失效主节点的其中一个从节点升级为新的主节点并让其他从节点改为复制新的主节点 配置提供者 - 客户端在初始化时通过连接哨兵来获得当前Redis服务的主节点地址 通知 - 哨兵可以将故障转移的结果发送给客户端 监控和自动故障转移功能使得哨兵可以及时发现主节点故障并完成转移 而配置提供者和通知功能则需要在与客户端的交互中才能体现 1基本工作流程
1.1哨兵集群组建(消息pub/sub)
哨兵实例之间可以相互发现要归功于 Redis 提供的 pub/sub 机制也就是发布 / 订阅机制【消息传递】 举一个简单的栗子 在主从集群中主库上有一个名为__sentinel__:hello的频道不同哨兵就是通过它来相互发现实现互相通信的。
哨兵 1 把自己的 IP172.16.19.3和端口26579发布到__sentinel__:hello频道上
哨兵 2 和 3 订阅了该频道。
那么此时哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。
然后哨兵 2、3 可以和哨兵 1 建立网络连接。 通过这个方式哨兵 2 和 3 也可以建立网络连接这样一来哨兵集群就形成了。
它们相互间可以通过网络连接进行通信比如说对主库有没有下线这件事儿进行判断和协商。
1.2下线判断(客观下线)
首先要理解两个概念主观下线和客观下线
主观下线任何一个哨兵都是可以监控探测并作出Redis节点下线的判断客观下线有哨兵集群共同决定Redis节点是否下线
当某个哨兵如下图中的哨兵2判断主库“主观下线”后就会给其他哨兵发送 is-master-down-by-addr 命令。
接着其他哨兵会根据自己和主库的连接情况做出 Y 或 N 的响应Y 相当于赞成票N 相当于反对票 如果赞成票数是大于等于哨兵配置文件中的 quorum 配置项比如这里如果是quorum2, 则可以判定主库客观下线了
1.3主哨兵选举(raft半数通过)
判断完主库下线后由哪个哨兵节点来执行主从切换呢 为什么需要哨兵的选举 为了避免单节点哨兵的存在就需要哨兵集群而集群的出现表示必然要面临共识问题【选举问题】
故障转移和通知都由主哨兵节点进行负责即可。 哨兵的选举是怎样的 哨兵的选举遵循的是著名的raft算法
当投票数 r num / 2 1 就进行当选 如何成为leader哨兵节点 第一拿到半数以上的赞成票第二拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值
以 3 个哨兵为例假设此时的 quorum 设置为 2那么任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票就可以了。
1.4新主库的选出(健康完整的)
过滤掉不健康的下线或断线没有回复过哨兵ping响应的从节点选择salve-priority从节点优先级最高redis.conf的选择复制偏移量最大指复制最完整的从节点 1.5故障转移(易主通知复制)
假设现在有这样的情况主库节点已经客观下线了哨兵节点3被选为了哨兵leader并且决定新的主库为从节点slave_1 故障转移的流程 将slave-1脱离原从节点5.0 中应该是replicaof no one)升级主节点将从节点slave-2指向新的主节点通知客户端主节点已更换将原主节点oldMaster变成从节点指向新的主节点 转移之后 2哨兵模式实际演示
2.1基本流程 创建哨兵服务器 修改哨兵服务器的配置文件
# 其他的全部都删除掉只写这一行
# 其中第一个和第二个参数是固定的
# 第三个参数是为监控对象名称随意
# 第四个第五个参数就是主节点的相关信息包括IP地址和端口
# 最后一个参数是哨兵支持数大于等于多少sentinel monitor cuihaida-sentinel 127.0.0.1 6001 1启动哨兵服务器 可以看到以哨兵模式启动后会自动监控主节点然后还会显示那些节点是作为从节点存在的。
主节点挂掉测试
一开始从节点还在常规报错因为会认为主节点只是网络卡顿了没必要急着切换主节点 但是一段时间之后哨兵发现还是连接不上主节点便开始重新选主 6003已经成为新的主节点 再次启动6001之后发现他已经变成了6003的从节点 那么这个选举规则是怎样的呢是在所有的从节点中随机选取还是遵循某种规则呢
首先会根据优先级进行选择可以在配置文件中进行配置添加replica-priority配置项默认是100越小表示优先级越高。如果优先级一样那就选择偏移量最大的要是还选不出来那就选择runid启动时随机生成的最小的。
要是哨兵也挂了咋办
咱们可以多安排几个哨兵只需要把哨兵的配置复制一下然后修改端口这样就可以同时启动多个哨兵了我们启动3个哨兵一主二从三哨兵这里我们吧最后一个值改为2
sentinel monitor cuihaida 192.168.0.8 6001 2这个值实际上代表的是当有几个哨兵认为主节点挂掉时就判断主节点真的挂掉了
2.2Jedis感知 在哨兵重新选举新的主节点之后我们Java中的Redis的客户端怎么感知到呢? dependenciesdependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion4.2.1/version/dependency
/dependenciespublic class Main {public static void main(String[] args) {try {//这里我们直接使用JedisSentinelPool来获取Master节点//需要把三个哨兵的地址都填入JedisSentinelPool pool new JedisSentinelPool(cuihaida, new HashSet(Arrays.asList(192.168.0.8:26741, 192.168.0.8:26740, 192.168.0.8:26739)))) {Jedis jedis pool.getResource(); //直接询问并得到Jedis对象这就是连接的Master节点jedis.set(test, 114514); //直接写入即可实际上就是向Master节点写入Jedis jedis2 pool.getResource(); //再次获取System.out.println(jedis2.get(test)); //读取操作} catch (Exception e) {e.printStackTrace();}}
}一集群
1集群的引入
如果我们服务器的内存不够用了但是现在我们的Redis又需要继续存储内容那么这个时候就可以利用集群来实现扩容。
因为单机的内存容量最大就那么多已经没办法再继续扩展了但是现在又需要存储更多的内容
这时我们就可以让N台机器上的Redis来分别存储各个部分的数据每个Redis可以存储1/N的数据量这样就实现了容量的横向扩展。
同时每台Redis还可以配一个从节点这样就可以更好地保证数据的安全性。 那么问题来现在用户来了一个写入的请求数据该写到哪个节点上呢 首先一个Redis集群包含16384个插槽集群中的每个Redis 实例负责维护一部分插槽以及插槽所映射的键值数据那么这个插槽是什么意思呢 实际上插槽就是键的Hash计算后的一个结果这里采用CRC16能得到16个bit位的数据也就是说算出来之后结果是0-65535之间再进行取模得到最终结果 Redis key的路由计算公式slot CRC16key % 16384 结果的值是多少就应该存放到对应维护的Redis下 比如Redis节点1负责0-25565的插槽而这时客户端插入了一个新的数据a10a在Hash计算后结果为666那么a就应该存放到1号Redis节点中。 简而言之本质上就是通过哈希算法将插入的数据分摊到各个节点的所以说哈希算法真的是处处都有用啊。 主从复制和哨兵机制保障了高可用就读写分离而言虽然slave节点扩展了主从的读并发能力但是写能力和存储能力是无法进行扩展就只能是master节点能够承载的上限。
如果面对海量数据那么必然需要构建master主节点分片)之间的集群同时必然需要吸收高可用主从复制和哨兵机制能力即每个master分片节点还需要有slave节点这是分布式系统中典型的纵向扩展集群的分片技术的体现
所以在Redis 3.0版本中对应的设计就是Redis Cluster 2设计目标
高性能可线性扩展至最多1000节点。集群中没有代理集群节点间使用异步复制没有归并操作
3常用概念
3.1哈希槽 hash_slot
redis分片没有使用一致性hash而是采用了hash槽的概念redis分片中有16284(214)个hash槽
每一个key通过校验之后对16284 mod决定放到那个槽里。分片中的每一个节点负责一部分槽。
比如集群中存在三个节点则可能存在的一种分配如下
节点A包含0到5500号哈希槽节点B包含5501到11000号哈希槽节点C包含11001到16384号哈希槽。
3.2hash_tags
hash_tags提供了一种策略用来将多个相关的key分配到相同的hash槽中这是实现muitl_key的基础。
hash_tags规则如下如果满足如下规则{和}之间的字符将用来计算hash槽以保证这样的key保存在同一个slot中
key包含一个{字符并且 如果在这个{的右面有一个}字符并且 如果在{和}之间存在至少一个字符
--------- 举几个例子 ----------
{user1000}.following和{user1000}.followers这两个key会被hash到相同的hash slot中因为只有user1000会被用来计算hash slot值。 foo{}{bar}这个key不会启用hash tag因为第一个{和}之间没有字符。 foozap这个key中的{bar部分会被用来计算hash slot foo{bar}{zap}这个key中的bar会被用来计算计算hash slot而zap不会分片nodes属性
每一个节点在分片中有唯一的名字
节点在配置文件中保存它的id并且永久的使用这个id直到被管理员使用CLUSTER RESET HARD命令重置节点。
节点id被用来在整个分集群中标识每个节点。一个节点可以修改自己的IP地址而不需要修改自己的id
节点ID不是唯一与节点绑定的信息但是他是唯一的一个总是保持全局一致的字段。
3.3集群总线
每个集群节点有一个额外的TCP端口用来接受其他节点的连接。
这个端口与用来接收client命令的普通TCP端口有一个固定的offset。该端口等于普通命令端口加上10000。
例如一个Redis在端口6379和客户端连接那么它的集群总线端口16379也会被打开。
节点到节点的通讯只使用集群总线同时使用集群总线协议有不同的类型和大小的帧组成的二进制协议。 集群拓扑 redis集群是一张全网拓扑节点与其他每个节点之间都保持着TCP连接。
在一个拥有N个节点的集群中每个节点由N-1个TCP传出连接和N-1个TCP传入连接。 这些TCP连接总是保持活性。
当一个节点在集群总线上发送了ping请求并期待对方回复pong如果没有得到回复在等待足够成时间以便将对方标记为不可达之前它将先尝试重新连接对方以刷新与对方的连接。
而在全网拓扑中的redis集群节点节点使用gossip协议和配置更新机制来避免在正常情况下节点之间交换过多的消息因此集群内交换的消息数目(相对节点数目)不是指数级的。 节点握手 节点总是接受集群总线端口的链接并且总是会回复ping请求即使ping来自一个不可信节点。
然而如果发送节点被认为不是当前集群的一部分所有其他包将被抛弃
节点认定其他节点是当前集群的一部分有两种方式
如果一个节点出现在了一条MEET消息中。一条MEET消息非常像一个ping消息但是它会强制接收者接受一个节点作为集群的一部分。节点只有在接收到系统管理员的如下命令后才会向其他节点发送MEET消息
cluster meet ip port如果一个被信任的节点gossip了某个节点那么接收到gossip消息的节点也会那个节点标记为集群的一部分。也就是说如果在集群中A知道B而B知道C最终B会发送gossip消息到A告诉A节点C是集群的一部分。这时A会把C注册未网络的一部分并尝试与C建立连接。
一旦我们把某个节点加入了连接图它们最终会自动形成一张全连接图。这意味着只要系统管理员强制加入了一条信任关系在某个节点上通过meet命令加入了一个新节点集群可以自动发现其他节点
4搭建实例
4.1基本流程
创建六个redis服务器 配置修改
修改三个节点的配置【主节点端口6001 6002 6003】【从节点端口7001,70027003】
bind 127.0.0.1注释掉【56行】保护模式关闭protected-mode no【75行】设置端口【79行】cluster-enabled yes解开注释 【707行】 改好一份复制redis.windows.conf到其他的服务器然后其他的服务器只需要改端口就可以了
全部启动 ⚠️ 要是起不来了把所有的持久化文件全部删除所有的节点内容必须是空的。 主从集群分配
然后输入redis-cli.exe --cluster create --cluster-replicas 1 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
这里的--cluster-replicas 1指的是每个节点配一个从节点 输入之后会为你展示客户端默认分配的方案并且会询问你当前的方案是否合理。可以看到6001/6002/6003都被选为主节点其他的为从节点我们直接输入yes即可 分配成功可以看到插槽的分配情况 5测试 随便连接一个节点尝试插入一个值 在插入时出现了一个错误实际上这就是因为a计算出来的哈希值插槽不归当前节点管我们得去管这个插槽的节点执行
通过上面的分配情况我们可以得到15495号插槽属于节点6003管理 6集群方式连接 以使用集群方式连接这样我们无论在哪个节点都可以插入只需要添加-c表示以集群模式访问 可以看到在6001节点成功对a的值进行了更新只不过还是被重定向到了6003节点进行插入
4.2其他说明
节点信息
我们可以输入cluster nodes命令来查看当前所有节点的信息 让某一个主节点挂掉会怎么样
现在我们把6001挂掉可以看到原本的6001从节点7001晋升为了新的主节点而之前的6001已经挂了 现在我们将6001重启可以看到6001变成了7001的从节点 主从都挂了怎么办
要是6001和7001都挂了 我们尝试插入新的数据可以看到当存在节点不可用时会无法插入新的数据
Jedis
// 我们需要用到JedisCluster对象
public class Main {public static void main(String[] args) {//和客户端一样随便连一个就行也可以多写几个构造方法有很多种可以选择try(JedisCluster cluster new JedisCluster(new HostAndPort(192.168.0.8, 6003))){System.out.println(集群实例数量cluster.getClusterNodes().size());cluster.set(a, yyds);System.out.println(cluster.get(a));}}
}