Redis集群 和 一致性hash算法

Redis集群简介

  • Redis 集群是一个提供多个Redis(分布式)节点间共享数据的程序集
  • Redis 集群的键空间被分隔了16384个hash槽(slot), 因此集群最大的节点数据是16384
  • Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行这些命令需要在多个 Redis 节点之间移动数据, 并且在高负载的情况下, 这些命令将降低 Redis 集群的性能, 并导致不可预测的错误。
  • Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

总结下优势:

  • 将数据自动切分(split)到多个节点的能力。
  • 当集群中的一部分节点失效或者无法进行通讯时, 仍然可以继续处理命令请求的能力。

Redis集群分片

  • Redis 集群是使用的hash槽来实现分片的
  • 一个redis集群包括; 0-16383 个hash槽, 所有的key都会映射到这些hash槽中
  • 集群使用公式slot=CRC16(key)%16384来计算key属于哪个槽,其中CRC16(key)语句用于计算key的CRC16 校验和
  • 按照槽来进行分片,就可以通过为每个节点指派不同数量的槽,可以控制不同节点负责的数据量和请求数.Redis集群 和 一致性hash算法_第1张图片

当前集群有3个节点, 槽默认是平均分的:

  • 节点 A (6381)包含 0 到 5499号哈希槽.
  • 节点 B (6382)包含5500 到 10999 号哈希槽.
  • 节点 C (6383)包含11000 到 16383号哈希槽.

这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我像移除节点A,需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.


Redis集群主从复制模型

为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下, 仍然可以正常运作。
Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)

在之前列举的节点 A 、B 、C 的例子中, 如果节点 B 下线了, 那么集群将无法正常运行, 因为集群找不到节点来处理 5501 号至 11000号的哈希槽。

另一方面, 假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5501 号至 11000 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。

不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。


Redis集群的一致性保证

Redis集群并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作:

第一个原因因为集群是用了异步复制.

写操作过程:

  • 客户端向主节点B写入一条命令.
  • 主节点B向客户端回复命令状态.
  • 主节点将写操作复制给他得从节点 B1, B2 和 B3
  • 主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡

Redis 集群另外一种可能会丢失命令的情况集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。

一致性hash算法

一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义:

1、平衡性(Balance): 平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。

2、单调性(Monotonicity): 单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

3、分散性(Spread): 在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。

4、负载(Load): 负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同 的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。


一致性哈希

在负载均衡策略中,可以应用一致性哈希,减少节点扩展时的数据失效或者迁移的情况。

一致性哈希是一种特殊的哈希算法。在使用一致性哈希算法后,哈希表槽位数(大小)的改变平均只需要对 K/n 个关键字重新映射,其中 K 是关键字的数量,n 是槽位数量。然而在传统的哈希表中,添加或删除一个槽位几乎需要对所有关键字进行重新映射。


一致性哈希基本思想

一致性哈希通过一个哈希环实现,Hash 环的基本思路是获取所有的服务器节点 hash 值,然后获取 key 的 hash,与节点的 hash 进行对比,找出顺时针最近的节点进行存储和读取。

举个例子 : 假设我们有 4 台缓存服务器:

  • A 服务器,地址 hash 结果是 100
  • B 服务器,地址 hash 结果是 200
  • C 服务器,地址 hash 结果是 300
  • D 服务器,地址 hash 结果是 400

现在有某条数据的 Key 进行哈希操作:

  • 得到 200,则存储在 B 服务器;
  • 得到 260,则存储在 C 服务器;
  • 得到 500,则存储在 A 服务器。

一致性哈希算法在扩展时,只需要迁移少量的数据就可以。
例如, 如果 D 服务器下线,原先路由到 D 服务器的数据,只要顺时针迁移到 A 服务器就可以,其他服务器不受影响,我们只需要移动一台机器的数据即可。


一致性哈希存在问题

一致性哈希虽然对扩容和缩容友好,但是存在另外一个问题,就很容易出现数据倾斜。

举个例子:
假设我们有 A、B、C 一直到 J 服务器,总共 10 台,组成一个哈希环。如果从 F 服务器一直到 J 服务器的 5 个节点宕机,那么这 5 台服务器原来的访问,都会被转移到服务器 A 之上,服务器的流量可能是原来的 5 倍或者更高,直到把服务器 A 打爆,这时候流量继续转移到 B 服务器,就出现缓存雪崩。


数据倾斜是如何解决的呢?
一个方案就是添加虚拟节点,对服务器节点也进行哈希操作,在整个哈希环上,均匀添加若干个节点
比如 a1 和 a2 都属于 A 节点,b1、b2 都属于 B 节点,这样在哈希时可以平衡各个节点的数据。

你可能感兴趣的:(Redis,redis)