1、主从模式(Master-slave)
Redis 如何实现主从模式?
Redis 的从服务器在向主服务器发起同步时,一般会使用 SYNC 或 PSYNC(Redis 2.8版本)命令。
初次同步
当从服务器收到 SLAVEOF 命令后,会向其主服务器执行同步操作,进入主从复制流程。
- 从服务器向主服务器发起SYNC 或 PSYNC 命令
- 主服务器执行 BGSAVE命令,生成 RDB 文件,并使用缓存区记录从现在开始的所有写命令
- RDB 文件生成完成后,主服务器会将其发送给slave
- slave载入 RDB 文件,将自己的数据库状态同步更新为主服务器执行 BGSAVE命令时的状态。
- 主服务器将缓冲区的所有写命令发送给从服务器,从服务将执行这些写命令,数据库状态同步为主服务器最新状态。最后master每当接受到写命令都会将命令发送给slave,从而保证数据一致
当主从同步完成后,如果此时从服务器宕机了一段时间,重新上线后势必要重新同步一下主服务器,SYNC与 PSYNC命令的区别就在于断线后重复制阶段处理的方式不同。
- SYNC
从服务器重新向主服务器发起 SYNC命令,主服务器将所有数据再次重新生成RDB快照发给从服务器开始同步
- PSYNC
从服务器重新向主服务器发起 PSYNC命令。主服务器根据双方数据的偏差量判断是否是需要完整重同步还是仅将断线期间执行过的写命令发给从服务器。
PSYNC 如何实现部分重同步?
实现部分重同步主要靠三部分
1. 记录复制偏移量
主服务器与从服务器都会维护一个复制偏移量offset。
- 当主服务器向从服务器发送 N 个字节的数据后,会将自己的复制偏移量 +N。
- 当从服务器收到主服务器 N 个字节大小数据后,也会将自己的复制偏移量 +N。
当同步时主从双方数据是,这个偏移量是相等的。而一旦有个从服务器断线一段时间而少收到了部分数据。那么此时主从双方的服务器偏移量是不相等的,而他们的差值就是少传输的字节数量。如果少传输的数据量不是很大,没有超过主服务器的复制积压缓冲区大小,那么将会直接将缓冲区内容发送给从服务器避免完全重同步。反之还是需要完全重同步的。
2. 复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个先进先出的字节队列,默认大小是 1mb。每当向主服务器发送写命令时,主服务都会将这些数据存入这个队列。从服务器在重连时会将自己的复制偏移量发送给主服务器,如果该复制偏移量之后的数据存在于复制积压缓冲区中,则仅需要将之后的数据发送给从服务器即可。
3. 记录服务器 ID
当执行主从同步时,主服务器会将自己的服务器 ID (一般是自动生成的 UUID ) 发送给从服务器。从服务器在断线恢复后会判断该 ID 是否为当前连接的主服务器。如果是同一个 ID 则代表主服务器没变尝试部分重同步。如果不是同一个 ID 代表主服务有变动,则会与主服务器完全重同步。
缺点:
- Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦。
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
2、哨兵模式(sentinel)
Redis 主从模式虽然能做到很好的数据备份,但是他并不是高可用的。一旦主服务器点宕机后,只能通过人工去切换主服务器。
哨兵模式:用于监控redis集群中Master主服务器工作的状态,在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用。
实现原理
1.Sentinel 与主从服务器建立连接,有三个定时任务
- 1、每10秒每个Sentinel 会向主服务器发送 INFO 命令,主服务器则会返回主服务器本身的信息,以及其所有从服务器的信息。
- 2、每2秒每个Sentinel 通过 master 节点的 channel(名称为_sentinel_:hello) 交换信息(pub/sub):用来交互对节点的看法(后面会介绍的节点主观下线和客观下线)以及自身信息.
- 3、每1秒每个 Sentinel 对其他 Sentinel和 redis 执行 ping 命令,用于心跳检测,作为节点存活的判断依据。
2.判定主服务器是否下线
每1秒每个 sentinel 对其他 sentinel 和 redis 执行 ping 命令,用于心跳检测,作为节点存活的判断依据.
判定主观下线
如果实例在收到 PING命令的 down-after-milliseconds 毫秒内(根据配置),未有有效回复。则该实例将会被发起 PING命令的 Sentinel 认定为主观下线。
判定客观下线
当一台主服务器被某个 Sentinel 服务器判定为客观下线时,为了确保该主服务器是真的下线, Sentinel 会向 Sentinel 集群中的其他的服务器确认,如果判定主服务器下线的 Sentinel 服务器达到一定数量时(一般是 N/2+1),那么该主服务器将会被判定为客观下线,需要进行故障转移。
即使不管我们在sentinel monitor中设置的数是多少,就算是满足了该值,只要达不到过半,就不会发生故障迁移
优点:
- 哨兵集群模式是基于主从模式的,所有主从的优点,哨兵模式同样具有。
- 主从可以切换,故障可以转移,系统可用性更好。
- 哨兵模式是主从模式的升级,系统更健壮,可用性更高。
缺点:
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
3、Redis Cluster
Redis哨兵模式实现了高可用,读写分离,但是其主节点仍然只有一个,即写入操作都是在主节点中,这也成为了性能的瓶颈。
因此 Redis 在3.0后加入了Cluster 模式,它采用去无心节点方式实现,集群将会通过分片方式保存数据库中的键值对。Redis 的每个节点都可以分为主节点与对应从节点。主节点负责处理槽,从节点负责复制某个主节点,并在主节点下线时,代替下线的主节点。
redis cluster 有固定的 16384(2^14)个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。
Redis Cluster采用的是类一致性哈希算法,一致性哈希则是对2^32取模,也就是值的范围在[0, 2^32 -1]。一致性哈希将其范围抽象成了一个圆环,redis cluster则是2^14。
redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot,就像下表这样。
节点 | 处理槽位 |
---|---|
A | 0 - 5000 |
B | 5001 - 10000 |
C | 10001 - 16383 |
hash slot 让 node 的增加和移除很简单:
-
增加一个 master,就将其他 master 的 hash slot 移动部分过去,
-
减少一个 master,就将它的 hash slot 移动到其他 master 上去。
4、Codis 集群
可以看到Codis-server、Codis-proxy、Codis-dashboard和zk组成,客户端要读写数据时,和Codis-proxy建立连接,然后Codis-proxy通过CRC-32计算计算出和Coids-server对应关系,然后把请求发送对应的coids-server,然后再通过codis-proxy返回客户端数据。
codis-proxy 是根据 crc32 算法算出 key 的名称对应的 slot,slot 分组信息是存储到 zookeeper 里面,zookeper 里面可以找到对应的 slot 分组。而Redis Cluster的映射关系是通过redis实例相互传递的,每个实例保存一份,如果事例过多 会消耗集群的资源的。
Codis-server节点添加 数据迁移步骤:
在源 server 上,Codis 从要迁移的 Slot 中随机选择一个数据,发送给目的 server
目的 server 确认收到数据后,会给源 server 返回确认消息。这时,源 server 会在本地将刚才迁移的数据删除
第一步和第二步就是单个数据的迁移过程。Codis 会不断重复这个迁移过程,直到要迁移的 Slot 中的数据全部迁移完成
参考: