Redis-集群Cluster
上篇文章谈到的哨兵机制,提高了系统的可用性,但是真正存储数据的还是master主节点和slave从节点,所有的数据都要存储在master和slave上.当数据量很大时,超出master/slave所在物理机的物理内存,就可能会出现很严重的问题了.
怎样获取更大的空间来存储数据呢?
目录
1.redis集群:
2.数据分片算法:
1.哈希求余:
2.一致性哈希算法:
3.哈希槽分区算法(Redis采用的):
这里有两个问题:
所谓"大数据"的核心是一台机器存储不下了,可以用多态机器共同来存储.
1.redis集群:
redis集群就在解决上述问题:
通过引入多组master/slave,每组matser/slave存储数据全集的一部分,从而构成一个更大的整体,就称为redis集群.
将每一组master/slave称为一个"分片".
当数据量进一步增多时,通过增加分片的方法即可解决.
2.数据分片算法:
redis-cluster通过多个分片共同存储所有数据,又有两个核心问题:
1 .当写入一个数据,要存储到哪个分片上;读取数数据时,从哪个分片读取;
2. 当当前分片由于数据的增多又无法存储,需要再增加新分片机器时,又要如何调整呢?
这里有几种方法来实现:
1.哈希求余:
设有N个分片,对其进行编号[0,N-1]; 针对某一数key,先对其求哈希值,得到一个整数,把得到的hash值%分片个数N,得到的值就是要存储的分片编号.
后续要取数据时,也是针对要取得key,对key计算hash值,再对其%N,就可以找到对应的分片编号了.
优点: 简单高效,数据分配均匀;
缺点: 一旦需要进行扩容时,N就要改变原有的映射规则就会被破坏,需要让节点之间相互数据传输, 对大量的数据进行重新排列,以满足新的映射规则,开销较大.
MD5算法 是一种计算哈希值的常用方法,MD5计算结果特点:
1. 计算结果是定长的: 16位/32位/64位
2.计算结果是分散的: 相差很小的key计算出的MD5值都有很大的差别.
3. 计算结果是不可逆的: 计算出的结果是无法复原的.
2.一致性哈希算法:
为了降低数据的搬运开销,更高效的扩容, 一致性和希算法 可以缓解上述问题:
具体过程:
1>. 将0-2^32-1这个数据空间,映射到一个圆环上,按照顺时针的方向增长;
2>. 首次分片时,将分片均匀的放到圆环上的某些位置.
3>. 当要存储一个数据时,计算其hash值, 根据hash值,在圆环上顺时针向下找,找到的第一个分片,就存储在该分片上;读取数据也是这样,先计算要读取数据的hash值,然后在圆环上顺时针向下找,找到的第一个分片就是数据所在的位置.
分片扩容时,仅需要在圆环的某个位置上安排一个新的分片即可,仅将圆环上 新分片插入位置 之前的数据从原分片上传到新分片上即可,无需对整体的数据进行调整.缓解了大量数据迁移的开销.
优点: 大大降低了分片扩容时,数据搬运的开销,提高了扩容操作的效率.
缺点: 这样分片会导致数据不均匀分布,出现数据倾斜.(有可能有的分片上一个数据也没有,浪费硬件资源)
3.哈希槽分区算法(Redis采用的):
上面两种分片方法都存在一定的缺陷,redis-cluster 并未采取上述两种方法,而是使用哈希槽分区算法. 该方法解决了数据搬运成本高和数据不均匀分配的问题.
算法规则:
把整个hash值,映射到16384个槽位上: [0,16383], 然后将这些槽位均匀的分给每一个分片. 每一个分片都要记录自己持有哪些槽点.
写入数时,先对16384求余,计算出哈希槽,然后根据哈希槽找到所在得分片,就可以将数据写入了.
当要扩容分片时,针对原有分片重新分配,从每个分片上拿走一些槽位,放到新的分片上,保证每个分片上槽位尽量均匀即可. 槽位分配可以是连续的,也可以是不连续的,非常灵活,每个分片使用"位图"表示该分片上所属于的操作.
假设当前有3个分片,可以这样分配:
0号分片:[0,5461],共5462个槽位;
1号分片: [5462,10923],共5462个槽位;
2号分片: [10924,16383],共5460个槽位.
当要新增一个分片时: 只需要从0号,1号和2号 分片上取走一些槽位放到3号分片上,保证槽位尽量均匀分布到不同的分片上即可.
这里有两个问题:
1.槽位一共有16384个,那么redis集群最多只能有16384个分片吗?
当出现16384这么多分片时,就无法保证每个分片上的数据的均匀性了.
我们分片包含多个槽位时,可以认为每个分片上包含的key的数量是相当的;当分片上的槽位非常少的时候,就无法直观反映key的数目了.(有的槽位上的key非常多,有的非常少)
redis官方建议做多使用1000个分片. 当使用的分片过于多的时候,整个数据服务器的集群是非常大的,可用性就难以估量了.
2.为什么槽位设置的是16384个?
节点之间通过"心跳包"进行通信,心跳包中包含了该节点持有哪些槽位,使用位图这样的数据结构来表示16384个槽位,这个位图占用2kb的空间,若这个数太大了,就要占用更大的存储空间,且节点间发送心跳包是周期性的,非常频繁,会占用更大的网络带宽.
另外,redis集群建议使用不超过1000个分片,16384对于1000个分片来说是够用的,也不会占用较大的空间, 很合适.
下一篇文章就要搭建redis集群了.