Redis集群方案
- 官方
- 主从模式
- 哨兵模式
- Redis Cluster
- 社区
- Codis(豌豆荚团队开源)
- Twemproxy(Twitter团队开源)
官方
主从模式
架构图
- 简说
- 主从复制是部署多个副本节点,多个副本节点实时复制主节点的数据,当主节点宕机时,我们有完整的副本节点可以使用
- 优点
- 高可靠性,主从实时备份,有效解决单节点数据丢失问题
- 可做读写分离,从库分担读操作,缓解主库读压力
- 缺点
- 主库异常需要手动主从切换
- 主库的写能力受到单机的限制,可以考虑分片
- 主库的存储能力受到单机的限制,可以考虑Pika
哨兵模式
架构图
- 简说
- 是基于主从模式做的升级,它能够为Redis提供了高可用性。相对于主从模式在主节点宕机导致不可写的情况下,多了一个竞选机制,即从所有的从节点竞选出新的主节点。竞选机制的实现,是依赖于在系统中启动一个sentinel(哨兵)进程。哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例
- 哨兵
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机
多哨兵模式
架构图
- 简说
哨兵进程也存在单点问题,为此,我们可以部署多个哨兵,使用多个哨兵进行监控,sentinel可以通过发布与订阅来自动发现Redis集群上的其它sentinel。sentinel在发现其它sentinel进程后,会将其放入一个列表中,这个列表存储了所有已被发现的sentinel,这样就形成了多哨兵模式 - 主观下线和客观下线
- 哨兵节点发送ping命令时,当超过一定时间(down-after-millisecond)后,如果节点未回复,则哨兵认为主观下线。主观下线表示当前哨兵认为该节点已经下线,如果该节点为主数据库,哨兵会进一步判断是够需要对其进行故障切换,这时候就要发送命令(SENTINEL is-master-down-by-addr)询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量(quorum)时,哨兵就会认为是客观下线
- 当主节点客观下线时就需要进行主从切换,主从切换的步骤为
- 选出领头哨兵
- 领头哨兵所有的slave选出优先级最高的从数据库。优先级可以通过slave-priority选项设置
- 如果优先级相同,则从复制的命令偏移量越大(即复制同步数据越多,数据越新),越优先
- 如果以上条件都一样,则选择run ID较小的从数据库
- 选出一个从数据库后,哨兵发送slave no one命令升级为主数据库,并发送slaveof命令将其他从节点的主数据库设置为新的主数据库
- 优点
- 能够解决 Redis 主从模式下的高可用切换问题
- 缺点
- 部署相对 Redis 主从模式要复杂一些,原理理解更繁琐
- 哨兵选举期间,不能对外提供服务
- 资源浪费,Redis 数据节点中 slave 节点作为备份节点不提供服务
Redis Cluster
架构图
-
简说
- Redis Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接
-
结构特点
- 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽
- 节点的fail是通过集群中超过半数的节点检测失效时才生效
- 客户端与redis节点直连,不需要中间proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- Redis Cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster负责维护node<->slot<->value
- Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中
- Redis Cluster为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉
-
一致性哈希
-
扩容
- 首先启动一个 Redis 节点,记为 M4
- 使用 cluster meet 命令,让新 Redis 节点加入到集群中。新节点刚开始都是主节点状态,由于没有负责的槽,所以不能接受任何读写操作,后续我们就给他迁移槽和填充数据
- 对 M4 节点发送 cluster setslot { slot } importing { sourceNodeId} 命令,让目标节点准备导入槽的数据
- 对源节点,也就是 M1,M2,M3 节点发送 cluster setslot { slot } migrating { targetNodeId} 命令,让源节点准备迁出槽的数据
- 源节点执行 cluster getkeysinslot { slot } { count } 命令,获取 count 个属于槽 { slot } 的键,然后执行步骤六的操作进行迁移键值数据
- 在源节点上执行 migrate { targetNodeIp} " " 0 { timeout } keys { key... } 命令,把获取的键通过 pipeline 机制批量迁移到目标节点,批量迁移版本的 migrate 命令在 Redis 3.0.6 以上版本提供
- 重复执行步骤 5 和步骤 6 直到槽下所有的键值数据迁移到目标节点
- 向集群内所有主节点发送 cluster setslot { slot } node { targetNodeId } 命令,通知槽分配给目标节点。为了保证槽节点映射变更及时传播,需要遍历发送给所有主节点更新被迁移的槽执行新节点
-
收缩
- 首先需要确认下线节点是否有负责的槽,如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性。
- 当下线节点不再负责槽或者本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记改节点后可以正常关闭。
- 下线节点需要将节点自己负责的槽迁移到其他节点,原理与之前节点扩容的迁移槽过程一致。
- 迁移完槽后,还需要通知集群内所有节点忘记下线的节点,也就是说让其他节点不再与要下线的节点进行 Gossip 消息交换。
-
虚拟节点
- 为了解决雪崩现象和数据倾斜现象,提出了虚拟节点这个概念。就是将真实节点计算多个哈希形成多个虚拟节点并放置到哈希环上,定位算法不变,只是多了一步虚拟节点到真实节点映射的过程,具体做法可以在服务器ip或主机名的后面增加编号来实现。
-
优点
- 无中心架构,数据按照 slot 存储分布在多个节点,节点间数据共享
- 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除
- 高可用性:部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升
- 降低运维成本,提高系统的扩展性和可用性
-
缺点
- Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性
- 数据通过异步复制,不保证数据的强一致性
- Slave 在集群中充当“冷备”,不能缓解读压力,当然可以通过 SDK 的合理设计来提高 Slave 资源的利用率
- Key 批量操作限制,如使用 mset、mget 目前只支持具有相同 slot 值的 Key 执行批量操作。对于映射为不同 slot 值的 Key 由于 Keys 不支持跨 slot 查询,所以执行 mset、mget、sunion 等操作支持不友好
- Key 事务操作支持有限,只支持多 key 在同一节点上的事务操作,当多个 Key 分布于不同的节点上时无法使用事务功能
- Key 作为数据分区的最小粒度,不能将一个很大的键值对象如 hash、list 等映射到不同的节点
- 不支持多数据库空间,单机下的 redis 可以支持到 16 个数据库,集群模式下只能使用 1 个数据库空间,即 db0
- 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构
- Redis Cluster 不建议使用 pipeline 和 multi-keys 操作,减少 max redirect 产生的场景
社区
Codis(豌豆荚团队开源)
- 组件:
- codis-proxy:客户端连接的Redis代理服务,本身实现了Redis协议,表现很像原生的Redis (就像 Twemproxy)。一个业务可以部署多个 codis-proxy,其本身是无状态的
- codis-dashbaord:统一的控制中心,整合了数据转发规则、故障自动恢复、数据在线迁移、节点扩容缩容、自动化运维API等功能
- codis-group:基于Redis 3.2.8版本二次开发的Redis Server,增加了异步数据迁移功能,每个Group包括1个Redis Master及至少1个Redis Slave,这样做的好处是,如果当前Master有问题,则运维人员可通过Dashboard“自助式”切换到Slave,而不需要小心翼翼地修改程序配置文件。
- codis-fe:管理多个集群的UI界面
- zookeeper:Codis依赖ZooKeeper来存放数据路由表和codis-proxy节点的元信息,codis-config发起的命令会通过 ZooKeeper同步到各个存活的codis-proxy
- 它的功能非常全,除了请求转发功能之外,还实现了在线数据迁移、节点扩容缩容、故障自动恢复等功能
- 优点
- 对客户端透明,与codis交互方式和redis本身交互一样
- 支持在线数据迁移,迁移过程对客户端透明有简单的管理和监控界面
- 支持高可用,无论是redis数据存储还是代理节点
- 自动进行数据的均衡分配
- 最大支持1024个redis实例,存储容量海量高性能
- 缺点
- 采用自有的redis分支,不能与原版的redis保持同步
- 如果codis的proxy只有一个的情况下, redis的性能会下降20%左右
- 某些命令不支持,比如事务命令muti
Twemproxy(Twitter团队开源)
后续补全
Q&A
- redis cluster3主3从集群,如果有1台主挂了,会怎样?
- 为什么主节点至少要3个,因为主从自动故障转移的时候,需要主节点进行投票选举,挂了一个,还有两个可以进行投票
- 如果一对主从节点都挂了,那么这一部分槽都不可用,也不会进行自动转移到其他主节点,所以redis集群的高可用是依赖于节点的主从复制与主从间的自动故障转移,但是其他节点的槽位还是正常使用
- 出现连续挂这种情况的场景,可能出现热点key,或者出现大value值,导致新的主节点又挂了,根据监控,业务排查
- 如果是qps高,考虑限流?或者拆分槽位?或者分散key分布?
- 如果是大value值,考虑是否不存redis?或者本身业务逻辑有问题?
- 数据分片策略有哪些?
- 范围分片,哈希分片,一致性哈希算法,哈希槽
- redis cluster的分片策略?
- redis cluster用的是哈希槽,主要的原因就是一致性哈希算法对于数据分布、节点位置的控制并不是很友好
- 哈希槽使用crc16算法,其实哈希槽的本质和一致性哈希算法非常相似,不同点就是对于哈希空间的定义。一致性哈希的空间是一个圆环,节点分布是基于圆环的,无法很好的控制数据分布
- 而 redis cluster 的槽位空间是自定义分配的,类似于 windows 盘分区的概念。这种分区是可以自定义大小,自定义位置的
- 从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态
- 但一定要注意的是,对于槽位的转移和分派,redis 集群是不会自动进行的,而是需要人工配置的。所以 redis 集群的高可用是依赖于节点的主从复制与主从间的自动故障转移
-
哈希槽结构图
- 哈希槽为什么是16384个?
- 在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息
- CRC16算法最多可分配65535个槽位,这个ping消息的消息头太大了,浪费带宽,2^14是作者在CRC16中权衡出的比较“合适”的一个数值
- 16384=16k,在发送心跳包时使用位图存储node,bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 2K)
- 集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵
- 因此redis作者,不建议redis cluster节点数量超过1000个。那么对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个
- 出现哈希碰撞怎么解决?
- 哈希算法是一定会出现这个问题的,这个不在本次讨论访问
- 一致性哈希是什么?
- 为了解决哈希取余映射算法新增/减少节点的时候,需要全局重新哈希取余映射而存在
- 但是出现可能不对节点实际占据环上的区间大小不一,所以出现虚拟节点来解决问题
- 虚拟节点就是通过对每个节点构造出多个虚拟节点再进行一致性哈希,比如机器A,可以创建A-1,A-2,A-3等虚拟节点,只要在这些节点上,都放到机器A
- 大数据场景常用方案?
- 起码3主3从,默认主节点进行读写,从节点只读主节点的数据,不提供服务,可通过readonly命令,将slave设置成可读
- 在故障转移阶段,需要由主节点投票选出哪个从节点成为新的主节点;从节点选举胜出需要的票数为N/2+1;其中N为主节点数量(包括故障主节点),但故障主节点实际上不能投票。
- 因此为了能够在故障发生时顺利选出从节点,集群中至少需要3个主节点(且部署在不同的物理机上)。
- Redis Cluster单个集群主节点可以添加到不超过1000个,一台机器16g的话,拥有16t的内存,够用了,再大的话,也会导致网络拥堵,还不如创建多个集群
- 同步信息是交换维护节点元数据信息,什么槽在什么节点,并不是数据
- codis之所以停止维护,可能也是因为官方这个玩意太好用了,个人看法,毕竟从设计上来看,两种是完全不同的方案