【Codis简介】
首先理清楚几个概念,Redis的主从,从从都是一种备份机制,所有节点上的内容基本相同,从节点努力赶上主节点的数据状态;而Sentinel哨兵是一种调度机制,能够监控、并在主节点挂掉的时候择优选择从节点为新主节点。接下来要介绍的Codis是一种分治工具。
在大数据高并发的场景下,单个Redis实例难以支撑。1.单个redis内存不宜过大,内存过大会导致rdb文件过大,进一步导致主从同步时时间过长,在实例重启恢复时会消耗很长的数据加载时间;2.单个redis实例只能利用单个CPU核心,指望单个核心完成海量数据的存取和管理工作,压力会很大。
要解决上述问题,需要用到Redis集群,它是利用多个小内存的Redis实例,将分布在多个机器上的CPU核心的计算能力聚集到一起,完成海量数据存储与高并发读写操作。
Codis就是Redis集群方案之一,它是一个中间代理,和Redis一样也使用Redis协议对外提供服务,当客户端向Codis发送指令时,Codis负责将指令转发到后面的Redis实例执行,并将返回结果返回给客户端。客户端操作Codis与操作Redis几乎没有区别,甚至还可以使用相同的客户端SDK。
当集群空间不足时,可以通过动态增加Redis实例来实现扩容需求。
另外启动多个Codis代理还可增加整体的QPS,还能起到容灾功能,挂掉一个Codis代理不妨碍剩下的Codis代理继续服务。
【Codis分片原理】
Codis需要将特定的key转发到特定的Redis实例。Codis默认将所有的key划分为1024个slot,首先对传入的key进行crc32运算计算hash值,再将hash后的整数值对1024这个整数进行取模,余数就是对应key的slot。
每个slot会唯一映射到多个Redis实例之一,而一个Redis实例可能被多个slot指向,二者是多对一的关系。如图:
Codis会在内存中维护槽位和Redis实例的映射关系,如果集群节点比较多,建议将这个数值设置大一些,如2048,4096等。
【不同Codis实例之间的槽位关系同步】
Codis的slot-redis实例的关系如果只存储在内存中,那么不同codis实例之间的关系就无法同步。所以Codis还需要一个分布式配置存储数据库专门用来持久化槽位关系。Codis将槽位关系存储在zookeeper中,并且提供了一个Dashboard可以用来观察和修改槽位关系,当槽位关系变化时,Codis Proxy会监听到变化并重新同步槽位关系,从而实现多个Codis Proxy之间共享相同的槽位关系配置。
【扩容】
当增加redis实例时,codis需要将一部分slot对应的key分配给新的redis实例。首先,codis增加了SLOTSSCAN指令,可以遍历指定slot下的所有key,然后挨个迁移每个key到新的Redis节点。
在迁移过程中,Codis还是会接收到新的请求,打在正在迁移的slot上,Codis会强制对当前的key进行迁移,然后再将请求转发到新的Redis实例中。
【自动均衡】
自动均衡会在系统比较空闲时观察每个redis实例对应的slot数量,如果不平衡,就会自动进行迁移。
【Codis的缺点】
1.不支持事务:Redis的实例分散在了不同的机器,不再支持事务
2.不支持部分指令,Codis官方文档给出了一些列不支持的命令,比如rename,它的参数是两个key,如果这两个key在不同的Redis实例中,该操作无法正确完成
3.为了支持扩容,单个key对应的value不宜过大,否则会带来迁移卡顿。官方给的建议是,单个集合结构总字节容量不要超过1M,如果存放的是社交关系数据,可以考虑分桶存储,在业务上做折中。
4.网络开销,作为中间层,所以网络开销会比redis大,但不明显
5.需要部署zookeeper,运维代价
【mget指令操作过程】
mget用于批量获取多个key值,这些key值可能分布在多个Redis实例中。Codis的策略是将key按照所分配的实例打散分组,然后依次对每个实例调用mget方法,最后将结果汇总为一个,再返回给客户端。