前面介绍了《进阶的Redis之数据持久化RDB与AOF》和《进阶的Redis之Sentinel原理及实战》,此次来了解下Redis的集群功能,以及其中哈希分片原理。node
若是Redis只用复制功能作主从,那么当数据量巨大的状况下,单机状况下可能已经承受不下一份数据,更不用说是主从都要各自保存一份完整的数据。在这种状况下,数据分片是一个很是好的解决办法。redis
Redis的Cluster正是用于解决该问题。它主要提供两个功能:算法
对于第二点,它的功能有点相似于Sentienl的故障转移(能够了解下以前Sentinel的文章),在这里不细说。下面详细了解下Redis的槽位分片原理,在此以前,先了解下分布式简单哈希算法和一致性哈希算法,以帮助理解槽位的做用。缓存
假设有三台机,数据落在哪台机的算法为bash
c = Hash(key) % 3
复制代码
例如key A的哈希值为4,4%3=1,则落在第二台机。Key ABC哈希值为11,11%3=2,则落在第三台机上。微信
利用这样的算法,假设如今数据量太大了,须要增长一台机器。A本来落在第二台上,如今根据算法4%4=0,落到了第一台机器上了,可是第一台机器上根本没有A的值。这样的算法会致使增长机器或减小机器的时候,引发大量的缓存穿透,形成雪崩。分布式
在1997年,麻省理工学院的Karger等人提出了一致性哈希算法,为的就是解决分布式缓存的问题。测试
在一致性哈希算法中,整个哈希空间是一个虚拟圆环 ui
假设有四个节点Node A、B、C、D,通过ip地址的哈希计算,它们的位置以下 spa
有4个存储对象Object A、B、C、D,通过对Key的哈希计算后,它们的位置以下
一致性哈希算法大概如此,那么它的容错性和扩展性如何呢?
假设Node C节点挂掉了,Object C的存储丢失,那么它顺时针找到的最新节点是Node D。也就是说Node C挂掉了,受影响仅仅包括Node B到Node C区间的数据,而且这些数据会转移到Node D进行存储。
同理,假设如今数据量大了,须要增长一台节点Node X。Node X的位置在Node B到Node C直接,那么受到影响的仅仅是Node B到Node X间的数据,它们要从新落到Node X上。
因此一致性哈希算法对于容错性和扩展性有很是好的支持。但一致性哈希算法也有一个严重的问题,就是数据倾斜。
若是在分片的集群中,节点太少,而且分布不均,一致性哈希算法就会出现部分节点数据太多,部分节点数据太少。也就是说没法控制节点存储数据的分配。以下图,大部分数据都在A上了,B的数据比较少。
Redis集群(Cluster)并无选用上面一致性哈希,而是采用了哈希槽(SLOT)的这种概念。主要的缘由就是上面所说的,一致性哈希算法对于数据分布、节点位置的控制并非很友好。
首先哈希槽实际上是两个概念,第一个是哈希算法。Redis Cluster的hash算法不是简单的hash(),而是crc16算法,一种校验算法。
另一个就是槽位的概念,空间分配的规则。其实哈希槽的本质和一致性哈希算法很是类似,不一样点就是对于哈希空间的定义。一致性哈希的空间是一个圆环,节点分布是基于圆环的,没法很好的控制数据分布。而Redis Cluster的槽位空间是自定义分配的,相似于Windows盘分区的概念。这种分区是能够自定义大小,自定义位置的。
Redis Cluster包含了16384个哈希槽,每一个Key经过计算后都会落在具体一个槽位上,而这个槽位是属于哪一个存储节点的,则由用户本身定义分配。例如机器硬盘小的,能够分配少一点槽位,硬盘大的能够分配多一点。若是节点硬盘都差很少则能够平均分配。因此哈希槽这种概念很好地解决了一致性哈希的弊端。
另外在容错性和扩展性上,表象与一致性哈希同样,都是对受影响的数据进行转移。而哈希槽本质上是对槽位的转移,把故障节点负责的槽位转移到其余正常的节点上。扩展节点也是同样,把其余节点上的槽位转移到新的节点上。
但必定要注意的是,对于槽位的转移和分派,Redis集群是不会自动进行的,而是须要人工配置的。因此Redis集群的高可用是依赖于节点的主从复制与主从间的自动故障转移。
下面以最简单的例子,抛开高可用主从复制级转移的内容,来重点介绍下Redis集群是如何搭建,槽位是如何分配的,以加深对Redis集群原理及概念的理解。
先找到redis.conf,启用cluster功能。
cluster-enabled yes
默认是关闭的,要启用cluster,让redis成为集群的一部分,须要手动打开才行。
而后配置cluster的配置文件
本次为了方便搭建,全部Redis实例都在同一台机器上,因此修改不一样的cluster config名字后,复制三份redis.conf配置,以用于启动三个集群实例(cluster至少要三个主节点才能进行)。
> redis-server /usr/local/etc/redis/redis-6379.conf --port 6379 &
> redis-server /usr/local/etc/redis/redis-6380.conf --port 6380 &
> redis-server /usr/local/etc/redis/redis-6381.conf --port 6381 &
复制代码
&符号的做用是让命令在后台执行,但程序执行的log依然会打印在console中。也能够经过配置redis.conf中deamonize yes
,让Redis在后台运行。
连上6379的Redis实例,而后经过cluster nodes
查看集群范围。
在6379上,经过cluster meet
命令,与6380、6381创建连接。
127.0.0.1:6379> cluster meet 127.0.0.1 6380
127.0.0.1:6379> cluster meet 127.0.0.1 6381
复制代码
经过cluster info
命令查看集群的状态
接下来给6379分配0~5000的槽位,给6380分配5001~10000的槽位,给6381分配10001~16383的槽位。
> redis-cli -c -p 6379 cluster addslots {0..5000}
> redis-cli -c -p 6380 cluster addslots {5001..10000}
> redis-cli -c -p 6381 cluster addslots {10001..16383}
复制代码
再看看cluster info
随便登上一个实例,记得加上参数-c
,启用集群模式的客户端,不然没法正常运行。
redis-cli -c -p 6380
复制代码
尝试下set、get操做
利用cluster keyslot
命令计算出key是在哪一个槽位上,从而得出会跳转到哪一个节点上执行。
更多技术文章、精彩干货,请关注
博客:zackku.com
微信公众号:Zack说码