一、Redis集群
Redis的集群实现是内置数据自动分片机制,集群内部将所有的key映射到16384个Slot中,集群中的每个Redis Instance负责其中的一部分的Slot的读写。集群客户端连接集群中任一Redis Instance即可发送命令,当Redis Instance收到自己不负责的Slot的请求时,会将负责请求Key所在Slot的Redis Instance地址返回给客户端,客户端收到后自动将原请求重新发往这个地址,对外部透明。一个Key到底属于哪个Slot由crc16(key) % 16384 决定。
注意:集群必须要3个以上的的主节点,否则在创建集群时会失败,我们在后续会实践到。
所以,我们假设现在有3个节点已经组成了集群,分别是:A, B, C 三个节点,它们可以是一台机器上的三个端口,也可以是三台不同的服务器。那么,采用哈希槽 (hash slot)
的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:
- 节点A覆盖0-5460;
- 节点B覆盖5461-10922;
- 节点C覆盖10923-16383.
二、Redis集群的主从模型
为了当部分节点失效时,或者无法与大多数节点通信时仍能保持可用,Redis集群采用每个节点拥有1(主服务自身)到N个副本(N-1个附加的从服务器)的主从模型。
在我们的例子中,集群拥有A,B,C三个节点,如果节点B失效集群将不能继续服务,因为我们不再有办法来服务在5501-11000范围内的哈希槽。
但是,如果当我们创建集群后(或者稍后),我们为每一个主服务器添加一个从服务器,这样最终的集群就由主服务器A,B,C和从服务器A1,B1,C1组成,如果B节点失效系统仍能继续服务。
B1节点复制B节点,于是集群会选举B1节点作为新的主服务器,并继续正确的运转。
三、Redis集群的一致性保证
Redis集群不保证强一致性。实践中,这意味着在特定的条件下,Redis集群可能会丢掉一些被系统收到的写入请求命令。
Redis集群为什么会丢失写请求的第一个原因,是因为采用了异步复制。这意味着在写期间下面的事情发生了:
- 你的客户端向主服务器B写入。
- 主服务器B回复OK给你的客户端。
- 主服务器B传播写入操作到其从服务器B1,B2和B3。
你可以看到,B在回复客户端之前没有等待从B1,B2,B3的确认,因为这是一个过高的延迟代价,所以如果你的客户端写入什么东西,B确认了这个写操作,但是在发送写操作到其从服务器前崩溃了,其中一个从服务器被提升为主服务器,永久性的丢失了这个写操作。
这非常类似于在大多数被配置为每秒刷新数据到磁盘的数据库发生的事情一样,这是一个可以根据以往不包括分布式系统的传统数据库系统的经验来推理的场景。同样的,你可以通过在回复客户端之前强制数据库刷新数据到磁盘来改进一致性,但这通常会极大的降低性能。
基本上,有一个性能和一致性之间的权衡。
注意:未来,Redis集群在必要时可能或允许用户执行同步写操作。
Redis集群丢失写操作还有另一个场景,发生在网络分割时,客户端与至少包含一个主服务器的少数实例被孤立起来了。
举个例子,我们的集群由A,B,C,A1,B1,C1共6个节点组成,3个主服务器,3个从服务器。还有一个客户端,我们称为Z1。
分割发生以后,有可能分割的一侧是A,C,A1,B1,C1,分割的另一侧是B和Z1。Z1仍然可以写入到可接受写请求的B。如果分割在很短的时间内恢复,集群会正常的继续。但是,如果分割持续了足够的时间,B1在分割的大多数这一侧被提升为主服务器,Z1发送给B的写请求会丢失。
注意,Z1发送给B的写操作数量有一个最大窗口:如果分割的大多数侧选举一个从服务器为主服务器后过了足够多的时间,少数侧的每一个主服务器节点将停止接受写请求。
这个时间量是Redis集群一个非常重要的配置指令,称为节点超时(node timeout)。
节点超时时间过后,主服务器节点被认为失效,可以用其一个副本来取代。同样地,节点超时时间过后,主服务器节点还不能感知其它主服务器节点的大多数,则进入错误状态,并停止接受写请求。