一、集群简介
本文章简要地介绍了Redis集群,它不使用特别深入地来理解分布式系统的概念。 它提供了关于如何设置集群、测试和操作它的说明,而不涉及Redis集群规范中涉及的细节,只是从用户的角度描述了系统的行为。
然而,本教程试图从最终用户的角度提供关于Redis集群可用性和一致性特征的信息,以一种简单易懂的方式进行说明。
注意:本教程要求Redis 3.0或更高版本。
如果您计划运行一个正式的Redis集群部署,建议阅读更正式的规范,即使不是严格要求的。不过,最好从本文开始,花些时间使用Redis集群,然后再阅读规范。
1、集群特性
Redis集群提供了一种方法来运行一个Redis安装,其中数据被自动分片到多个Redis节点。 Redis集群还在分区期间提供了一定程度的可用性,即在某些节点失败或无法通信时继续操作的能力。 但是,当出现较大的故障时(例如,当大多数主机不可用时),集群将停止运行。
在实际应用中,你能从Redis集群中得到什么?
-
在多个节点之间自动分割数据集的能力。
-
当节点子集发生故障或无法与集群的其他部分通信时,继续运行的能力。
2、 Redis集群TCP端口
每个Redis集群节点都需要打开两个TCP连接。一个是用于服务客户端的常规Redis TCP端口,例如6379,另一个是常规端口加10000所获得的数据端口,因此在示例中为16379。
第二个端口用于集群总线,这是一个使用二进制协议的节点到节点通信通道。 节点使用集群总线进行故障检测、配置更新、故障转移授权等。 客户端永远不应该尝试与集群总线端口通信,而应该始终使用普通的Redis命令端口,但是要确保在防火墙中打开两个端口,否则Redis集群节点将无法通信。
命令端口和集群总线端口偏移量是固定的,总是10000。
请注意,为了让Redis集群正常工作,您需要对每个节点:
1、 用于与客户机通信的普通客户机通信端口(通常为6379)向所有需要到达集群的客户机以及所有其他集群节点(使用客户机端口进行密钥迁移)开放。
2、 集群总线端口(客户机端口+ 10000)必须能够从所有其他集群节点访问。
如果不同时打开两个TCP端口,集群将无法正常工作。
集群总线使用一种不同的二进制协议来进行节点间的数据交换,这种协议更适合于在节点之间交换信息,只需要很少的带宽和处理时间。
3、 Redis集群数据分片
Redis集群不使用一致的散列,而是一种不同的分片形式,其中每个键在概念上都是我们所说的散列槽的一部分。
Redis集群中有16384个哈希槽,要计算给定键的哈希槽是多少,只需取CRC16(键)%16384。
Redis集群中的每个节点负责哈希槽的子集,例如,您可能有一个包含3个节点的集群,其中:
-
节点A包含从0到5500的哈希槽。
-
节点B包含从5501到11000的哈希槽。
-
节点C包含从11001到16383的哈希槽。
这使得我们可以轻松地添加和删除集群中的节点。例如,如果我想添加一个新节点D,我需要将一些哈希槽从节点A、B、C移动到D。 类似地,如果我想从集群中删除节点A,我可以将由A提供的哈希槽移动到B和C。 当节点A为空时,我可以将它从集群中完全删除。
因为将哈希槽从一个节点移动到另一个节点不需要停止操作,所以添加和删除节点,或者更改节点持有的哈希槽的百分比,都不需要任何停机时间。
只要一个命令执行(或整个事务,或Lua脚本执行)中涉及的所有键都属于同一个散列槽,Redis集群就支持多个键操作。 用户可以使用一个称为哈希标签的概念强制多个键成为同一个哈希槽的一部分。
散列标签在集群规范有文档说明,但主旨是,如果键在{}里有子字符串,只有这个子字符串被散列,例如 this{foo}key和 another{foo}key是保证在同一散列槽,并可以在使用多个键作为参数的命令中一起使用。
4、 Redis集群主从模型
为了在主节点子集出现故障或无法与大多数节点通信时保持可用性,Redis集群使用主从模型,其中每个哈希槽都有1个(主节点本身)到N个副本(N-1个额外的从节点)。
在节点A、B、C的示例集群中,如果节点B失败,集群将无法继续,因为我们不再能够提供范围为5501-11000的哈希槽。
然而创建集群时(或在稍后的时间)我们从节点添加到每一个主节点,所以最终集群由A,B, C主节点和A1, B1, C1从节点组成,如果节点B失败系统能够继续运行。
节点B1复制B, B失败,集群将提升节点B1为新主节点,并继续正常运行。
但是请注意,如果节点B和B1同时失败,则Redis集群无法继续运行。
5、 Redis集群一致性保证
Redis集群不能保证强一致性。实际上,这意味着在某些条件下,Redis集群可能会丢失系统向客户端确认的写操作。
Redis集群会丢失写操作的第一个原因是它使用异步复制。这意味着在写期间会发生以下情况:
-
客户端向主B写入。
-
主节点B向客户端回复OK。
-
主B将写操作传播到它的从服务器B1、B2和B3。
可以看到B回复给客户端之前,不等待来自B1, B2, B3的确认回复,因为这将会有延迟,所以如果你的客户写东西,B回复确认写,但崩把数据写入从节点之前崩溃了,一个从节点(没有接受到写操作)可以被选拔为新主人,但是永远失去了这个写操作。
他的方法与大多数数据库非常类似,这些数据库配置每秒将数据刷新到磁盘,因此,由于过去使用传统数据库系统而不涉及分布式系统的经验,您已经能够推断出这种情况。 类似地,您可以通过强制数据库在响应客户机之前刷新磁盘上的数据来提高一致性,但是这通常会导致非常低的性能。 对于Redis集群,这相当于同步复制。
基本上,在性能和一致性之间需要进行权衡。
Redis集群在绝对需要时支持同步写入,通过WAIT命令实现,这使得丢失写入的可能性大大降低,但是请注意,即使使用同步复制,Redis集群也没有实现强一致性: 在更复杂的故障场景下,始终有可能选择无法接收写操作的从