redis分布式架构cluster(一)
优势:解决单机的内存,并发,流量瓶颈
一、数据分布理论
-
redis cluster 的数据分布规则:
哈希–虚拟槽分区 -
集群功能限制
a. 批量操作例如mget、mset只支持在一个槽中的keys,只支持多key在同一节点的事务功能
b. 不支持多数据库空间,仅一个库 db 0
c. 复制结构只支持一层,不能嵌套树状复制结构 -
常见的分区理论:
顺序分区、哈希分区 -
哈希分区方法:
节点取余分区:当节点数量变化时,需要重新计算映射关系,数据要迁移(常用于数据库分表方案,表的数量已固定),可用翻倍扩容来避免全量的数据迁移,只迁50%。
一致性哈希分区:节点变化只影响在哈希环中相邻的节点,会造成部分数据无法命中的情况,较适用于缓存场景,会造成数据负载不均衡,需要增加或者减少一倍的节点。节点少的时候影响范围更大。
虚拟槽分区 :所有的键按照哈希函数映射到0~16383的槽内,每个节点负责维护一部分槽和所映射的键值数据。
二、集群搭建
流程:
-
修改配置文件,cluster相关内容
clusert-enabled yes 等 -
启动6个以上redis-server节点,指定配置文件,不然就会自动创建一个新的
redis-server conf/redis-6379.conf -
节点握手,在任意节点执行
cluster meet ip port -
将槽均匀分配到n个节点,分配槽后集群在线
cluster addslots{0…5461}
cluster addslots{5462…10922}
cluster addslots{10923…16383} -
设置主从关系,在从节点上执行
cluster replicate nodeid
ps:nodeid可以通过cluster nodes的第一列获得
几个知识点:
- redis-server集群初始化时有一个节点id,是不会改变的;而运行id,每次重启会改变;
- 集群模式的redis除了自己的配置文件外,还有一份集群配置文件,redis自动维护,不要手动修改;
三、节点通信
-
通信协议
gossip协议 -
节点定时任务
每秒随机选取5个节点找出最久没有通信的节点发送ping消息,
每秒执行10次,间隔一秒。每100ms如果发现节点最近一次接受pong消息大于cluster_node_timeout/2,立刻发送ping消息 -
消息数据量
主要是消息头(2k)和消息体(与节点数量息息相关)
几个运维建议:
- 当带宽资源紧张时,可以适当调大cluster_node_timeout
- 集群数量并不是越多越好
四、集群扩缩容
槽和数据在节点之间的迁移
1、集群扩容
- 新节点安装好redis-server,参数配置同其他几台
- 加入集群,再集群里的任意一个节点执行
cluster meet 127.0.0.1 6385
经过一段时间的通信后就加入集群了
redis-trib.rb工具,可以实现为集群添加新节点命令,也可以直接添加为从节点(正式环境建议使用,避免发生新节点已经加入别的集群导致的集群合并,数据错乱)
redis-trib.rb add-node 127.0.0.1:6386 127.0.0.1:6379 --slave --master-id
redis-trib.rb add-node 新节点ip:port 旧节点ip:port
- 迁移槽和数据(redis-trib.rb)
槽迁移过程中集群可以正常提供读写服务
redis-trib.rb reshard host:port --from --to --slots --yes --timeout --pipline
host:port 集群内任意节点地址
–from 指定源节点id,可写多个逗号分隔
–to 目标节点只有一个
–slots 需要迁移槽的总数量
–yes 是否需要用户输入yes确认后再执行reshard
–timeout 每次migrate操作的超时时间,默认为6w ms;
–pipline 控制每次批量迁移键的数量,默认为10
-
迁移后建议使用redis-trib rebalance检查节点之间槽的均衡性
redis-trib rebalance ip:port -
给该节点添加从节点
集群模式下,slaveof不可用,要使用cluster replicate {masternodeid}
2、集群缩容
-
迁移槽
把下线节点的槽均匀迁移至其他几个节点,需要执行多次redis-trib.rb reshard -
忘记节点
cluster forget {downnodeid} 执行后会有60s内禁用该节点通信,需要在60s内将所有节点都禁用。比较麻烦且容易失败。
redis-trib.rb del-node {host:ip} {downnodeid}
实现了安全下线的功能:判断是否已经没有槽,是否有从节点,从节点迁移到拥有从节点最少的主节点上。
五、请求路由
1. moved重定向
返回key所对应的槽:cluster keyslot key
2. 自动重定向(由客户端完成)
redis-cli -p6379 -c
3. 计算槽
根据键的有效部分,使用CRC16函数计算散列值,再对16383取余数。
键的有效部分的定义:当有{}时,按照{}里的内容;没有时则为完整key
4. ask重定向
当集群slots正在迁移,会出现ask重定向,不知道什么时候会结束,是个暂时的重定向
5. smart客户端
dummy(傀儡客户端):随机连上集群的一个,根据moved重定向信息再次发起请求
smart客户端:客户端维护slot–>node的映射关系,moved重定向用来维护本地的映射关系
- 初始化时先选择一个运行节点,初始化槽和节点的映射关系,使用cluster slot命令完成
- 执行键命令
- 计算slot并根据slot缓存获得目标节点,发送命令
- 如果出现连接错误,使用随机链接重新执行键命令,每次命令重试对redirections参数减1
- 捕获到moved重定向错误,使用cluster slots命令更新slots缓存
- 重复执行上述3步,直至命令执行成功或者redirections<=0,抛出异常
- 抛出JedisConnectionException异常可能的原因:
Jedis连接节点发生socket错误;
所有命令/Lua脚本读写超时抛出;
JedisPool连接池获取可用Jedis对象超时抛出;
redis集群支持故障转移,故障从发现到转移需要一定的时间,节点宕机期间所有指向这个节点的命令都会触发随机重试,每次收到moved重定向后会调用JedisClusterInfoCache类的renewslotcache方法。
获得写锁之后再执行cluster slots命令初始化缓存,由于集群所有的键命令都需要调用getslotspool方法计算槽对应的节点,内部要求读锁。读写锁互斥,从而所有的请求都会造成阻塞。即cluster slots风暴
cluster slots风暴
现象:
- 重试机制导致io通信放大问题
- 个别节点操作异常导致频繁的更新slots缓存,多次调用cluster slots命令,集群架构越庞大,问题越严重
- 频繁触发更新本地slots缓存操作,内部使用了写锁,阻塞对集群所有键命令的调用
建议升级到jedis 2.8.2以上,会减少调用cluster slots命令,减少更新slots缓存次数,且优化写锁阻塞和不必要的并发调用