Redis集群

— May 04, 2015

由于Redis在3.0之前不支持Cluster,因此关于Redis集群的配置需要根据当前使用的版本区别考虑(目前使用的是2.2.12版本)。2015年4月1日Redis 3.0正式发布,其中一个重大更新是引入了原生集群功能。

数据分区

可以通过以下几种方式实现数据分区。

  • 客户端分区。这种方式客户端直接选择一个合适的节点写入或者读取数据
  • 通过代理协助分区。客户端将请求发送到代理服务器,再由代理服务器完成请求转发功能。Twemproxy实现了这种功能,目前系统中也是使用这种方案。
  • 查询路由。客户端的请求发送到一个随机的节点,再由该节点决定是否将其转发到合适的节点。Redis cluster实现的是一种混合查询路由机制,它是通过客户端将请求重定向到合适的节点而不是通过节点内部转发实现

数据分区的缺点

没有什么事情是只有好处而没有坏处的。享受数据分区带来的便利性的同时需要承受其引入的缺陷。数据分区存在有以下诸多缺点:

  • 不支持引入多键的操作
  • 不支持引入多键的事务
  • 分区粒度是基于key级别的,因此无法对单个很大的键分片
  • 数据处理变得更加复杂
  • 增加或者减少容量可能变得很复杂,例如基于代理或者客户端分区的方案不支持数据的rebalance(可以通过预分片解决)

数据存储与Cache

如果将Redis作为数据存储使用,数据分区与理论上有些差别:通常给定的键只映射到同一个节点。而当Redis作为Cache使用时,如果给定当前节点不可用并不是个大问题,可以修改键-节点映射表提高可用性。因此:

  • 如果Redis作为Cache使用,使用一致性hash算法scale up和scale down非常容易
  • 如果Redis作为存储使用,固定的key - 节点映射导致节点数也是固定的,不能改变。否则需要引入另外一个系统来对增加节点和删除节点时引入的不平衡性做rebalance。

Redis 副本机制

Redis采用主-从备份拷贝机制,其主要特征有:

  • 异步副本拷贝,从2.8版本开始Slave节点会定期应答其已经处理的数据量
  • 一个master节点可以有多个slave节点
  • slave节点可以接受其它slave节点的连接形成图的结构
  • Redis的副本拷贝在master节点是非阻塞的,因此slave节点在做副本拷贝时不会阻塞主节点接受请求
  • Redis的副本拷贝在slave节点也是非阻塞的,通过配置slave节点可以在同步数据时给请求响应旧的数据
  • 副本拷贝可以用于水平扩展实现多个slave的只读模式
  • 主节点可以不保存数据而是将数据保存在slave节点中,但是这种应用场景必须关闭自动重启功能!!!
  • 从2.8版本开始支持部分同步功能,避免在主-从节点失联之后同步全部数据。该功能使用PSYNC命令
  • 从2.6版本开始slave节点支持只读模式,该模式默认打开
  • 从2.8版本开始支持N份写成功,只有最少N个从节点连接到主节点的情况下写才认为成功

Redis 2.X集群

由于Redis 2.X时代并不支持原生集群功能,因此Redis集群都是通过代理曲线救国的形式完成的。这种通过代理引入的集群功能需要非常谨慎的处理数据分区以及数据灾备切换。

目前系统采用Redis + Twemproxy + Sentinel的形式组建Redis集群,在Redis 3.0集群功能未经过大规模验证使用之前,不建议切换到3.0的原生集群功能。

Redis Sentinel

从Redis 2.8开始引入RedisSentinel 2用于管理和监控Redis实例,RedisSentinel 1已经被废弃。

Redis Sentinel用于管理Redis集群,主要完成下面四个工作:

  • 监控。Sentinel持续检查master和slave节点的工作状态
  • 通知。当被监控的节点出错时,通过API通知系统管理员或者其他程序
  • 自动failover。如果一个master节点失效,Sentinel会启动一个failover进程选出一个新的master节点取代失效的master节点
  • 配置发现。客户端连接到Sentinel查询master节点,如果发生了failover, Sentinel将回报一个新的master地址

分布式Sentinel

Redis Sentinel是一个分布式系统,因此通常会运行多个Sentinel进程。这些Sentinel进程使用gossip协议通信完成failover。

Twemproxy

Twemproxy是twitter开发并开源的代理服务器,支持Memcached文本协议和Redis协议。它的主要特点是:

  1. 以代理服务器的形式工作在客户端和Redis instance之间
  2. 可以自动的在配置好的redis instance之间分配数据
  3. 支持不同策略与功能的一致性hash算法

Failover方案

twemproxy对failover的支持是可以根据需要配置的:

  • 关闭node-ejection,当一个节点失效时,需要用户预先配置好HA Instance, 比如通过Redis Sentinel自动failover
  • 开启node-ejection,当一个节点失效时,数据可能进入其他节点

优点

速度快,几乎和与redis直接通信一样快, 最多损耗20%的速度

不足

不支持multiple keys命令和transaction

Redis3.0集群

Redis从3.0开始原生支持集群,其主要功能是:

  • 可以自动将数据分发到多个节点上
  • 在一部分节点失效的情况下可以继续工作

Redis集群TCP端口

每个Redis集群中的节点都需要打开两个TCP连接:

  • 用于和客户端通信的TCP端口P1,例如6379
  • 用于节点之间通信的总线数据端口P2,P2端口与P1端口采用固定的偏移量相加:P2 = P1 + 10000,例如16379

第二个端口P2用作集群总线通信管道,集群内部的节点与节点之间通过该总线使用二进制协议互相通信。集群中的节点通过总线完成失效检测、配置升级、主备切换认证等等。

Note

  • 客户端不能和总线端口互相通信。
  • 总线端口号 = 服务端口 + 10000
  • 集群中的所有节点都必须访问和总线端口
  • 如果没有同时打开这两个TCP端口,集群不会正常工作
  • 集群总线使用一种与Redis协议不同的二进制协议用作节点之间的数据交换

数据分片

Hash槽

Redis集群不是使用一致性哈希算法做数据分片,而是使用Hash Slot存储key。

Redis集群中有16384个Hash Slot(),通过计算Key所在的slot。

集群中的每个节点都代表Hash slot中的一部分,例如3节点集群的Hash slot分配如下:

  • 节点A存储0 - 5500 号Hash slot
  • 节点B存储5501 - 11000 号Hash Slot
  • 节点C存储11001 - 16384 号Hash Slot

如果增加节点或者减少节点,只需移动一些Hash Slot就可以了(问题:没有采用一致性哈希算法,出现集群增删节点时会导致大量的数据迁移)

Redis集群支持将单个命令中的多个键值存入相同的Hash Slot,用户可以使用hashtag强制将多个键存入相同的Hash slot

Hash tag

key中包含在{}中相同的子字符串则被存储在相同的Hash Slot中。

Master-Slave模型

Redis集群采用master-slave模型,为master节点中的每个hash slot存储N-1份副本到N-1个slave节点中。例如有A,B,C三个主节点,A1,B1,C1三个节点作为对应主节点的slave节点。如果节点B失效,则B1被提升为主节点,但是如果B1也失效,则整个集群不可用。也就是说即使系统中存在A,A1,C,C1四个几点,集群任然是不可用的,这也是其一个内在的缺陷吧。

数据一致性

Redis集群不能保证强一致性。 其原因是Redis集群使用异步副本拷贝完成写操作,例如下面的情况:

  • 客户端将数据写入master B节点
  • Master B节点返回OK给客户端
  • Master B节点向它的slave节点B1,B2和B3发送写操作请求

B节点并不等待其slave节点的确认就发送响应给客户端了,如果B节点在向其slave节点发送写操作之前崩溃,其中一个slave节点会被提升为主节点,但是该节点尚未接收到写数据请求,从而丢失了数据。(实际上出现丢失数据的情况很少,因为Redis几乎同时向客户端发送响应与向slave节点发送写请求)

Redis集群提供了WAIT命令实现同步写,但是即使采用同步写也不能保证强一致性,在某些复杂的情况下一个无法接收到写请求的slave节点可能会被选举为主节点。

还有一种可能会丢失写数据的情况是发生网络分区:客户端与少数节点(包括至少一个master节点)被隔离在一个网络分区中。例如:

假设一个集群由3个主节点A,B,C和3个从节点A1,B1,C1共6个节点组成,并且有一个客户端Z1。当网络分区发生之后,很可能出现在一个分区中有A,C,A1,B1,C1这5个节点,在另外一个分区中有B节点和客户端Z1。此时Z1仍然可以向B写入数据,B节点也可以接收数据。如果网络分区持续时间很短,集群还会正常工作但是如果分区持续时间较长,直到B1节点被选举为大多数节点所在分区的一个Master节点,那些从Z1写入到B节点的数据将会丢失。

注意:客户端Z1可以向节点B发送写请求的数量是有一个最大窗口值(maximum window)限制的,如果网络分区持续时间足够长导致B1节点被选举为新的Master节点,在较少节点所在分区中的所有Master节点都将停止接收写入请求。这个最大窗口是Redis Cluster中一个非常重要的配置指令,叫做node timeout。

配置Redis集群

  1. 需要一个cluster配置文件,如下:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
  1. 需要使用redis-trib.rb脚本创建集群,该脚本需要安装rubygems并安装redisruby包。这也是一个缺点,导致集群的创建和配置依赖外部脚本。

Redis在服务端支持集群功能但是目前在客户端仍然缺乏支持的库,目前已有的库:

  • redis-rb-cluster Ruby实现
  • redis-py-cluster redis-rby-cluster的Python移植版,支持大部分redis-py的功能
  • Predis PHP实现
  • thunk-redis支持Node.js

Resharding

仍然通过redis-trib.rb脚本进行reshard,在reshard过程中不影响cluster正常工作。(怀疑其性能)

手动Failover

TBD

Copyright © [email protected] 2017 all right reserved,powered by Gitbook该文件修订时间: 2017-08-03 05:55:25

results matching ""

    No results matching ""