之前我们讨论的话题,无论是主从复制,还是哨兵模式,都是单个redis实例在工作。在大数据高并发的场景下,这种部署方案会显得力不从心。
首先单个实例的内存不宜过大,过大的内存,会导致rdb文件过大,从而导致主从同步时全量同步时间过长,重启时也需要消耗很长的时间加载数据。
其次体现在cpu的利用率上,单个redis实例只能利用单个核心,处理海量数据时,压力很大。
Codis是redis集群解决方案之一,它将众多小内存的reids实例整合起来,将分布在多台机器上的众多cpu的计算能力聚集到一起,完成海量数据的存储与高并发的读写。
Codis是豌豆荚中间件团队发明的,在redis cluster未广泛使用时,codis起到了至关重要的作用。
Codis是Go语言开发的代理中间件,如下图所示:
Codis也采用redis的协议,对外提供服务,但是由于代理的机制,导致其有些命令是无法支持的。
Codis是无状态的,这意味着我们可以启用多个codis实例提供给client,每个codis实例是对等的,这样可以提高codis集群的qps,也可以起到容灾的作用。
Codis分片机制
Codis默认所有的key划分为1024个slot,对客户端传入的key做crc32运算计算hash值,再将hash后的整数值对1024取模获取key的slot。codis会在内存中维护slot与redis实例的对应关系。根据映射关系将数据转发到对应的实例。
slot数量是可以设置的。
不同的codis实例之间slot关系是如何同步的?
codis集群通过对zk与etcd的支持来保证数据的一致性,如果是依赖zk,那么codisProxy的slot关系信息会存储在zk节点上,通过zk的监听机制来共享slot信息。
如下图:
管理员可以通过dashboard维护slot相关信息,然后再同步到各个codisProxy。
如何扩容?
进行扩容,意味着集群中增加新的redis实例,这时slot与实例的映射关系需要调整,意味着一部分数据需要进行迁移。
首先第一个问题是,我们需要找到槽位对应的所有key。codis增加了slotsscan命令,可以遍历指定slot下所有的key。然后挨个将每个key迁移到新的redis节点。迁移过程中,codis收到新请求,如果是查询key,那么会强制先完成迁移工作,然后再提供对外服务。
最后一点,迁移操作是一个move操作,即迁移完成后,旧实例中就不存在key了。
自动均衡机制
redis新增实例,手动均衡slot比较麻烦,所以codis提供了自动均衡机制。自动均衡机制会在系统空闲时观察每个实例对应的slot数量,不平衡会自动进行迁移。
Codis的劣势
1.使用codis扩容的机器,redis不再支持事务。
2.rename这种危险的命令也不支持。官方文档中提供了不支持的命令列表。
3.为了支持迁移,单个key对应的value不宜过大。过大会导致迁移卡顿,官方建议小于1M,所以不适合存放社交关系数据等等。
4.网络开销比单个实例要大,性能略微下降。可以通过增加代理数量来弥补性能不足。
5.如果依赖zk,那么会增加zk运维成本。
Codis的优势
1.设计上比官方的redis cluster方案要简单。
2.托管给zk或者etcd,省去了分布式一致性逻辑。
问题:mget查询多个key的场景,codis会将key按照映射关系分组,然后对涉及的redis执行mget,最后由codis汇总返回。