Redis 主从,哨兵,集群搭建
记录一下自己对 Redis 主从、哨兵、集群三种模式的搭建流程。Redis 的所有部署模式都不复杂,主要是配置文件的书写,以及各个模式的优缺点分析。
官网:Redis
部署环境
操作系统:CentOS7
Redis 版本:Redis7.0.5
主从模式
优点: 配置简单、快速,读写分离,完全分担了主节点的读压力,并且从节点还可“套娃”继续配置多层从节点。
缺点: 非高可用,主节点宕机则失去写能力;数据冗余量大,每个从节点 100% 复制主节点数据。属于非高可用架构,生产环境一般不单独使用。
配置流程
1)配置从节点 redis.conf 文件。

主从节点密码最好保持一致。
2)配置主节点 redis.conf 文件,正常设置验证密码和远程连接信息即可。

配置效果:
哨兵模式

优点: 哨兵模式解决了主从集群中主节点宕机导致不可写入的问题。当主节点宕机时,sentinel 会监控到主节点已经不可用,从剩余可用从节点中选举出新的主节点,从而解决不可写问题。之前的主节点重新启动之后,将会以从节点身份加入集群。
缺点: 依然存在大量数据冗余,并且哨兵节点不参与数据存储,计算资源存在浪费,存储能力得不到提升,写能力得不到提升,存在中心化。
部署注意事项
- 哨兵节点本身不存储数据。
- 哨兵节点至少 3 个,并且必须为奇数个(基于 Raft 算法进行选举)。
- 哨兵节点必须独立部署且只跑哨兵进程,不要跑其他应用程序,避免哨兵进入 TILT 模式;其他应用存在硬件资源抢占问题,会导致哨兵监控可靠性下降。
哨兵模式基于主从模式。 1 主 2 从加 3 个哨兵已经是 6 个计算实例资源。在生产环境中哨兵必须单独部署,不能部署到主节点或从节点上,以保证整个集群的稳定性。
此模式可用于生产环境,高可用架构,保留了 Redis 所有的功能。
Sentinel 节点配置流程
前提:主从模式已经配置好。
1)配置 sentinel.conf 文件,设置监听的 master 地址。哨兵会自动从主节点的 info 信息中读取对应从节点信息。

2)保证所有主从节点密码一致。
3)通过 redis-sentinel 命令启动哨兵。
4)spring-boot 配置哨兵连接。

如果哨兵节点数过多,可分行配置:

哨兵对节点下线的判断分主观下线与客观下线,只有当足够数量的哨兵都认为主节点已经不可用时才会选举投票。在选举期间,整个集群无法对外提供服务。
主观下线与客观下线
SDOWN(subjectively down),直译为“主观”失效,即当前 sentinel 实例认为某个 redis 服务为“不可用”状态。
ODOWN(objectively down),直译为“客观”失效,即多个 sentinel 实例都认为 master 处于 SDOWN 状态,此时 master 将处于 ODOWN。ODOWN 可以简单理解为 master 已经被集群确定为“不可用”,将会开启故障转移机制。

集群模式
官方文档:使用 Redis 集群|进行扩展

常见的集群方案有三种:
1)Twemproxy(Twitter 开源的 Redis 代理)
- 没有友好的监控管理后台界面,不利于运维监控。
- 最大的痛点在于无法平滑地扩容/缩容。当因业务需要增加 Redis 实例时,运维工作量非常大。
2)Codis(豌豆荚自主研发)
- 有 UI 管理界面。
- 可以很好地动态添加节点。
3)官方搭建方案 Redis Cluster
优点: 集群模式相对于哨兵模式来说,去除了中心化思想,自动实现了负载均衡,并且数据存储的上限随着节点数的增加而增加。节点之间使用轻量级协议,减少带宽占用,支持动态扩展节点。集群采用哈希槽(hash slot)来处理键与实例之间的映射关系:集群中总共有 16384 个哈希槽,默认将 16384 个哈希槽分配给所有节点,每个实例节点分配一段哈希槽,类似数据分区;每个键按照 CRC16 算法得到哈希值再对 16384 取模,即 CRC16(key) mod 16384,通过最终结果得到 key 所在的哈希槽位置。
概括起来有三点:写能力得到提升,存储能力得到提升,去中心化。
缺点: 存在数据存储倾斜与数据访问倾斜问题。
数据倾斜问题
数据在切片集群的多个实例上分布不均衡,大量数据集中到了一个或几个实例上,存储压力得不到均匀分摊,且热点数据访问总会落到固定实例上,导致实例压力增大,最终面临宕机风险。数据倾斜产生的主要原因有三个。
1)保存了 bigkey。
bigkey 的 value 值很大(String 类型),或者是 bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加,内存资源消耗也相应增加。而且,bigkey 的操作一般都会造成实例 IO 线程阻塞,如果 bigkey 的访问量比较大,就会影响到这个实例上的其它请求被处理的速度。
为了避免 bigkey 造成的数据倾斜,一个根本的应对方法是,在业务层生成数据时,尽量避免把过多的数据保存在同一个键值对中。如果 bigkey 正好是集合类型,还有一个方法,就是把 bigkey 拆分成很多个小的集合类型数据,分散保存在不同的实例上。
2)Slot 分配不均衡。
如果集群运维人员没有均衡地分配 Slot,就会有大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个实例上分布,这就会导致大量数据被集中到一个实例上,造成数据倾斜。
可以通过运维规范,在分配之前就避免把过多的 Slot 分配到同一个实例。如果是已经分配好 Slot 的集群,可以先查看 Slot 和实例的具体分配关系,判断是否有过多的 Slot 集中到了同一个实例;如果有的话,就将部分 Slot 迁移到其它实例,从而避免数据倾斜。
不同集群上查看 Slot 分配情况的方式不同:如果是 Redis Cluster,就用 CLUSTER SLOTS 命令;如果是 Codis,就可以在 codis dashboard UI 控制板上查看。
3)使用了 Hash Tag。
Hash Tag 是指加在键值对 key 中的一对花括号 {}。这对括号会把 key 的一部分括起来,客户端在计算 key 的 CRC16 值时,只对 Hash Tag 花括号中的 key 内容进行计算。
假设 key 是 user:profile:3231,我们把其中的 3231 作为 Hash Tag,此时 key 就变成了 user:profile:{3231}。当客户端计算这个 key 的 CRC16 值时,就只会计算 3231 的 CRC16 值;否则,客户端会计算整个 “user:profile:3231” 的 CRC16 值。
使用 Hash Tag 的好处是,如果不同 key 的 Hash Tag 内容都是一样的,那么这些 key 对应的数据会被映射到同一个 Slot 中,同时会被分配到同一个实例上。

Hash Tag 一般用在什么场景呢?其实,它主要是用在 Redis Cluster 和 Codis 中,支持事务操作和范围查询。因为 Redis Cluster 和 Codis 本身并不支持跨实例的事务操作和范围查询,当业务应用有这些需求时,就只能先把这些数据读取到业务层进行事务处理,或者是逐个查询每个实例,得到范围查询的结果。
可以使用 Hash Tag 把要执行事务操作或是范围查询的数据映射到同一个实例上,这样就能很轻松地实现事务或范围查询了。
但是,使用 Hash Tag 的潜在问题,就是大量的数据可能被集中到一个实例上,导致数据倾斜,集群中的负载不均衡。这就需要在范围查询、事务执行的需求和数据倾斜带来的访问压力之间进行取舍了。
我的建议是,如果使用 Hash Tag 进行切片的数据会带来较大的访问压力,就优先考虑避免数据倾斜,最好不要使用 Hash Tag 进行数据切片。因为事务和范围查询都还可以放在客户端来执行,而数据倾斜会导致实例不稳定,造成服务不可用。
数据访问倾斜的成因和应对方法
发生数据访问倾斜的根本原因,就是实例上存在热点数据(比如新闻应用中的热点新闻内容、电商促销活动中的热门商品信息,等等)。一旦热点数据被存在了某个实例中,这个实例的请求访问量就会远高于其它实例,面临巨大的访问压力。
热点数据通常是一个或几个数据,所以直接重新分配 Slot 并不能解决热点数据的问题。通常来说,热点数据以服务读操作为主,在这种情况下,可以采用热点数据多副本的方法来应对。
具体做法是,把热点数据复制多份,在每一个数据副本的 key 中增加一个随机前缀,让它和其它副本数据不会被映射到同一个 Slot 中。这样一来,热点数据既有多个副本可以同时服务请求,同时这些副本数据的 key 又不一样,会被映射到不同的 Slot 中。在给这些 Slot 分配实例时,也要注意把它们分配到不同的实例上,热点数据的访问压力就被分散到不同的实例上了。
注意:热点数据多副本方法只能针对只读的热点数据。如果热点数据是有读有写的话,就不适合采用多副本方法了,因为要保证多副本间的数据一致性,会带来额外的开销。对于有读有写的热点数据,就要给实例本身增加资源了,例如使用配置更高的机器,来应对大量的访问压力。
集群使用注意事项
Redis Cluster 节点之间会定期交换 Gossip 消息,以及做一些心跳检测。官方建议 Redis Cluster 节点数量不要超过 1000 个,当集群中节点数量过多时,会产生不容忽视的带宽消耗。
- 消息发送频率:节点发现与其他节点最后通信时间超过 cluster-node-timeout/2 时,会直接发送 PING 消息。
- 消息数据量:slots 槽数组(2kb 空间)和整个集群 1/10 的状态数据(10 个节点状态数据约为 1kb)。
- 节点部署的机器规模:集群分布的机器越多且每台机器划分的节点数越均匀,则集群内整体的可用带宽越高。
Redis Cluster 集群的限制:
key批量操作支持有限:例如mget,mset必须在一个slot
key事务和Lua支持有限:操作的key必须在一个节点
key是数据分区的最小粒度:不支持bigkey分区
不支持多个数据库:集群模式下只有一个db0
复制只支持一层:不支持树形复制结构
Redis Cluster满足容量和性能的扩展性,很多业务'不需要'
大多数时客户端性能会'降低'
命令无法跨节点使用:mget,keys,scan,flush,sinter等
Lua和事务无法跨节点使用
客户端维护更复杂:SDK和应用本身消耗(例如更多的连接池)
很多场景 Redis Sentinel 已经够用了。
Codis 与 Redis Cluster 集群对比:

Redis Cluster 总结
1.Redis Cluster数据分区规则采用虚拟槽方式(16384个槽),每个节点负责一部分槽和相关数据,实现数据和请求的负载均衡
2.搭建Redis Cluster划分四个步骤:准备节点,meet操作,分配槽,复制数据。
3.Redis官方推荐使用redis-trib.rb工具快速搭建Redis Cluster(redis5.0版本之前使用redis-trib,之后版本使用redis-cli命令)
4.集群伸缩通过在节点之间移动槽和相关数据实现,扩容时根据槽迁移计划把槽从源节点迁移到新节点,收缩时如果下线的节点有负责的槽需要迁移到其他节点,再通过cluster forget命令让集群内所有节点忘记被下线节点
5.使用smart客户端操作集群达到通信效率最大化,客户端内部负责计算维护键,槽以及节点的映射,用于快速定位到目标节点
6.集群自动故障转移过程分为故障发现和节点恢复。节点下线分为主观下线和客观下线,当超过半数节点认为故障节点为主观下线时,标记这个节点为客观下线状态。从节点负责对客观下线的主节点触发故障恢复流程,保证集群的可用性
7.开发运维常见问题包括:超大规模集群带宽消耗,pub/sub广播问题,集群倾斜问题,单机和集群对比等
使用 redis-cli 搭建 Redis Cluster(搭建版本 7.0.4)
至少 6 节点,(1 主 + 1 从) × 3 = 6 节点。redis 5.0 版本之前使用 redis-trib 工具,之后版本使用 redis-cli 命令。
1)设置 redis.conf 文件。所有节点的密码相同,然后开启集群模式(更多配置细节查看官网文档):
# 开启集群模式
cluster-enabled yes

2)执行创建集群命令:
redis-cli --cluster create --cluster-replicas 1 192.168.121.145:6379 192.168.121.143:6379 192.168.121.142:6379 192.168.121.141:6379 192.168.121.144:6379 192.168.121.140:6379 -a 【密码】
其中 redis-cli --cluster 或者 ./redis-trib.rb 代表集群操作命令;create 代表创建集群;--replicas 1 或者 --cluster-replicas 1 指定集群中每个 master 的副本个数为 1,此时节点总数 ÷ (replicas + 1) 得到的就是 master 的数量。因此节点列表中的前 n 个就是 master,其它节点都是 slave 节点,随机分配到不同 master。
输入命令后会提示确认集群 slot 分配,必须输入完整的 yes 而不是 y,不然会跳过 slot 自动分配,集群不可用。如果未分配,可以使用 redis-cli --cluster fix 127.0.0.1:6379 -a 【密码】(任意节点)命令进行 slot 修复。
通过日志输出看到,集群已经自动创建完毕。

3)查看当前集群节点信息:
redis-cli -h 【任意节点ip】 -p 【端口】 -a 【密码】 cluster nodes
下图为节点信息(当前未分配 slot):

执行 slot 分配(可以看到每个 master 负责的 slot 槽):

尝试插入值:

通过 UI 工具查看值是否存入,连接集群任意节点即可:

可见 Redis 集群已经成功搭建。
通过 spring-boot 连接集群:

小结
主从模式配置最简单,但主节点宕机后集群失去写能力,更多是作为其他模式的基础。哨兵模式在主从之上补齐了自动故障转移,很多业务场景用它已经够了。集群模式带来写能力和存储容量的水平扩展,但也引入了数据倾斜、跨节点命令受限等新问题。选型时先评估数据量和写压力,没必要一步到位直接上集群。
评论 / COMMENTS