分布式存储算法(Redis集群)

1.分布式存储之哈希取余算法

 

 

 算法实现:2 亿条记录就是 2 亿个 k,v,我们单机不行必须要分布式多机,假设有 3 台机器构成一个集群,

用户每次读写操作都是根据公式:hash(key) % N 个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。

优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如 3 台、8 台、10 台,就能保证一段时间的数据支撑。

使用 Hash 算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。

缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦,不管扩或缩,映射关系需要重新进行计算。

在服务器个数固定不变时没有问题,若需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:

Hash(key)/3   ————>>   Hash(key) /?

此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。

某个 redis 机器宕机了,由于台数数量变化,会导致 hash 取余全部数据重新洗牌。

2.分布式存储之一致性哈希算法

2.1 一致性哈希环

一致性哈希算法必然有个 hash 函数并按照算法产生 hash 值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个 hash 空间[0,2^32-1]。

这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32-1),这样让它逻辑上形成了一个环形空间。

 

 

2.2 节点映射(第一步哈希)

对存储节点进行哈希计算,也就是对存储节点做哈希映射,比如根据节点的 IP 地址进行哈希。

将集群中各个 IP 节点映射到环上的某一个位置。将各个服务器使用 Hash 进行一个哈希,具体可以选择服务器的 IP 或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
假如 4个节点 NodeA、B、C、D,经过 IP 地址的哈希函数计算(hash(ip)),使用 IP 地址哈希后在环空间的位置如下:

 

 

2.3 key 落到服务器的落键规则(第二部哈希)

当对数据进行存储或访问时,对数据进行哈希映射。

当我们需要存储一个 kv 键值对时,首先计算 key 的hash 值,hash(key),将这个 key 使用相同的函数Hash 计算出哈希值并确定此数据在环上的位置,
从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有 Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:
根据一致性 Hash 算法,数据 A 会被定为到 Node A 上,B 被定为到 Node B 上,C 被定为到Node C 上,D 被定为到 Node D 上。

 

 优点:(1)容错性

假设 Node C 宕机,可以看到此时对象 A、B、D 不会受到影响,只有 C 对象被重定位到 Node D。
一般的,在一致性 Hash 算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。
简单说,就是 C 挂了,受到影响的只是 B、C 之间的数据,并且这些数据会转移到 D 进行存储。
(2)扩展性
数据量增加了,需要增加一台节点 NodeX,X 的位置在 A 和 B 之间,那收到影响的也就是 A 到 X 之间的数据,重新把 A 到 X 的数据录入到 X 上即可,不会导致 hash 取余全部数据重新洗牌。
缺点:Hash 环的数据倾斜问题
一致性 Hash 算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜
(即被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器:

 

 

3.分布式存储之哈希槽算法(重点!)

哈希槽实质就是一个数组,数组[0,2^14 -1]形成hash slot 空间。

解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。

 

槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用 key 的哈希值来计算所在的槽,便于数据分配。

哈希槽的数量:

一个集群只能有 16384 个槽,编号 0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。
集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对 key 求哈希值,然后对 16384 取余,余数是几 key 就落入对应的槽里。
slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。

哈希槽计算:

Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。
如下代码,key 之 A 、B 在 Node2, key之 C 落在 Node3 上

 

 

 

posted on 2022-07-30 11:49  HHHuskie  阅读(478)  评论(0编辑  收藏  举报

导航