之前写过一篇介绍redis集群的文章,那篇可以方便大家入门,这篇是对这几个方案的原理介绍,方便对大家对这几个集群方案有个更加深入的了解,下面会分别对codis、sentinel、cluster三个集群方案分别进行详细的核心原理介绍,这里只是对核心原理进行介绍,让大家能够从本质上了解它们的区别方便大家进行选择,如果要更加深入的了解还是要看源码,好了开始。
Codis
在数据量比较大的情况下,使用单实例会因为内存大小导致空间不足,同时也会因为内存过大导致rdb文件过大从而使得master和slave之间数据全量同步时间过长,在有新节点加入时候会非常慢,在节点重启的时候也会非常慢。
数据集中式的方案当然不是我们希望看到的,所以这几年 各大厂都推出了自己的redis分片方案,随着这几年codis的快速发展,互联网公司使用的redis分布式分片中间件基本都是codis了,对于一些没有做缓存集中管理或者数据量本来就不大的公司,使用sentinel方案就可以满足主从互备的要求了,毕竟多了一个中间件多了一份不稳定因素。
下面我们就来详细看下Codis的实现原理。
Codis使用Go语言开发,是一个在客户端和redis之间的代理中间件,使用和redis一样的协议对外提供key/value服务,所以在客户端使用的时候完全感觉不到codis中间件的存在。Codis不但可以代理连接很多redis实例进行数据分片和互备,同时Codis因为是无状态的,也可以启动多个Codis实例,这样可以起到Codis实例HA互备的作用。
Codis分片原理
codis最核心的部分就是数据分片,下面我们来看下codis如何实现数据分片。
codis作为客户端和redis之间的代理,主要负责将特定的key转发到特定的redis实例,并将value返回给客户端。
codis将所有的key划分为1024个slot,首先对客户端传来的key进行crc32计算hash,再将hash值对1024取模,取模后的余数就是这个key的slot位置。每个slot都会唯一影射到一个redis实例,codis会在内存中维护slot和redis实例之间的对应关系。
解决了key值存放问题那么还有个问题要解决,就是不同codis实例之间如何同步这个slot和redis实例之间的映射关系数据,因为要保证客户端随便访问到一个codis实例都能够查询到key值对应的redis实例,那就要保障每个codis实例在内存中都保留一份映射关系。为了解决这个问题,codis使用了zookeeper和etcd来解决这个问题,codis将slot关系存储在zookeeper中,并且提供了codis dashboard来观察和修改slot关系,当slot关系发生变化后,codis proxy会监听到变化并重新同步slot映射关系到所有codis实例,从而实现多个codis节点之间的数据同步。
Codis扩容
大家喜欢使用codis的一个很大的原因就是codis可以根据业务量对于redis做到动态实例扩容。因为codis是redis分片代理方案有点类似数据库分库分表方案,当涉及到扩容那就涉及到slot和redis实例映射关系调整和数据的迁移,旧节点需要将数据迁移到新节点。
在有新增redis实例后,codis会扫描出旧节点中所有待迁移slot中的所有key,然后逐个将key迁移到新的redis节点中。在迁移过程中新旧节点中都有同一个slot,但是其中的key可能还没迁移完成,这时候可能有请求待迁移slot中的key,codis使用的方案不是去看这个key有没有完成迁移,而是立即强制对当前单个key进行迁移,迁移完成后,将请求转发到新的redis实例。
codis的难点不是在使用,而是在理解其中的分片和扩容的原理,这样可以方便我们在后续使用中做到心中有数,这里再说明下因为codis不是redis官方的方案,那么必定会存在和官方新版本不兼容的问题,这里需要注意,并且一些新版本的新特性可能不能及时支持。
sentinel
sentinel是官方的一种主从方案,,他的原理对比上面介绍的codis要简单很多,sentinel实现的原理有点类似于zookeeper,主要负责监控主从节点的健康状况,当master节点挂掉后,自动选择一个最优的slave节点顶替master节点,因为master和slave之间的数据是完全同步的,并且都是保存了全部数据,所以不存在数据的迁移和分片问题。客户端需要配置sentinel集群信息,在每次set和get数据的时候,sentinel都会告诉客户端去哪个redis节点进行操作。
唯一要处理的一个问题就是,当master节点切换后,sentinel会通知所有slave节点新的master节点地址,并且主从节点的数据复制关系也进行了变更,这些信息都会保存在sentinel中,并下发到所有节点。
总的来说sentinel的原理非常简单,sentinel的集群方案主要解决的是HA的问题,并不能解决超大缓存数据的分片和分散访问热点的问题。
Cluster
cluster是redis官方的集群方案,也是未来大家比较看好的一种集群方案,redis cluster内部实现了分布式的数据一致性,不用通过zookeeper这样的中间件来保证数据一致性。同codis一样,cluster方案也是一种数据分片方案,每个节点负责整个集群的一部分数据,每个节点之间通过自定义的二进制协议来传递集群信息。
redis cluster将所有数据分为16384个slot,他比codis的1024个slot划分更为精细,每个节点负责一部分slot。slot的映射信息保存在每个节点中,他和codis不一样,codis为了保证所有节点数据一致性需要将这些slot映射数据保存在zk或者etcd中,cluster方案因为内部已经实现分布式存储,所以不需要再有额外的分布式存储中间件。
当cluster的客户端来连接集群的时候,会得到一份集群的slot映射信息,这样客户端可以直接根据映射信息定位到key所在的redis节点。这样就会比用codis需要先到codis获取key所在节点,再去相应节点获取数据要快。但是这样也会存在客户端和服务器slot映射信息不一致的问题,这就需要cluster有补偿机制来保证。
cluster分片原理
cluster的分片原理其实和codis类似,对key使用crc16进行散列得到hash,然后对hash值对16384进行取模得到具体的slot。
cluster补偿原理
当客户端根据本地保存的slot映射信息访问了错误的redis节点时,就需要cluster有补偿机制来保证客户端可以访问到正确的节点。
当redis节点收到客户端这个错误的指令后,该节点会发现这个key的slot不归自己管,它会向客户端发送一个跳转指令并携带正确的目标地址,让客户端去正确的地址获取数据。同时客户端收到这个指令后会修改本地的slot映射表数据。
cluster数据迁移原理
和codis一样,当出现redis节点挂掉或者新增节点,那么就需要对redis节点进行数据迁移,cluster提供了人工调整slot的工具redis-trib,同时也有和codis一样的自动化迁移的功能,不需要人工干预。
redis cluster迁移的单位是slot,一个slot一个slot的迁移,当一个slot正在迁移的时候,这个slot在原节点的状态为migrating,目标节点的状态为importing。迁移的过程中,目标节点会先获取到原节点待迁移slot中所有的key,再逐个key迁移,当key保存到目标节点后,就会在源节点中删除数据。
当在迁移过程中,客户端访问cluster的方式也会发生很大变化并且和codis的方法也会有很大不同。首先新旧节点都有这个slot,但是因为迁移成功一个key后原节点就会删除,所以都只存在部分数据,这点和codis不同。这时客户端会先根据本地记录的slot映射表访问旧节点,如果数据还在旧节点,那么旧节点返回数据。如果数据不在旧节点那么又分为两种情况,一种是数据在新节点,另一种是数据不在这个slot中。旧节点不知道是哪种情况,会返回迁移目标节点的地址,客户端根据这个新地址重新访问数据,如果存在数据则返回给客户端,如果不存在则返回无数据。
cluster节点变更通知原理
当集群中有节点挂了,那么应该第一时间通知到客户端要更新本地的slot映射表,cluster是如何处理节点变更后通知到所有客户端呢?
cluster采用的是被动的方案,当目标节点挂了,客户端访问了挂掉的节点会抛出一个ConnectionError,这时客户端会在集群中随机找一个节点来重试,这时被重试的节点会告知客户端slot已经被分配到新的节点,同时客户端会关闭所有连接,清空本地保存的slot映射信息,然后重新初始化节点信息。
HA
cluster和codis一样不但有数据分片功能,同时也有HA主从复制功能,对于每个分片节点都可以配置对应的从节点,这样就可以做到节点主备。
总结
这篇文章主要介绍了主流的redis 3种集群方案的核心原理,如果大家仔细看了文章中讲到的原理后,因为会很清楚自己因该选择哪种集群方案了,这里简单总结下来就是,如果数据量不大,使用集群只是为了解决HA问题那使用简单不易出问题的sentinel方案就可以了;如果你的缓存数据量很大需要能做到灵活动态扩容那么可以使用codis或者官方的cluster方案,对于codis和cluster的选择可以根据公司的具体情况来选择,如果公司已经有成熟的公用zk和相关运维人员,那么建议使用codis毕竟那么多大厂用了那么多年,网上也有很多资料可查询,如果公司还没有现成的zk集群,那么可以考虑使用官方社区推崇的cluster方案,毕竟官方社区会一直维护下去,并且能享受到new feature。