redis:codis 电玩女神 2022-09-14 15:26 240阅读 0赞 在大数据高并发场景下,单个redis实例往往不足以应付。首先体现在内存上,单个redis的内存不宜过大,内存太大会导致rdb文件过大,进一步导致主从同步时**全量同步**时间过长,在实例重启恢复时也会消耗很长的数据加载时间,特别是在云环境下,单个实例内存大小往往都是受限的。其次体现在CPU的利用率上,单个redis实例只能利用单个核心,单个核心要完成海量数据的存取和管理工作,压力会非常大。 正是在这样的大数据高并发的需求之下,redis集群方案应运而生。它可以将众多小内存的redis实例整合起来,将分布在多台机器上的众多CPU核心的计算能力聚集在一起,完成海量数据存储和高并发读写操作。 codis是redi集群方案之一,由中国人开发并开源 ![在这里插入图片描述][c381e88ceba64b7c92f138b68837fb99.png] 从redis的广泛流行到redis cluster的广泛使用,其间间隔了好多年,codis就是在这样的市场空缺下发展出来了。大型公司有明确的redis在线扩容需求,但是市场上并没有特别好的中间件可以做到这一点 codis使用go语言开发,它是一个代理中间件,和redis一样使用redis协议对外提供服务,当客户端向codis发送指令时,codis负责将指令转发到后面的redis实例来执行,并返回结果再转回给客户端。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_16_color_FFFFFF_t_70_g_se_x_16] codis上挂接到所有redis实例构成一个redis集群,当集群空间不足时,可以通过动态增加redis实例来实现扩容需求。 客户端操作codis和操作redis几乎没有区别,还可以使用相同的客户端sdk,不需要任何变化。 因为codis是无状态的,它只是一个转发代理中间件,这意味着我们可以启用多个codis实例,供客户端使用,每个codis节点都是对等的,如下图。因为单个codis代理能够支持的QPS比较有限,通过启动多个codis代理可以显著增加整体的QPS需求,还能起到容灾功能,挂掉一个codis代理没有关系,还有很多codis代理可以继续服务。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_13_color_FFFFFF_t_70_g_se_x_16] # codis分片原理 # codis要负责将特定的key转发到特定的redis实例,那么这种对应关系如何管理呢? * codis默认将所有的key划分为1024个槽位(slot) * 它首先**对客户端传来的key**进行crc32运算计算hash值,再将hash后的整数值对1024这个整数进行取模得到一个余数,这个余数就是对应的key的槽位 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_9_color_FFFFFF_t_70_g_se_x_16] 如上图,每个槽位都会唯一映射到后面的多个redis实例之一,**codis会在内存中维护槽位和redis实例的映射关系**。这样有了上面key对应的槽位,那么它应该转发到哪个redis实例就很明确了。 ![在这里插入图片描述][2c7c669cfc6548c2b1d3a4be562fb128.png] 槽位数量默认是1024,它是可以设置的,如果集群节点比较多,建议将这个数值设置大一点,比如2048、4096等 # 不同的codis实例之间槽位关系如何同步 # 如果codis的槽位映射关系只存储在内存里面,那么不同的codis实例之间的槽位关系就无法得到同步。所以codis还需要一个分布式配置存储数据库专门用来持久化槽位关系。codis开始使用zookeeper,后来连etcd也一块支持了。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_12_color_FFFFFF_t_70_g_se_x_16] 如上图 * codis将槽位关系存储在zookeeper中,并且提供了一个Dashboard可以用来观察和修改槽位关系 * 当槽位关系变化时,codis proxy会监听变化并重新同步槽位关系,从而实现多个codis proxy之间共享相同的槽位关系。 # 扩容 # 刚开始codis后端只有一个redis实例,1024个槽位全部指向同一个redis。然后一个redis实例内存不够了,所以又加了一个redis实例。**这时候需要对槽位关系进行调整,讲一半的槽位划分到新的节点。这意味着需要对这一半的操作对应的所有key进行迁移,迁移到新的redis实例**。 > 那么codis如何找到槽位对应的所有key呢? codis对redis进行了改造,增加了SLOTSSCAN指令,可以遍历指定slot下所有的key。codis通过SLOTSSCAN扫描出待迁移槽位的所有key,然后挨个迁移每个key到新的redis节点。 在迁移过程中,**codis还是会接收到新的请求打在当前正在迁移的槽位上,因为当前槽位的数据同时存在于新旧两个槽位中**,codis如何判断该讲请求转发到哪个具体实例呢? **codis无法判断迁移过程中的key究竟在哪个实例中,所以它采用了另一种完全不同的思路。当codis接收到位于正在迁移槽位中的key后,会立即强制对当前的单个key进行迁移,迁移完成后,再将请求转发到新的redis实例。(??????)** ![在这里插入图片描述][271524f06cf1465eb8f57ad2d8834fa8.png] 我们知道redis支持的所有scan指令都是无法避免重复的,同样codis自定义的SLOTSSCAN也是一样,但是这并不影响迁移。因为单个key被迁移一次后,在旧实例中它就被彻底删除了,也就不肯能会被再次扫描出来了。 # 自动均衡 # redis新增实例,手动均衡slot太烦琐,所以codis提供了自动均衡功能。自动均衡会在系统比较空闲的时候观察每个redis实例对应的slot数量,如果不平衡,就会自动进行迁移 # codis的缺点 # codis给redis带来了扩容的同时,也损失了其他一些特性。 * **因为codis中所有的key分散在不同的redis实例中,所以就不能再支持事务了,事务只能在单个redis实例中完成** * rename操作也很危险,它的参数是两个key,如果这两个key在不同的redis实例中,rename操作是无法正确完成的。 codis的官方文档中给出了一系列不支持的命令列表 * 为了支持扩容,**单个key对应的value不宜过大**,因为集群迁移的最小单位是key,对于一个hash结构,它会一次性使用`hgetall`拉取所有的内容,然后使用`hmset`将之放置到另一个节点中。如果hash内部的key/value太多,可能会导致卡顿。官方建议单个集合结构的总字节容量不要超过1MB。 * codis因为增加了proxy作为中转层,所以在**网络开销上要比单个redis大**,毕竟数据包多走了一个网络节点,整体性能上要比单个redis的性能有所下降,但是这部分性能损耗不太明显,可以通过增加proxy的数量来弥补性能上的不足。 * codis的集群配置中心使用zookeeper来实现 ,这意味着**在部署上增加了zookeeper运维为代价**,不过大部分互联网内部都有zookeeper集群,使用现有的zookeeper集群即可 另外,codis不是redis官方项目,redis官方提供的最新功能,codis往往要等很久才能同步。而且现在官方redis cluster已经开始流行起来,codis能否保持竞争力还是个问题 # codis的优点 # * codis在设计上相比redis cluster官方集群方案要简单的多,因为它将分布式的问题交给了第三方(zookeeper或etcd)去负责,自己就省去了复杂的分布式一致性代码的编写维护工作。 * 与之相比,redis cluster的内部实现非常复杂,它为了实现去中心化,混合使用了复杂的raft和gossip协议,还有大量的需要调优的配置参数,当集群出现故障时,维护人员往往不知道从何处入手 # mget指令的操作过程 # mget指令用于批量获取多个key的值,这些key可能会分布在多个redis实例中。codis的策略是将key按照所分配的实例打散分组,然后依次对每个实例调用mget方法,最后将结果汇总为一个,再返回给客户端。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_14_color_FFFFFF_t_70_g_se_x_16] [c381e88ceba64b7c92f138b68837fb99.png]: /images/20220828/dfca3b62b5e542fcace4caf88c34cd71.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_16_color_FFFFFF_t_70_g_se_x_16]: /images/20220828/a396aa7afa40430ba50621c79c1e47b5.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_13_color_FFFFFF_t_70_g_se_x_16]: /images/20220828/d29d2e2f90bf4dbc87e988817b23555e.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_9_color_FFFFFF_t_70_g_se_x_16]: /images/20220828/b33ad10c0a4c48b19495b88f8040f6de.png [2c7c669cfc6548c2b1d3a4be562fb128.png]: /images/20220828/20bb394c560c4f1db0bd38aae52a98c8.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_12_color_FFFFFF_t_70_g_se_x_16]: /images/20220828/4b17e472435b4746b647384f67f16779.png [271524f06cf1465eb8f57ad2d8834fa8.png]: /images/20220828/51ef1a455f3a447bb99d6bc33b925883.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAT2NlYW4mJlN0YXI_size_14_color_FFFFFF_t_70_g_se_x_16]: /images/20220828/3cc6f22ecdc64a8183433e175eae6ffb.png
还没有评论,来说两句吧...