如何深入了解Redis中的Codis

70次阅读
没有评论

共计 3084 个字符,预计需要花费 8 分钟才能阅读完成。

这篇文章给大家介绍如何深入了解 Redis 中的 Codis,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

场景

在大数据高并发场景下,使用单个 redis 实例,即使 redis 的性能再高,也会变的非常吃力,

首先,数据量越大,redis 占用内存就越大,进一步导致 rdb 文件过大,这种情况会使的主从全量同步时间过长,同时实例重启时,加载过大的 rdb 也会让启动时间变长。【相关推荐:Redis 视频教程】

其次在 CPU 的使用上,单个实例的 Redis 只能使用一个 CPU 核心,一个核心应多过多的数据,也会显得力不从心,

因此需要一个集群方案,将巨大的数据量由一台实例分散到多台实例上,从 Redis 流行到官方支持自己的 Cluster 方案之间,第三方也在自己开发支持集群的组件,Codis 就是其中之一,

Codis 使用 Go 语言开发,在 Redis 与客户端中间充当代理的角色,使用 Redis 协议,所以客户端直接连接 Codis,向其发送指令即可,Codis 负责转发指令给 Redis,最后接收返回结果再返回给客户端,

Codis 代理的 Redis 实例构成一个 Redis 集群,当集群空间也不足以使用时,可以动态扩容,继续增加 Redis 实例,与此同时,客户端使用的 sdk 不需要做任何改动,只需由原来的连接 redis 改成连接 codis 即可,

Codis 自身也可以采取一个集群,来保证自身的高可用,由于其本身就是无状态的,只负责转发内容,增加多个 Codis 没有副作用还可以保证 QPS 的提高,当其中一个 Codis 挂掉时,还可以使用别的。

原理

Codis 将特定的 Key 转发到特定的 Redis 实例,集群中每个实例都保存一部分 Key,降低其他实例的压力,同时所有实例的数据加起来,就是一份完整的信息。

Codis 默认划分了 1024 个槽位(slot),集群中的每个 Redis 实例对应一部分槽位,Codis 会在内存中维护槽位与 Redis 实例的对应关系,

槽位的数量默认是 1024,可以更改,如果集群节点比较多,可以将数字调大。

当接收到客户端发送过来的 key 时,Codis 对该 key 进行 crc32 运算得出一个 hash 值,

再将 hash 后的整数值对 1024(槽位数量) 进行取模得到一个余数,该余数就是 Key 将被保存到的槽位,有了槽位就可以找到这个 key 该发到哪个 redis 实例上了。

伪代码:

hash = crc32(command.key) #  计算 hash 值
slot = hash % 1024 #  取模得到槽位
redisInstance = slots[slot].redis #  得到 redis 实例
redis.do(command) #  执行命令复制代码

集群槽位同步

Redis 与槽位的映射关系存在 Codis 的内存当中,因此 Codis 集群需要考虑保证每个节点中的槽位映射关系同步,所以 Codis 采用 Zookeeper、Etcd 分布式配置存储中间件来持久化槽位映射关系,保证 Codis 集群之间的数据同步,

如下图,Codis 将槽位关系存在 Zookeeper 中,并提供了一个 Dashboard 观察与修改槽位关系,当发生改变时,Codis Proxy 监听到变化并重新同步槽位关系。

拓容

当现有集群也不满足业务需求时,就需要新增实例加入到的集群中,此时槽位映射关系需要进行重新分配,需要分配一部分的槽位给新节点。

Codis 新增了一个 SLOTSSCAN 指令,可以遍历指定 slot 下的所有 key,通过该指令扫描出待迁移槽位的所有 key,然后挨个遍历每个 key 迁移到新节点中,

迁移过程中,Codis 继续对外提供服务,此时来了一个请求打在了正在迁移的槽位上,由于该槽位现在对应新老两个节点,此时 Codis 无法判断该 key 有没有从旧节点中迁移到新节点上,

因此这种情况 Codis 会立即强制对当前的 key 进行单个迁移,迁移完成后,将请求转发给新的 Redis 实例上。

伪代码:

slot_index = crc32(command.key) % 1024
if slot_index in migrating_slots:
 doMigratingKey(command.key)
 redis = slots[slot_index].new_redis
else:
 redis = slots[slot_index].redis 复制代码

SLOTSSCAN 与 Redis 自身的 Scan 指令一样,无法避免扫描出来的数据重复,但这不会影响到迁移的正确性,因为单个 key 迁移之后,就立刻从旧实例中删除了,无法再被扫描出来。

自动均衡槽位

每次新增实例,如果都需要人工维护 slot 的映射关系太麻烦,Codis 提供自动均衡,该功能会在系统比较空闲的时候观察每个 Redis 实例对应的 slot 数量,如果不平衡,就进行自动均衡,迁移数据的操作。

缺点

Codis 给 Redis 带来扩容好处,但也造成了一些副作用。

不支持事务

一个事务可能对多个 key 做了操作,但事务只能在单个实例中完成,但是由于 key 分散在不同的实例中,因此 Codis 无法支持事务操作。

不支持 rename

rename 将一个 key 命名成另一个 key,但是这两个 key 可能 hash 出来的槽位并不是同一个,而是在不同实例的槽位上,因此 rename 也不被支持。

官方提供的不支持的指令列表:https://github.com/CodisLabs/codis/blob/master/doc/unsupported_cmds.md

扩容卡顿

Codis 在扩容过程中,对数据的迁移是将整个 key 直接迁移过去的,例如一个 hash 结构,Codis 会直接 hgetall 拉取所有的内容,使用 hmset 放到 新节点中,

如果该 hash 的内容过大,将会引起卡顿,官方建议单个集合结构的总大小不超过 1MB,在业务上可以通过分桶存储等,将大型数据拆成多个小的,做一个折中。

网络开销

由于 Codis 在 客户端与 Redis 实例之间充当网络 Proxy,多了一层,网络开销自然多一些,比直接连接 Redis 的性能要稍低一些。

中间件运维开销

Codis 集群配置需要使用 Zk 或 Etcd,这意味着引入 Codis 集群又要引入其他中间件,增加运维机器资源成本。

优点

Codis 将分布式一致性的问题交给了第三方 (ZK 或 Etcd) 负责,省去了这方面的维护工作,降低实现代码的复杂性,

Redis 官方的 Cluster 为了实现去中心化,引入了 Raft 与 Gossip 协议,以及大量需要调优的配置参数,复杂度骤增。

批量获取

对于批量操作,例如使用 mget 获取多个 key 的值,这些 key 可能分散在多个实例中,Codis 将 key 按照所在的实例进行分组,然后对每个实例挨个调用 mget,最后汇总返回给客户端。

其他功能

Codis 提供 Dashboard 界面化,以及 Codis-fe 对集群进行管理,还可以进行增加分组、节点、执行自动均衡等操作,查看 slot 状态以及 slot 对应的 redis 实例,这些功能使的运维更加方便轻松。

Codis 是为了弥补 Redis 官方没有提供集群这一概念时出现的,现在 Redis 官方提供 Cluster 功能,官方的支持自然比第三方的更有优势,

同时第三方软件还需要实时关注官方发布的新特性各种,而 Cluster 肯定是实时兼容新特性,因此更推荐使用官方的 Cluster,Codis 作为曾经的一个知识点了解,某些思想与 Cluster 是有重合的。

关于如何深入了解 Redis 中的 Codis 就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-15发表,共计3084字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)