Skip to content

Latest commit

 

History

History
288 lines (152 loc) · 9.28 KB

redis的主从复制、哨兵、集群.md

File metadata and controls

288 lines (152 loc) · 9.28 KB

主从复制

启动一个或以上的服务器作为redis的备份

为什么需要主从复制

  • 提高redis读写并发能力

    主服务器负责写入命令,从服务器负责读取命令,从服务器分担了部分请求,整体而言提高了并发能力

    对于相同数据,只能存在一个主服务器负责写入;但可以将数据按一定规则分配给多个主服务器负责,提升写入能力

  • 利用主从切换实现容灾

    当主服务器挂了时,可以将从服务器升级为主服务器,代替其执行写入命令,即主从切换

主从复制的实现

  1. 主从服务器之间保持tcp长连接,主服务器周期性发送心跳包确保从服务器有效
  2. 从服务器向主服务器发送sync命令
  3. 主服务器收到命令,执行bgsave生成rdb文件,期间接收到的新命令存储到缓冲区
  4. 主服务器将rdb发送给从服务器
  5. 主服务器将缓冲区(长度固定的先进先出字符串)的数据发送给从服务器
  6. 主服务器接收到客户端命令时,将命令转发给从服务器

这是一个完整的同步过程,但假设从服务器故障时间很短,可以拿缓冲区的命令进行恢复,那么执行让主服务器生成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会报错