一、reids 集群
1、扩容集群
准备新节点 =》加入集群 =》迁移槽和数据
新节点:
- 集群模式
- 配置和其他节点统一
- 孤立节点
加入集群作用
- 为它迁移槽和数据实现扩容
- 作为从节点负责故障转移
建议使用 redis-trib.rb 能够避免新节点加入其他集群,造成故障
迁移槽和数据 - 槽迁移计划
- 迁移数据
- 对目标节点发送
- 添加从节点
步骤
- 目标节点准备导入槽
- 源节点准备导出槽
- 获取 slot 下 count 个键
- 批量迁移相关键的数据
- 循环迁移键
redis-cli -p 7000 cluster meet 127.0.0.1 7006
redis-trib.rb reshard 127.0.0.1 7000
2、收缩扩容
下线迁移槽
忘记节点:cluster forget downNodeId
关闭节点
redis-trib.rb reshard –from nodeId –to nodeId –slots 1366 127.0.0.1:7000
redis-trib.rb del-node 127.0.0.1:7000 nodeId
3、客户端路由
moved 重定向
- 发送键命令
- 计算槽和对应节点
- 回复 moved
- 重定向发送命令
槽命中:直接返回
槽命不中:moved 异常
ask 重定向
- 发送键命令
- 回复 ask 转向
- asking
- 发送命令
- 响应结果
moved 和 ask:两者都是客户端重定向,moved 槽已经确定迁移,ask 槽还在迁移中
smart 客户端
- 从集群中选一个可运行节点,使用 cluster slots 初始化槽和节点映射
- 将 cluster slots 的结果映射到本地,为每个节点创建 JedisPool
- 准备执行命令
批量优化的方法:
串行 mget,串行 IO,并行 IO,hash_tag
4、故障转移
故障发现:
通过 ping/pong 消息实现故障发现,不需要 sentinel
主观下线和客观下线
主观下线:某个节点认为另一个节点不可用,偏见
客观下线:当半数以上持有槽的主节点都标记某节点主观下线
故障恢复:
资格检查
- 每个节点检查与故障主节点的断线时间
- 超过 cluster-node-timeout * cluster-slave-validity-factor 取消资格
- cluster-slave-validity-factor:默认是 10
准备选举时间
选举投票
替换主节点
- 当前从节点取消复制为主节点
- 执行 clusterDelSlot 撤销故障主节点负责的槽,并执行 clusterAddSlot 把这些槽分配给自己
- 向集群广播自己的 pong 消息,表明已经替换了故障从节点
5、集群完整性
cluster-require-full-coverage 默认为 yes
- 集群中 16384 个槽全部可用:保证集群完整性
- 节点故障或者正在故障转移
大多业务无法容忍,cluster-require-full-coverage 建议设置为 no
带宽消耗
- 官方建议: 1000 个节点
- PING/PONG 消息
- 不容忽视的带宽消耗
三个方面:消息发送频率;消息数据量;节点部署的机器规模
避免大集群:避免多业务使用一个集群,大业务可以多集群
cluster-node-timeout:带宽和故障转移速度的均衡
尽量均匀分配到多机器上:保证高可用和带宽
数据倾斜
- 节点和槽分配不均
redis-trib.rb info ip:port 查看节点,槽,键值分布
redis-trib.rb rebalance ip:port 重新分配槽,节点,键值
- 不同槽对应键值数量差异较大
- 包含 bigkey
- 内存相关配置不一致
请求倾斜
热点 key:重要的 key 或者 bigkey
优化:
避免 bigkey
热键不要使用 hash_tag
当一致性不高时,可用使用本地缓存 + MQ
集群读写分离
只读连接:集群模式的从节点不接受任何读写请求
- 重定向到负责槽的主节点
- readonly 命令可以读 连接级别的命令
读写分离:更加复杂
- 同样的问题:复制延迟 读取过期数据 从节点故障
- 修改客户端:cluster slaves nodeId
数据迁移
官方迁移工具:redis-trib.rb import
只能从单机迁移到集群
不支持在线迁移:source 需要停写
不支持断点续传
单线程迁移:影响速度
集群和单机
集群限制
key 批量操作支持有限:mget,mset 必须再一个 slot
key 事物和 lua 支持有限:操作的 key 必须在一个节点
key 时数据分区的最小粒度:不支持 bigkey 分区
不支持多个数据库:集群模式下只有一个 db 0
复制只支持一层:不支持树形复制结构
二、reids 缓存成本和收益
1、缓存的受益和成本
受益:
- 加速读写
通过缓存加速读写速度
- 降低后端负载
后端服务器通过前端缓存降低负载,业务端使用 Redis 降低后端 Mysql 负载
成本:
- 数据不一致:
缓存从和数据层有时间窗口不一致,和更新策略有关
- 代码维护成本
多了一层缓存逻辑
- 运维成本
使用场景
- 降低后端负载
对高消耗的 SQL=>join 结果集 / 分组统计结果缓存
- 加速请求响应
利英 Redis/Memcache 优化 IO 响应时间
- 大量写合并为批量写
如计数器先 Redis 累加再批量写 DB
2、缓存更新策略
- LRU/LFU/FIFO 算法剔除
- 超时剔除
- 主动更新:开发控制生命周期
建议
低一致性:最大内存和淘汰策略
高一致性 超时剔除和主动更新结合,最大内存和淘汰策略兜底
3、缓存粒度控制
- 通用性:全量属性更好
- 占用空间:部分属性更好
- 代码维护:表面上全量属性更好
4、缓存穿透问题
大量请求不命中
原因:
- 业务代码自身问题
- 恶意攻击,爬虫等等
发现:
- 业务的响应时间
- 业务本身问题
- 相关指标 总调用数 缓存层命中数 存储层命中数
解决方案:
- 缓存空对象
两个问题:
需要更多的键
缓存层和存储层数据短期不一致 - 布隆过滤器拦截
5、缓存雪崩优化
由于 cache 服务承载大量请求,当 cache 服务器异常 / 脱机,流量直接压向后端组建,造成级联故障
优化方案:
保证缓存高可用性
- 个别节点,个别机器,甚至是机房
- 依赖隔离组件为后端限流
- 提前演练:例如压力测试
6、无底洞优化
优化 IO 的几种方法
- 命令本身优化:例如慢查询 keys hgetall bigkey
- 减少网络通信次数
- 降低接入成本:例如客户端长连接 / 连接池 NIO 等
- 串行 mget 串行 IO 并行 IO hash_tag
7、热点 key 的重建优化
三个目标
- 减少重缓存的次数
- 数据尽可能一致
- 减少潜在危险
两个解决
- 互斥锁
- 永远不过期
缓存层面:没有设置过期时间
功能层面:为每个 value 添加逻辑过期时间,但发现超过逻辑过期时间后,会使用单独的线程去构建缓存
缓存收益:加速读写,降低后端存储负载
缓存成本 缓存和存储数据不一致性 代码维护成本 运维成本
推荐结合剔除、超时、主动更新三种方案共同完成
穿透问题:使用缓存空对象和布隆过滤器来解决,注意他们各自的使用场景和局限性
无底洞问题:分布式缓存中,有更多的机器不保证有更高的性能
雪崩问题:缓存层高可用,客户端降级 提前演练
热点 key 问题 互斥锁 永远不过期 能够子啊一定程度上解决热点 key 的问题