启动一个或以上的服务器作为redis的备份
-
提高redis读写并发能力
主服务器负责写入命令,从服务器负责读取命令,从服务器分担了部分请求,整体而言提高了并发能力
对于相同数据,只能存在一个主服务器负责写入;但可以将数据按一定规则分配给多个主服务器负责,提升写入能力
-
利用主从切换实现容灾
当主服务器挂了时,可以将从服务器升级为主服务器,代替其执行写入命令,即主从切换
- 主从服务器之间保持tcp长连接,主服务器周期性发送心跳包确保从服务器有效
- 从服务器向主服务器发送sync命令
- 主服务器收到命令,执行bgsave生成rdb文件,期间接收到的新命令存储到缓冲区
- 主服务器将rdb发送给从服务器
- 主服务器将缓冲区(长度固定的先进先出字符串)的数据发送给从服务器
- 主服务器接收到客户端命令时,将命令转发给从服务器
这是一个完整的同步过程,但假设从服务器故障时间很短,可以拿缓冲区的命令进行恢复,那么执行让主服务器生成rdb显然是多余,因此2.8有提出新的方案
从服务器记录一个已同步命令的偏移量,主服务器记录缓冲区首字节偏移量
- 从服务器发送psync(2.8将sync改为psync)请求
- 主服务器判断从服务器的偏移量是否大于缓冲区首字节偏移量
- 是,将缓冲区数据发送过去,这个称为部分重同步
- 否,生成发送rdb,发送缓冲区数据,这个称为完全重同步
从服务器的同步偏移量是保存在内存的,当从服务器重启的时候,偏移量丢失,需要向主服务器发送完全同步的请求
但显然假如重启的时间很短,这也是可以使用部分同步来恢复数据的
因此在4.0版本中进行优化,从服务器重启前生成rdb文件,并将同步偏移量一同保存进rdb,重启后根据这个同步偏移量判断是进行完全同步还是部分同步
-
processInputBufferAndReplicate
-
从服务器,直接执行指令
-
主服务器
-
执行指令
-
replicationFeedSlavesFromMasterStream
-
feedReplicationBacklog
将指令放入缓冲区
-
遍历从服务器、发送指令
-
-
-
主服务处理完写入指令后,向从服务器发送同步信息,但并不能保证从服务器完成,返回给客户端后,客户端立即向从服务器查询这个数据,假设数据未完成同步,可能查不到这个数据
主从复制的一个目的是为了容灾,当从服务器挂了的时候,还有其他从服务器可以用;但挂的是主服务器呢,其他都是从服务器,不能处理写入命令,主服务器挂了怎么办?
-
实现主从切换
在主服务器挂了的时候,由哨兵挑选合适的从服务器升级为主服务器
哨兵可以通过redis-sentinel命令启动
redis-sentinel /path/to/sentinel.conf
或redis-server的哨兵模式启动
redis-server /path/to/sentinel.conf --sentinel
- 哨兵启动后与会与配置的主库建立连接
- 哨兵启动后进入定时任务sentinelTimer
- 与实例(实例主要分三种:主、从服务器,哨兵,由之前的定时任务获得实例信息)建立链接,订阅**_sentinel_:hello**渠道
- 每10秒向主、从库发送一次INFO,主、从库会返回从库列表
- 给所有实例发送ping,更新目标实例的存活状态
- 通过发布订阅模式,每两秒向**_sentinel_:hello**渠道发送哨兵本身的信息
-
容灾
只部署一个哨兵,哨兵挂了,用来容灾的主从切换也就没了,因此尽量避免单点部署
-
主从切换选举机制
哨兵负责主从切换的选举,因此需要多个哨兵进行投票,为了避免票数相同,最好哨兵数量比从服务器多且是奇数(2个从服务器最好设置3个哨兵,3-4个从服务器设置5个)
- 通过配置获得主库信息
- 定时任务的INFO阶段获得从库信息
- 通过对**_sentinel_:hello**渠道的发布订阅获得所有哨兵的信息
- 哨兵对配置文件有写入权限,记录从库、其他哨兵,以便在重启依赖配置快速建立链接
由多个主从复制所组成的分布式redis
-
提高redis的并发能力
使用主从复制可以一定程度地提高服务器的并发能力,但写入命令只能由主服务器来执行,单靠主从复制无法进一步提升,必须有一种机制来提升redis的写入能力
-
分槽(slot)
redis会把key分配到最多16384个slot中,使用集群需要将这些slot分配给多个主从管理
-
节点重定向
当节点接受到key的请求,会判断key的slot是否属于本节点,是则执行,否则根据缓存找到槽对应的节点的IP、端口告知客户端做重定向
-
客户端缓存重定向
- 客户端可以向集群请求slot与节点的映射并缓存,直接根据key找到slot再找到节点,避免重定向
- 当集群因节点故障或扩容导致重新分片后,客户端不得不做重定向之后,也会缓存映射关系,下一次不需要再重定向
-
每个主节点都需要从节点
在主节点挂了的时候利用主从切换容灾,避免该节点的数据无法访问
-
节点处理命令
- 写命令 - 从节点收到写命令会告知客户端重定向到主节点(客户端当然也会做缓存)
- 读命令 - 客户端只有执行readonly后,从节点才会处理客户端的读命令,否则还是重定向到主节点
-
hashTags
当一个命令处理的多个key分布在不同节点的时候,命令会报错。hashtag为业务提供了控制key保存节点的能力,hash的时候只对key里面"{"与"}"之间的部分进行hash,例如: key1{user_id}, key2{user_id}两个key会被hash同一个节点上,从而业务需要处理的的多个key分配到不同节点上
-
容灾
节点故障的时候有另一个节点代替其处理命令
-
哨兵模式
-
链接
哨兵依赖配置连接主服务器、并依赖主服务器的INFO与从服务器建立链接
-
心跳包
通过主、从服务器的ping命令判断服务器是否有效
-
主库故障
-
哨兵选举
当主服务器挂了时,所有哨兵会挑选一个哨兵来负责主从切换
-
从库挑选
主哨兵按照优先级、已同步命令偏移量,挑选用于代替主库的从库
-
主从切换
-
-
集群模式
-
链接
-
心跳包
-
主库故障
-
从库挑选
定时任务从故障节点的所有从库中,选择复制偏移量最大的作为替换目标
-
通知集群中其他主库
-
主从切换
-
集群中每个主节点至少需要一个从节点作为容灾,但依旧有可能主从节点一起故障导致集群失效
因此主节点下的从节点越多容灾能力越强,但会导致启动大量实例(例如100个主节点,每个节点部署3个从节点便需要400个实例)
副本漂移是一种从节点共享机制,每个主节点还是只部署1个从节点,但固定主节点A额外部署2个从节点,当某个主节点B发生故障进行主从切换后,从节点B1变为主节点,这时判定B1为单点,主节点A下漂移一个从节点A1作为B1的从节点
- 使用尽量少的节点进一步提高集群的容灾能力
- 定时任务
- 检查集群中的单点主节点
- 是否存在有多个从节点的主节点
- 副本漂移
- 查找从节点最多的主节点A
- 在A中按从节点名称递增排序挑选漂移的从节点A1
- 清除A1的数据
- 建立B1、A1的主从关系
- 数据同步
- 通过定时心跳包将新的主从关系告知集群下的所有节点
当集群中的主节点数量发生变化时,需要手动将slot重新分配到合适的节点上,该过程称为分片迁移
例:
变化前实例 | 变化前slot范围 | 变化后实例 | 变化后slot范围 |
---|---|---|---|
A | [0, 5000) | A | [0, 4000) |
B | [5000, 10000) | B | [4000, 8000) |
C | [10000, 16384] | C | [8000, 12000) |
D | [12000, 16384] |
-
手动执行迁移指令
CLUSTER SETSLOT slot NODE node # 将slot指定为由node节点提供服务 CLUSTER SETSLOT slot MIGRATING node # 将slot从本节点迁移到node节点 CLUSTER SETSLOT slot IMPORTING node # 将slot从node节点迁移到本节点
将slot的所有key迁移到目标节点上
-
访问迁移中的key(假设从A迁移到B)
- 向A请求key
- key存在:直接返回
- 不存在:客户端向B发送asking后请求key
- 向B请求key,没有先执行asking会报错
- 向A请求key