10.分片集群-Sharding
约 1750 字大约 6 分钟...
分片集群:
分片集群作用:
- 单机内存存在上限,通过横向扩展来突破存储的上限
主从集群作用:
- 提升可用性,避免单点故障
分片集群方案:
- 官方的
redis cluster
- 更早更稳定的
codis
redis cluster:
特性:
- 采用无中心化的模式(无proxy,客户端与服务端直连)
- cluster 使用
哈希槽(Hash Slot)
处理数据和实例之间的映射关系 - 默认有16384个槽,需要把所有的槽分配给实例,否则 Redis 集群无法正常工作。(一个实例会对应多个槽位)
- 每个redis实例都有所有哈希槽的映射关系
- 如果集群中Redis实例的内存大小不一样,可以使用cluster addslots进行手动分配哈希槽
hash slot
简化了节点扩缩容的难度,便于集群的维护和管理,但是增加了客户端映射表的维护成本
,客户端需要支持重定向机制带来的新的协议。
查找过程(主要在客户端):
客户端
通过哈希函数CRC16,给key
计算哈希值- 将结果与
16384
取模 - 查找本地哈希槽表,找出对应槽的redis实例信息
- 客户端到对应实例查询key的value
哈希槽获取和更新
:
广播
(去中心化的gossip协议):- Redis每个实例通过
广播
,把自己的哈希槽信息发给和它相连接
的其它实例,完成哈希槽分配信息的扩散
- Redis每个实例通过
客户端通过key获取对应实例:
- 客户端连接实例,实例把本地哈希槽信息发给客户端
- 当客户端请求键值对时,先计算键所对应的哈希槽,找到对应的实例
- 客户端连接对应实例,获取key对应的value
集群扩容、数据迁移:
- 扩容的难点:
- 扩容时按照
hash slot
的粒度进行数据迁移 - 扩容通过修改哈希槽信息,将部分哈希槽重新分配给新实例,这样原来负责这些槽的redis实例需要把这些数据迁移给新实例
- 扩容时,数据可能在旧实例上,需要客户端支持
重定向协议
- 扩容时按照
重定向机制
:- 含义:
- 客户端给一个实例发送数据读写操作时(假设本地哈希槽信息还没更新,访问了旧槽),这个实例上并没有相应的数据,客户端要再给一个新实例发送操作命令
- 产生的原因:
- 因为扩容导致哈希槽映射发生变化,但客户端本地的哈希槽信息还没更新,此时Cluster 提供
重定向机制
,让客户端可以到新实例查询数据
- 因为扩容导致哈希槽映射发生变化,但客户端本地的哈希槽信息还没更新,此时Cluster 提供
- 客户端查询哈希槽:
哈希槽
已迁移完成
:- key已经迁移到新实例:
- 旧实例返回
MOVED
- 客户端
更新本地哈希槽信息
,并向返回的新实例发起GET查询
- 旧实例返回
GET hello:key (error) MOVED 13320 172.16.19.5:6379
- key已经迁移到新实例:
哈希槽
迁移中
:- key已经迁移到新实例:
- 旧实例返回
ASK
- 客户端
不更新哈希槽信息
,先向返回的新实例发送ASKING
,新实例响应后,再发GET查询
- 旧实例返回
GET hello:key (error) ASK 13320 172.16.19.5:6379
- key还在旧实例:
- 旧实例直接返回 Value
- key已经迁移到新实例:
已完成:
迁移中:
- 含义:
codis:
特性:
- 采用中心化的模式(proxy,客户端proxy连接)
- 使用
1024
个逻辑槽,给redis实例分配若干逻辑槽,映射key和逻辑槽的关系

查找过程(主要在服务端):
- 客户端直接和codis proxy 建立连接,使用
RESP
Redis Serialization Protocol协议交互(一种序列化协议) codis-proxy
接收到客户端的请求codis-proxy
通过哈希函数CRC32,计算key
哈希值- 将结果与
1024
取模 - 查找槽和实例路由表,找出对应
codis server
- 将客户端请求转发给相应的
codis server
进行处理,并返回给客户端处理结果
逻辑槽信息获取和更新
:
- 在codis dashboard 上分配好路由表
- dashboard 会把路由表push给codis proxy,同时把路由表保存在 Zookeeper 中
- codis-proxy 会把路由表缓存在本地,当它接收到客户端请求后查找本地缓存。
- 当proxy宕机重启,则从zookeeper重新获取配置
集群扩容、数据迁移:
扩容特点:
- Codis集群扩容时按照Slot的粒度进行数据迁移
- codis 扩容时,客户端不需要关心当前各实例状态
扩容过程:
- 在
源server上
,Codis从要迁移的Slot中随机选择一个数据,发送给目的server
目的server
正常响应,给源server
返回确认消息源server
将本地的key删除- 不断重复上述步骤,直到slot数据全部迁移
- 在
同步迁移/异步迁移:
同步
:源server
发送迁移数据给目的server
时,暂停处理用户请求,等待目的server
确认消息- 优点:
- 逻辑简单,不会出现数据不一致
- 缺点:
- 阻塞,可能会影响正常用户访问
- bigkey迁移会导致阻塞时间过长
异步
:源server
把数据发送给目的server
时,继续处理用户请求。key被发送之后,会被设置为只读以确保数据一致性。- 优点:
- 非阻塞,不影响正常用户访问
- 异步迁移 Slot时,允许每次迁移多个key
- 对于bigkey,异步迁移采用了拆分指令的方式进行迁移
- 缺点:
- 相比同步稍微复杂些
- 部分阻塞,迁移中的数据会被设置为只读,无法修改
异步下的bigkey迁移:
- 如果key是
bigkey
,codis会拆分成多次发送并设置过期时间
,来保证数据一致性和原子性 - 如果bigkey迁移发生中断,则
新server
会在过期后删除bigkey - 如果整个bigkey迁移完成,则
新server
删除bigkey的过期时间
- 如果key是
codis可靠性集群设计:

codis server
使用主从模式进行读写分离,从库挂了不影响服务- 使用哨兵集群,实现主从自动切换,故障转移
- 使用
zookeeper
集群存储 slot-> redis实例路由表,确保proxy列表获取的可用性 codis proxy
设计为无状态服务,可以随时扩容
两者比较:

补充:
- 都使用了逻辑上的
slot
将实例切成更小的槽- 提升扩容速度
- 实现数据分片和负载均衡
- 易于水平扩展