Redis 集群,顾名思义就是使用多个 Redis 节点构成的集群,从而知足在数据量和并发数大的业务需求。git
在单个 Redis 的节点实例下,存储的数据量大和高并发的状况下,内存很容易就暴涨。同时,一个 Redis 的节点,内存也是受限的,两个缘由,一个是内存过大,在进行数据同步的时候,全量同步的时候会致使时间过长,会增长同步失败的风险;另外一个缘由就是通常的 Redis 都是部署在云服务器上的,这个也会受到CPU
的使用率的影响。github
因此,在面对着大数据量的时候,就会 Redis 集群的方案来管理,同时也是把这么多 Redis 实例的CPU
计算能力聚集到一块儿,从而完成关于大数据和高并发量的的读写操做。redis
Redis
集群的方案有哪些?优缺点分别是什么?Codis
集群的分片原理是怎么样的?Codis
集群的迁移方式和工具备哪些?Codis
为何不少命令行不支持?Codis
集群正在迁移中,怎么处理发送过来的读写请求?Redis
集群方案有哪些Redis
的集群解决方案有社区的,也有官方的,社区的解决方案有 Codis
和Twemproxy
,Codis
是由我国的豌豆荚团队开源的,Twemproxy
是Twitter
团队的开源的;官方的集群解决方案就是 Redis Cluster
,这是由 Redis
官方团队来实现的。下面的列表能够很明显地表达出三者的不一样点。算法
- | Codis | Twemproxy | Redis Cluster |
---|---|---|---|
resharding without restarting cluster | Yes | No | Yes |
pipeline | Yes | Yes | No |
hash tags for multi-key operations | Yes | Yes | Yes |
multi-key operations while resharding | Yes | No(details) | |
Redis clients supporting | Any clients | Any clients | Clients have to support cluster protocol |
Codis
集群Codis
是一个代理中间件,用的是 GO
语言开发的,以下图,Codis
在系统的位置是这样的。 c#
Codis
分为四个部分,分别是Codis Proxy (codis-proxy)
、Codis Dashboard (codis-config)
、Codis Redis (codis-server)
和ZooKeeper/Etcd
.服务器
Codis
就是起着一个中间代理的做用,可以把全部的Redis
实例当成一个来使用,在客户端操做着SDK
的时候和操做Redis
的时候是同样的,没有差异。网络
由于Codis
是一个无状态的,因此能够增长多个Codis
来提高QPS
,同时也能够起着容灾的做用。并发
Codis
分片原理在Codis
中,Codis
会把全部的key
分红1024
个槽,这1024
个槽对应着的就是Redis
的集群,这个在Codis
中是会在内存中维护着这1024
个槽与Redis
实例的映射关系。这个槽是能够配置,能够设置成 2048 或者是4096个。看你的Redis的节点数量有多少,偏多的话,能够设置槽多一些。高并发
Codis
中key
的分配算法,先是把key
进行CRC32
后,获得一个32
位的数字,而后再hash
%1024
后获得一个余数,这个值就是这个key
对应着的槽,这槽后面对应着的就是redis
的实例。(能够思考一下,为何Codis
不少命令行不支持,例如KEYS
操做)工具
CRC32:CRC自己是“冗余校验码”的意思,CRC32则表示会产生一个32bit(8位十六进制数)的校验值。因为CRC32产生校验值时源数据块的每个bit(位)都参与了计算,因此数据块中即便只有一位发生了变化,也会获得不一样的CRC32值。
Codis
中Key
的算法代码以下
//Codis中Key的算法
hash = crc32(command.key)
slot_index = hash % 1024
redis = slots[slot_index].redis
redis.do(command)
复制代码
Codis
之间的槽位同步思考一个问题:若是这个
Codis
节点只在本身的内存里面维护着槽位与实例的关系,那么它的槽位信息怎么在多个实例间同步呢?
Codis
把这个工做交给了ZooKeeper
来管理,当Codis
的Codis Dashbord
改变槽位的信息的时候,其余的Codis
节点会监听到ZooKeeper
的槽位变化,会及时同步过来。如图:
Codis
中的扩容思考一个问题:在
Codis
中增长了Redis
节点后,槽位的信息怎么变化,原来的key
怎么迁移和分配?若是在扩容的时候,这个时候有新的key
进来,Codis
的处理策略是怎么样的?
由于Codis
是一个代理中间件,因此这个当须要扩容Redis
实例的时候,能够直接增长redis节点。在槽位分配的时候,能够手动指定Codis Dashbord
来为新增的节点来分配特定的槽位。
在Codis
中实现了自定义的扫描指令SLOTSSCAN
,能够扫描指定的slot
下的全部的key
,将这些key
迁移到新的Redis
的节点中(话外语:这个是Codis
定制化的其中一个好处)。
首先,在迁移的时候,会在原来的Redis
节点和新的Redis
里都保存着迁移的槽位信息,在迁移的过程当中,若是有key
打进将要迁移或者正在迁移的旧槽位的时候,这个时候Codis
的处理机制是,先是将这个key
强制迁移到新的Redis
节点中,而后再告诉Codis
,下次若是有新的key
的打在这个槽位中的话,那么转发到新的节点。代码策略以下:
slot_index = crc32(command.key) % 1024
if slot_index in migrating_slots:
do_migrate_key(command.key) # 强制执行迁移
redis = slots[slot_index].new_redis
else:
redis = slots[slot_index].redis
redis.do(command)
复制代码
面对着上面讲的迁移策略,若是有成千上万个节点新增进来,都须要咱们手动去迁移吗?那岂不是得累死啊。固然,Codis
也是考虑到了这一点,因此提供了自动均衡策略。自动均衡策略是这样的,Codis
会在机器空闲的时候,观察Redis
中的实例对应着的slot
数,若是不平衡的话就会自动进行迁移。
Codis
的牺牲由于Codis
在Redis
的基础上的改造,因此在Codis
上是不支持事务的,同时也会有一些命令行不支持,在官方的文档上有(Codis
不支持的命令)
官方的建议是单个集合的总容量不要超过1M
,不然在迁移的时候会有卡顿感。在Codis
中,增长了proxy
来当中转层,因此在网络开销上,是会比单个的Redis
节点的性能有所降低的,因此这部分会有些的性能消耗。能够增长proxy
的数量来避免掉这块的性能损耗。
MGET
的过程思考一个问题:若是熟悉
Redis
中的MGET
、MSET
和MSETNX
命令的话,就会知道这三个命令都是原子性的命令。可是,为何Codis
支持MGET
和MSET
,却不支持MSETNX
命令呢?
缘由以下: 在Codis
中的MGET
命令的原理是这样的,先是在Redis
中的各个实例里获取到符合的key
,而后再汇总到Codis
中,若是是MSETNX
的话,由于key
可能存在在多个Redis
的实例中,若是某个实例的设值成功,而另外一个实例的设值不成功,从本质上讲这是不成功的,可是分布在多个实例中的Redis
是没有回滚机制的,因此会产生脏数据,因此MSETNX
就是不能支持了。
Codis
集群总结Codis
是一个代理中间件,经过内存保存着槽位和实例节点之间的映射关系,槽位间的信息同步交给ZooKeeper
来管理。Redis
实例没有回滚机制和WAL,因此是不支持的.本文参考资料:
推荐一篇大规模的Codis集群治理文章
更多干货,欢迎也能够关注个人公众号:spacedong