一站式学习Redis, 从入门到高可用分布式实践-10-深入redis cluster
本章针对redis cluster的集群伸缩、请求路由、故障转移等方面进行分析说明
-
集群伸缩原理
集群伸缩 = 槽和数据在节点之间的移动 -
扩容集群
- 准备新节点
集群模式、配置和其它节点统一、启动后是孤儿节点
redis-server redis-8006.conf
redis-server redis-8007.conf
- 加入集群
cluster meet ip 8006
cluster meet ip 8007
加入集群后观察集群配置
cluster nodes
加入集群的作用: 为他迁移槽和数据,实现扩容;作为从节点负责故障转移
加入集群的第2中方法(推荐),redis-cli命令:
redis-cli add-node new_host:new_port existing_host:existing_port --slave --master-id
redis-cli add-node ip:8006 ip:8007
建议使用redis-cli能够避免新节点已经加入了其它集群,造成故障
- 迁移槽和数据
- 槽迁移计划
- 迁移数据
迁移数据完整流程图
迁移数据伪代码
- 集群扩容演示
启动两台实例
redis-server redis-8006.conf
redis-server redis-8007.conf
加入到cluster中
redis-cli -p 8000 cluster meet ip 8006
redis-cli -p 8000 cluster meet ip 8007
主从分配
redis-cli -p 8007 cluster replicate 4bc81058b7051845c5xxx46c4f14e57fd
此时查看集群状态
redis-cli -p 8006 cluster nodes
可以看到8006 master节点还没有负责任何的槽
-
使用redis-cli迁移槽数据
redis-cli --cluster reshard ip:8000
提示:How many slots do you want to move (from 1 to 16384)? 4096
提示:What is the receiving node ID? 4bc81058b7051845cxxxxa046c4f14e57fd
提示:Source node #1: all
提示:Do you want to proceed with the proposed reshard plan (yes/no)? yes
迁移成功,查看集群状态
redis-cli -p 8006 cluster nodes
整个迁移的过程中对客户端来说是无痛的,相当强大! -
收缩集群
- 迁移槽
redis-cli --cluster reshard ip 8006
- 让集群中其它节点忘记该节点
切记,要先下线从节点:(如果先下主节点,会触发故障转移,所以先下从节点,在下主节点)
redis-cli --cluster del-node ip:8000 317e195ac96xxxxxa69ff28db82c782
redis-cli --cluster del-node ip:8000 4bc81058b7xxxxx5cx046c4f14e57fd
查看集群状态
redis-cli -p 8000 cluster nodes
发现8006、8007已经不在集群当中了,至此,缩容完成
- moved重定向
- 获取键所在的槽命令
redis-cli -p 8000 cluster keyslot name
cluster keyslot name
- ASK重定向
- moved和ask的区别
两者都是客户端重定向、moved槽已经确定迁移、ask槽还在迁移中
- smart客户端实现原理
- smart客户端的目标:追求性能
从集群中选取一个可运行节点,使用cluster slots初始化槽和节点映射
将cluster slots的结果映射到本地,为每个节点创建redisPool
准备执行命令
- redis-py-cluster使用
安装:pip install redis-py-cluster
基本使用
from rediscluster import RedisCluster
# 需要至少一个节点进行集群发现,建议使用多个节点
startup_nodes = [
{"host": "ip", "port": "8000"},
{"host": "ip", "port": "8001"},
{"host": "ip", "port": "8002"},
{"host": "ip", "port": "8003"},
{"host": "ip", "port": "8004"},
{"host": "ip", "port": "8005"},
]
# 链接redis集群
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
# 设置获取值
rc.set("yuwen", 80)
ret = rc.get("yuwen")
print(ret)
使用技巧:
单例:内置了所有节点连接池
无序手动借还连接池
- 批量操作优化
- 串行mset、mget(性能差一些,需要n次的网络时间)
- 串行IO
- 并行IO
- hash_tag
四种方案优缺点:
- 故障发现
- redis sentinel一样也分为主观下线和客观下线
- 通过ping/pong消息实现故障发现,不依赖于sentinel
主观下线的基本流程
客观下线:
当半数以上持有槽的主节点都标记某节点主观下线
客观下线的两个操作:通知集群内的所有节点标记故障节点为客观下线,通知故障节点的从节点触发故障转移流程
- 故障恢复
-
资格检查
-
准备选举时间
-
选举投票
-
替换主节点
- 故障模拟
- 执行 kill -9 节点模拟宕机
- 观察客户端故障恢复时间
- 观察各节点日志
redis cluster开发运维常见为题
-
集群完整性
-
带宽消耗
节点带宽的优化:
-
pubsub(广播)
集群中发布订阅会存在问题,当集群中的一个节点进行发布消息的时候,所有与其它节点都会订阅到该消息,加重了带宽压力
限订阅
redis-cli -p 8001 subscribe cluster_p
在发布
redis-cli -h ip -p 8005 publish cluster_p "afdslfdas"
-
集群倾斜目录
-
数据倾斜
节点和槽分配不均
查看每个主节点分配的槽的数量:redis-cli -p 8000 cluster nodes | grep master
不同槽对应键值数量差异较大
CRC16正常情况下比较均匀,可能存在hash_tag
包含bigkey
例如大字符串,几百万的hash或者set等,查找大字符串:redis-cli -p 8000 --bigkeys
优化数据结构
内存相关配置不一致 -
请求倾斜
- 热点key: 重要的key或者bigkey
- 优化:避免bigkey,热键不要用hash_tag,当一致性不高时,可以用本地缓存+MQ
如何使用hash_tag
set name{sankuan} zhangsan
set name{sankuan} lisi
所有指定为{sankuan}这个hash_tag指定的槽里
- 集群读写分离
- 只读连接:集群模式下的从节点不接受任何读写请求
重定向到负责槽的主节点
readonly命令可以读:连接级别命令
也就是说在集群模式下的从节点中,不会执行读取命令,读取命令也会直接跳转到对应的主节点去执行读的操作
如果想在从节点执行读的命令,可以执行readonly - 集群读写分离
尽量不要在集群模式下使用读写分离,否则成本太高、问题也会比较多
- 数据迁移
- 离线/在线迁移
- 集群VS单机
- 集群限制
key批量操作支持有限:例如mget、mset必须在一个slot里
key事务和Lua支持有限:操作的key必须在一个节点
key是数据分区的最小粒度:不支持bigkey分区
不支持多个数据库:在集群模式下只有一个db 0
复制只支持一层,不支持树形复制结构
-
思考分布式redis不一定好?
-
本章总结