Redis集群教程(Redis cluster tutorial)

本博文翻译自Redis官网:http://redis.io/topics/cluster-tutorial

       本文档以温和的方式介绍Redis集群,不使用复杂的方式来理解分布式系统的概念. 它介绍了如何建立、测试和使用一个集群,没有详细的覆盖Redis集群说明书 ,只是从用户的角度描述了系统的特性.
不管怎么样,本教程尝试从最终用户的角度来提供关于Redis 集群 可用性和一致性的信息。
注意:本教程需要Redis 3.0版本或者更高.
如果你计划运行一个重要的Redis 集群部署,更多的正规说明书建议阅读下,虽然不严格要求.    不管怎样,这是一个好的建议从本文档开始玩转Redis 集群一段时间,稍后阅读Redis 集群详细说明书.

一、Redis集群101

Redis集群提供一个将数据通过多个Redis节点自动分片的方式来安装Redis.
Redis集群也通过分区提供可用性程度,那就是在实践中当一些节点down掉或失去联系,仍然可以继续工作的能力. 不管怎样,大量节点出现故障时(例如当大多数的主节点不可用)Redis集群会停止工作.
所以在实践中,你从Redis集群中得到了什么?
  • 在多个节点中数据自动分片的能力.
  • 当一部分节点失败或者不能与集群其余节点通信 ,集群仍然可以工作的能力.

二、Redis集群TCP端口

每个Redis集群节点需要开通两个TCP 连接。正常的Redis TCP端口被用作服务端的端口,例如6379,服务端口加上10000得到的作为数据端口,例如16379。
第二个高端口被用于集群总线,即使用二进制协议的点对点的通信通道。  集群总线被节点用来作 失败探测、配置更新、故障转移授权认可等等。 客户端不应该尝试联系集群总线端口,但是总与正常的命令行端口连接。无论如何,确保这两个端口在你的防火墙是打开的,否则Redis集群节点不能互相联系.
命令行端口和集群总线端口的偏移量书固定的,总是10000.
注意为了让集群正确的工作,你需要为每个节点:
  1. 命令行端口对需要访问集群的客户端可达, 加上所有其余的集群节点(使用命令行端口做keys转移)。
  2. 集群总线端口(命令行端口+10000)对集群中的所有节点可达.
如果你不打开两个TCP端口,你的集群将不会预期的工作.
集群总线使用一个不同的、二进制协议来进行点对点的数据交换, 这种方式使用少量的带宽和处理时间使其更适合节点之前的数据交换.

三、Redis集群和Docker

当前的Redis集群不支持NATted (网路地址转换)环境和常规下IP地址或端口重新规划的环境。
Docker使用一个叫端口映射的技术:程序运行在Docker容器中可能暴露一个不同的端口给一认为比较信任的程序使用.  对于为了在同一端口、同一时间、同一服务运行多个服务是非常有用的。
为了使Docker和Redis集群更好的兼容,你需要使用Docker的主机网络模式(   host networking mode  ),请在 Docker文档是查看更多关于 --net=host选项的信息.

四、Redis集群数据分片

Redis集群不使用一致性Hash算法,使用另一种分片形式----- 每个key被概念性的区分,我们称之为Hash slot.
Redis集群有16384个hash slot,然后为了计算一个给定key的hash slot是什么,我们简单的采用了这个key的CRC16校验模16384得到.

每个Redis集群节点负责hash slots总数的的一个子集,举个例子,你拥有三个节点的Redis集群:

  • 节点A包含0~5500个hash slots.
  • 节点B包含5501~11000个hash slots.
  • 节点C包含11001~16383个hash slots.
这使得添加和和删除节点在Redis集群中变得容易.例如,如果我想加个节点D,我需要从节点A、B、C移动一些hash slot到D节点上. 同样的,如果我想从集群中移除节点A,我只需要把服务于A节点的hash slot移动到B和C.当节点A没有任何的hash slot时,就可以完全的删除节点A了.
因为从一个节点移动hash slots到另一个节点不需要停止操作、添加和删除节点、或者改变节点间持有hash slot的百分比,也不需要任何的服务停止.

Redis集群支持多key操作,只要所有的key在单一的命令行运行(或整个事务,或Lua脚本运行)所有的key属于同一个hash slot。 用户可以强制多key在同一个hash slot,通过使用一个叫hash tags的东西. 
Hash tags被记录在Redis 集群的规范说明书里. 但是主旨是如果有一个子串在方括号和一个key之间,只要方括号里的字符串是散列的,例如:  this{foo}key和another{foo}key
保证在同一个hash slot里,可以在同一个命令行窗口里多key操作作为参数使用。

五、Redis集群主从模型

当集群中的一部分主节点失败或不能与多数的节点通信,为了保持集群的可用行,Redis集群使用主从复制模型,每一个hash slot拥有1(主节点自身)到N(N-1额外的副节点)的复制.
在我们的例子中,节点A、B、C,如果集群中的节点B失败后,我们没有办法继续服务5501-11000范围的hash slot。
不管怎样,当一个集群被创建时(或之后的一段时间),我们为每一个主节点添加一个副节点,所以最终的集群A、B、C三个主节点和A1、B1、C1三个副节点组成,如果节点B出现故障时,集群仍然可以继续工作。
节点B1代替了节点B,B失败了,集群会提升节点B1作为新的主节点,集群将会继续正常工作.
注意:如果集群中的节点B和B1同时失败了,集群将不会继续工作.

六、Redis集群一致性保证

Redis集群不能保证较强的一致性。在实践中意味着,当前条件下,Redis 集群会丢失写的数据---系统被客户端承认.
Redis集群丢失写数据的首要原因是它使用了异步复制. 这就意味着你写数据的时候会有如下情况发生:
  • 你的客户端写数据到主节点B.
  • 主节点B回应你的客户端一切OK.
  • 主节点复制写的数据到副节点B1、B2、B3.
就你所看见的,主节点B在回应客户端之前不会等待B1、B2、B3的确认,所以这对Redis集群将是一个潜在的隐患. 所以你的客户端写了一些东西,节点B对写操作进行确认,但是节点B这时候出现故障down了,副本节点的其中一个(数据没有写入的那个节点)将会被提升为主节点,永远的丢失刚写入的数据.
这跟大多数数据库配置每秒刷新数据到磁盘很相似。所以设想下你已经知道原因,通过使用传统数据库的过去经验,没有涉及分布式系统。同样的,你可以强制数据库刷新数据到磁盘在回应客户端之前来提供数据的一致性,但是通常的结果是降低系统的性能。 这个例子等价于Redis集群的异步复制.
基本上,需要把性能和一直性权衡考虑.
Redis集群支持同步写当绝对需要的时候,通过 WAIT命令实现, 这使得写数据丢失率少一点。不管怎样,值得注意的是Redis集群不实现强一致性,即使同步复制操作被使用 :更复杂的失败场景总是可能的,当一个副节点没有接收到数据之前已经被选为主节点了.
这里有一个值得注意的Redis将会丢失数据的场景 ,发生在网路分区时,一个客户端和一些实例连接,连接的实例中至少包含一个主节点.
就拿我们由A,B,C,A1,B1,C1 6个节点组成的集群作为例子,3主点,3个副节点。还有一个客户端,我们称它为Z1.
分区出现后,可能的情况是,在分区的一面有A,C,A1,B1,C1,在另一面有B和Z1。
Z1仍然可以写数据到B,B也可以接收到Z1写的数据。如果分区在较短时间内恢复完成,集群可以正常的工作.然而,如果分区持续足够时间让大分区的B1节点成为主节点,Z1写到B的数据将会丢失.
有一点注意的是Z1写数据到B的数量有一个最大值窗口:如果足够时间的流失,使得拥有较多节点的那一个分区中的一个副节点被选举为主节点,拥有较少节点分区里的主节点将停止接收数据的写入。
节点超时时间在Redis集群中是一个非常重要的配置指令. 节点超时时间过去之后,一个主节点被认为失败,可能会被它副节点其中的一个代替.同样的,节点超时时间过去之后,没有一个主节点可以感知到其它主节点的大多数,它将进入一个错误的状态、停止数据的写入.

七、Redis集群配置参数

我们打算创建一个集群部署的样例。在继续之前,让我们先介绍下Redis 集群配置文件redis.conf 的一些配置参数. 一些参数是很显然的,另一些参数在你继续阅读之后将变的更清晰。

  • cluster-enabled <yes/no>:如果是设置为yes,Redis集群将支持一个指定的Redis实例;否则这个实例将作为一个普通单独的实例。
  • cluster-config-file <filename>注意不要管这个选项的名称,这不是一个用户编辑的配置文件,而是Redis集群自动存留的Redis配置(状态,基本的)每当集群发生变化时,为了在启动时再次读取. 这个文件列出了其他节点在集群中的状态、持久性变化等等。这个文件常常在磁盘上重写、刷新作为一些消息接收的结果.
  • cluster-node-timeout <milliseconds>:Redis集群中的点不用,被认为失败节点的的最大等待时间。如果一个主节点在指定的时间段内不可达,就会被他的副节点做失败处理. 这个参数控制Redis集群中其它重要事情。尤其,每个节点不能在指定时间内与主节点中的大多数通信,该节点将会停止查询。
  • cluster-slave-validity-factor <factor>:如果设置成0,一个副节点总是尝试FailOver主节点,不管主节点和副节点保持断开连接多长时间。如果这个值设置为正数,一个最大失联时间将会被计算出来,node-timeout 乘以这个选项配置的factor, 如果这个节点是副节点,它将不会启动Failover当失联超过计算出的指定时间。 例如,如果node-timeoutt设置为5秒,validity factor 设置为10,一个副节点与主节点失去联系超过50秒后,副节点将不会尝试Failover它的主节点。注意,任何不同于0的值将导致集群不可用,当一个主节点失败后, 没有副节点可以替换它。在那个例子中,集群将返回可用,只要原始的主节点重新加入到集群中.
  • cluster-migration-barrier <count>:  一个主节点最少副节点连接数目,另一个移动到一个不被任何副节点覆盖的主节点。阅读本教程更多关于对于副本迁移恰当分区的信息.
  • cluster-require-full-coverage <yes/no>:如果设置为yes,默认的,如果一定比例的key空间不被任何节点覆盖,集群将停止消息的写入;如果设置为no,集群仍然支持查询服务即使只有一部分key处理请求.

八、创建和使用一个Redis集群

注意:手工部署一个Redis集群,学习集群的特定操作是非常重要的。不管怎样,如果你想快速的运行一个集群,跳过这一部分 ,你可以阅读下一部分 创建一个Redis集群使用create-cluster脚本。
创建一个集群,首先我们要有一些以集群模式运行的空的Redis实例。这基本意味着集群不是用正常的Redis实例创建的,但是一个特殊的模式需要被配置将使Redis实例拥有集群指定的属性和命令。
如下是一个集群配置文件最少的配置项:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
就你所知,使集群模式工作只需要一个简单的cluster-enabled指令。每个实例也包含该节点配置信息被存储的文件的路径,默认是nodes.conf。 该配置文件不会被手动更改,它仅仅在Redis 集群实例启动的时候生成,每次需要的时候更新这个文件。
注意期望最小集群工作,要求至少三个主节点。对于你第一次测试,强烈建议你开启一个6个节点的集群,包含3个主节点和3个副节点。
就这样做,进入一个新的目录,创建如下以端口号命令的目录:
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
在7000到7005每个目录下面,创建一个Redis.conf文件。你配置文件的模板只要使用上面小例子的内容就可以了,但是要确保替换port 7000 右边的port  数字和你目录名字保持一致.
现在把在GitHub上不稳定分支编译好的可以执行redis-server 源文件复制到 cluster-test目录下面,最后打开6个终端标签在你最喜欢的终端应用上.
向这样开始每个实例,在每个终端标签上:
cd 7000
../redis-server ./redis.conf
你可以看见每个实例的日志,由于 nodes.conf文件不存在,每个节点分配一个新的ID.
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
这个ID将被指定的Redis 实例永远使用,为了让这个实例在Redis上下文中有一个唯一的名字. 每一个节点都是通过ID来记录其余的节点,而不是通过IP地址或端口。 IP地址和端口可以改变,但是这个节点唯一的节点标识在节点生命周期内永远不会改变。我们把这个标识简单的称之为节点ID.
1、创建集群
现在我们有一个实例在运行,我们需要写一些对节点有意义的配置来创建集群。 这个很容易完成通过Redis集群命令行工具---redis-trib,一个Ruby程序执行在实例上执行特殊的命令来创建Redis集群、检查或分片一个已存在的集群等等。
redis-trib工具分配在Redis源代码目录下的src目录下。你需要安装  redis gem 使运行redis-trib成为可能。
gem install redis
创建集群的简单类型
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
这里使用的命令是creat,因为我们想创建一个新的Redis集群。   --replicas 1 这个选项的意思是我们想要为每个master节点创建一个slave节点,其余参数是我想创建集群实例要使用的地址列表。
显然,集群是按照我们的要求建立的,3个master节点、3个slave节点。
Redis-trib将会提出一个配置,输入yes,集群将会被配置和加入,这意味着,Redis实例将会被引导至互相交互。最后,你将看到一个信息:
[OK] All 16384 slots covered
上面这条信息意味着,至少有一个master节点实例服务于16384 slot 中的每一个。

2、使用create-cluster脚本创建Redis集群

如果你不想通过手工的配置和执行单独的实例来创建Redis集群,就像上面解释的那样。这里有一个更简单的方法(但是你不必学习相同数量的操作细节)。
只要查看下Redis分布的utils/create-cluster 目录下,这里有一个叫做create-cluster的脚本(和包含这个脚本的目录名称相同),它是一个简单的bash脚本。为了开始6个节点的集群(3个master节点,3个slave节点) ,只要输入如下命令:
1、create-cluster start
2、create-cluster create
第二步的时候,当redis-trib工具想让你接受redis集群布局时,请输入yes。
现在你能够和Redis集群相互作用,第一个节点默认在30001端口启动。当你完成,使用下面命令停止Redis集群:
create-cluster stop.
请阅读目录下面的README 文件查看更多关于运行该脚本的信息.

3、玩转Redis集群(Playing with the cluster)

在这个阶段,Redis集群的一个问题是缺少客户端库的实现。
我知道如下的实现:
redis-rb-cluster is a Ruby implementation written by me (@antirez) as a reference for other languages. It is a simple wrapper around the original redis-rb, implementing the minimal semantics to talk with the cluster efficiently.
redis-py-cluster A port of redis-rb-cluster to Python. Supports majority of redis-py functionality. Is in active development.
The popular Predis has support for Redis Cluster, the support was recently updated and is in active development.
The most used Java client, Jedis recently added support for Redis Cluster, see the Jedis Cluster section in the project README.
StackExchange.Redis offers support for C# (and should work fine with most .NET languages; VB, F#, etc)
thunk-redis offers support for Node.js and io.js, it is a thunk/promise-based redis client with pipelining and cluster.
redis-go-cluster is an implementation of Redis Cluster for the Go language using the Redigo library client as the base client. Implements MGET/MSET via result aggregation.
The redis-cli utility in the unstable branch of the Redis repository at GitHub implements a very basic cluster support when started with the -c switch.
一个简单的方式来测试你的集群是使用上面任何一种客户端或者简单的命令行工具redis-cli。
如下是一个使用redis-cli方式交互的例子:
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"










***************未完待续******************

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值