Redis 集群教程

Redis 集群教程
本文档是Redis集群的通常介绍,没有涉及复杂难懂的分布式概念的赘述,只是提供了从用户角度来如何搭建测试以及使用的方法,若是你打算使用并深刻了解Redis集群,推荐阅读完本章节后,仔细阅读 Redis 集群规范 一章。
本教程试图提供最终用户一个简单的关于集群和一致性特征的描述
请注意,本教程使用于Redis3.0(包括3.0)以上版本
若是你计划部署集群,那么咱们建议你从阅读这个文档开始。
Redis集群介绍
Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
Redis集群并不支持处理多个keys的命令,由于这须要在不一样的节点间移动数据,从而达不到像Redis那样的性能,在高负载的状况下可能会致使不可预料的错误.
Redis 集群经过分区来提供必定程度的可用性,在实际环境中当某个节点宕机或者不可达的状况下继续处理命令. Redis 集群的优点:
自动分割数据到不一样的节点上。
整个集群的部分节点失败或者不可达的状况下可以继续处理命令。
Redis 集群的数据分片
Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.
Redis 集群有16384个哈希槽,每一个key经过CRC16校验后对16384取模来决定放置哪一个槽.集群的每一个节点负责一部分hash槽,举个例子,好比当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽.
节点 B 包含5501 到 11000 号哈希槽.
节点 C 包含11001 到 16384号哈希槽.
这种结构很容易添加或者删除节点. 好比若是我想新添加个节点D, 我须要从节点 A, B, C中得部分槽到D上. 若是我想移除节点A,须要将A中的槽移到B和C节点上,而后将没有任何槽的A节点从集群中移除便可. 因为从一个节点将哈希槽移动到另外一个节点并不会中止服务,因此不管添加删除或者改变某个节点的哈希槽的数量都不会形成集群不可用的状态.
Redis 集群的主从复制模型
为了使在部分节点失败或者大部分节点没法通讯的状况下集群仍然可用,因此集群使用了主从复制模型,每一个节点都会有N-1个复制品.
在咱们例子中具备A,B,C三个节点的集群,在没有复制模型的状况下,若是节点B失败了,那么整个集群就会觉得缺乏5501-11000这个范围的槽而不可用.
然而若是在集群建立的时候(或者过一段时间)咱们为每一个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会由于槽找不到而不可用了
不过当B和B1 都失败后,集群是不可用的.
Redis 一致性保证
Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操做.
第一个缘由是由于集群是用了异步复制. 写操做过程:
客户端向主节点B写入一条命令.
主节点B向客户端回复命令状态.
主节点将写操做复制给他得从节点 B1, B2 和 B3.
主节点对命令的复制工做发生在返回命令回复以后, 由于若是每次处理命令请求都须要等待复制操做完成的话, 那么主节点处理命令请求的速度将极大地下降 —— 咱们必须在性能和一致性之间作出权衡。 注意:Redis 集群可能会在未来提供同步写的方法。 Redis 集群另一种可能会丢失命令的状况是集群出现了网络分区, 而且一个客户端与至少包括一个主节点在内的少数实例被孤立。
举个例子 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点, 其中 A 、B 、C 为主节点, A1 、B1 、C1 为A,B,C的从节点, 还有一个客户端 Z1 假设集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A 、C 、A1 、B1 和 C1 ,小部分的一方则包含节点 B 和客户端 Z1 .
Z1仍然可以向主节点B中写入, 若是网络分区发生时间较短,那么集群将会继续正常运做,若是分区的时间足够让大部分的一方将B1选举为新的master,那么Z1写入B中得数据便丢失了.
注意, 在网络分裂出现期间, 客户端 Z1 能够向主节点 B 发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项:
搭建并使用Redis集群
搭建集群的第一件事情咱们须要一些运行在 集群模式的Redis实例. 这意味这集群并非由一些普通的Redis实例组成的,集群模式须要经过配置启用,开启集群模式后的Redis实例即可以使用集群特有的命令和特性了.
下面是一个最少选项的集群的配置文件:
[AppleScript] 纯文本查看 复制代码
?
1
2
3
4
5
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
文件中的 cluster-enabled 选项用于开实例的集群模式, 而 cluster-conf-file 选项则设定了保存节点配置文件的路径, 默认值为 nodes.conf.节点配置文件无须人为修改, 它由 Redis 集群在启动时建立, 并在有须要时自动进行更新。
要让集群正常运做至少须要三个主节点,不过在刚开始试用集群功能时, 强烈建议使用六个节点: 其中三个为主节点, 而其他三个则是各个主节点的从节点。
首先, 让咱们进入一个新目录, 并建立六个以端口号为名字的子目录, 稍后咱们在将每一个目录中运行一个 Redis 实例: 命令以下:
[AppleScript] 纯文本查看 复制代码
?
1
2
3
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
在文件夹 7000 至 7005 中, 各建立一个 redis.conf 文件, 文件的内容可使用上面的示例配置文件, 但记得将配置中的端口号从 7000 改成与文件夹名字相同的号码。
从 Redis Github 页面 的 unstable 分支中取出最新的 Redis 源码, 编译出可执行文件 redis-server , 并将文件复制到 cluster-test 文件夹, 而后使用相似如下命令, 在每一个标签页中打开一个实例:
[AppleScript] 纯文本查看 复制代码
?
1
2
cd 7000
../redis-server ./redis.conf
实例打印的日志显示, 由于 nodes.conf 文件不存在, 因此每一个节点都为它自身指定了一个新的 ID :
[AppleScript] 纯文本查看 复制代码
?
1
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
实例会一直使用同一个 ID , 从而在集群中保持一个独一无二(unique)的名字。
搭建集群
如今咱们已经有了六个正在运行中的 Redis 实例, 接下来咱们须要使用这些实例来建立集群, 并为每一个节点编写配置文件。
经过使用 Redis 集群命令行工具 redis-trib , 编写节点配置文件的工做能够很是容易地完成: redis-trib 位于 Redis 源码的 src 文件夹中, 它是一个 Ruby 程序, 这个程序经过向实例发送特殊命令来完成建立新集群, 检查集群, 或者对集群进行从新分片(reshared)等工做。
[AppleScript] 纯文本查看 复制代码
?
1
2
./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:7005java

这个命令在这里用于建立一个新的集群, 选项–replicas 1 表示咱们但愿为集群中的每一个主节点建立一个从节点。
以后跟着的其余参数则是这个集群实例的地址列表,3个master3个slave redis-trib 会打印出一份预想中的配置给你看, 若是你以为没问题的话, 就能够输入 yes , redis-trib 就会将这份配置应用到集群当中,让各个节点开始互相通信,最后能够获得以下信息:
[AppleScript] 纯文本查看 复制代码
?
1
[OK] All 16384 slots coverednode

这表示集群中的 16384 个槽都有至少一个主节点在处理, 集群运做正常。
Creating a Redis Cluster using the create-cluster script
If you don’t want to create a Redis Cluster by configuring and executing individual instances manually as explained above, there is a much simpler system (but you’ll not learn the same amount of operational details).
Just check utils/create-cluster directory in the Redis distribution. There is a script called create-cluster inside (same name as the directory it is contained into), it’s a simple bash script. In order to start a 6 nodes cluster with 3 masters and 3 slaves just type the following commands:
create-cluster start
create-cluster create
Reply to yes in step 2 when the redis-trib utility wants you to accept the cluster layout.
You can now interact with the cluster, the first node will start at port 30001 by default. When you are done, stop the cluster with:
create-cluster stop.
Please read the README inside this directory for more information on how to run the script.
使用集群
Redis 集群现阶段的一个问题是客户端实现不多。
如下是一些我知道的实现:
redis-rb-cluster 是我(@antirez)编写的 Ruby 实现, 用于做为其余实现的参考。 该实现是对 redis-rb 的一个简单包装, 高效地实现了与集群进行通信所需的最少语义(semantic).
redis-py-cluster 看上去是 redis-rb-cluster 的一个 Python 版本, 这个项目有一段时间没有更新了(最后一次提交是在六个月以前), 不过能够将这个项目用做学习集群的起点。
流行的 Predis 曾经对早期的 Redis 集群有过必定的支持, 但我不肯定它对集群的支持是否完整, 也不清楚它是否和最新版本的 Redis 集群兼容 (由于新版的 Redis 集群将槽的数量从 4k 改成 16k 了).
使用最多的时java客户端, Jedis 最近添加了对集群的支持, 详细请查看项目README中Jedis Cluster部分.
StackExchange.Redis 提供对 C# 的支持(而且包括大部分 .NET 下面的语言,好比: VB, F#等等)
thunk-redis 提供对 Node.js 和 io.js的支持。
Redis unstable 分支中的 redis-cli 程序实现了很是基本的集群支持, 可使用命令 redis-cli -c 来启动。
测试 Redis 集群比较简单的办法就是使用 redis-rb-cluster 或者 redis-cli , 接下来咱们将使用 redis-cli 为例来进行演示:
[AppleScript] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
$ 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"redis

注意: 若是你是使用脚本建立的集群节点,那么默认端口多是从30001开始。
redis-cli 对集群的支持是很是基本的, 因此它老是依靠 Redis 集群节点来将它转向(redirect)至正确的节点。一个真正的(serious)集群客户端应该作得比这更好: 它应该用缓存记录起哈希槽与节点地址之间的映射(map), 从而直接将命令发送到正确的节点上面。这种映射只会在集群的配置出现某些修改时变化, 好比说, 在一次故障转移(failover)以后, 或者系统管理员经过添加节点或移除节点来修改了集群的布局(layout)以后, 诸如此类。
使用redis-rb-cluster写一个例子
在展现如何使用集群进行故障转移、从新分片等操做以前, 咱们须要建立一个示例应用, 了解一些与 Redis 集群客户端进行交互的基本方法。
在运行示例应用的过程当中, 咱们会尝试让节点进入失效状态, 又或者开始一次从新分片, 以此来观察 Redis 集群在真实世界运行时的表现, 而且为了让这个示例尽量地有用, 咱们会让这个应用向集群进行写操做。
本节将经过两个示例应用来展现 redis-rb-cluster 的基本用法, 如下是本节的第一个示例应用, 它是一个名为 example.rb 的文件, 包含在redis-rb-cluster 项目里面
[AppleScript] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
require './cluster'数据库

startup_nodes = [缓存

{:host => "127.0.0.1", :port => 7000},
 {:host => "127.0.0.1", :port => 7001}

]
rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)ruby

last = falsebash

while not last网络

begin
    last = rc.get("__last__")
    last = 0 if !last
rescue => e
    puts "error #{e.to_s}"
    sleep 1
end

endapp

((last.to_i+1)..1000000000).each{|x|异步

begin
    rc.set("foo#{x}",x)
    puts rc.get("foo#{x}")
    rc.set("__last__",x)
rescue => e
    puts "error #{e.to_s}"
end
sleep 0.1

}

这个应用所作的工做很是简单: 它不断地以 foo<number> 为键, number 为值, 使用 SET 命令向数据库设置键值对:
[AppleScript] 纯文本查看 复制代码
?
1
2
3
4
SET foo0 0
SET foo1 1
SET foo2 2
And so forth…

代码中的每一个集群操做都使用一个 begin 和 rescue 代码块(block)包裹着, 由于咱们但愿在代码出错时, 将错误打印到终端上面, 而不但愿应用由于异常(exception)而退出。
代码的第七行是代码中第一个有趣的地方, 它建立了一个 Redis 集群对象, 其中建立对象所使用的参数及其意义以下:第一个参数是记录了启动节点的 startup_nodes 列表, 列表中包含了两个集群节点的地址。第二个参数指定了对于集群中的各个不一样的节点, Redis 集群对象能够得到的最大链接数 ,第三个参数 timeout 指定了一个命令在执行多久以后, 才会被看做是执行失败。
启动列表中并不须要包含全部集群节点的地址, 但这些地址中至少要有一个是有效的: 一旦 redis-rb-cluster 成功链接上集群中的某个节点时, 集群节点列表就会被自动更新, 任何真正的的集群客户端都应该这样作。
如今, 程序建立的 Redis 集群对象实例被保存到 rc 变量里面, 咱们能够将这个对象看成普通 Redis 对象实例来使用。
在十一至十九行, 咱们先尝试阅读计数器中的值, 若是计数器不存在的话, 咱们才将计数器初始化为 0 : 经过将计数值保存到 Redis 的计数器里面, 咱们能够在示例重启以后, 仍然继续以前的执行过程, 而没必要每次重启以后都从 foo0 开始从新设置键值对。为了让程序在集群下线的状况下, 仍然不断地尝试读取计数器的值, 咱们将读取操做包含在了一个 while 循环里面, 通常的应用程序并不须要如此当心。
二十一至三十行是程序的主循环, 这个循环负责设置键值对, 并在设置出错时打印错误信息。程序在主循环的末尾添加了一个 sleep 调用, 让写操做的执行速度变慢, 帮助执行示例的人更容易看清程序的输出。执行 example.rb 程序将产生如下输出:
[AppleScript] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
ruby ./example.rb
1
2
3
4
5
6
7
8
9
^C (I stopped the program here)

这个程序并非十分有趣, 稍后咱们就会看到一个更有趣的集群应用示例, 不过在此以前, 让咱们先使用这个示例来演示集群的从新分片操做。
集群从新分片
如今, 让咱们来试试对集群进行从新分片操做。在执行从新分片的过程当中, 请让你的 example.rb 程序处于运行状态, 这样你就会看到, 从新分片并不会对正在运行的集群程序产生任何影响, 你也能够考虑将 example.rb 中的 sleep 调用删掉, 从而让从新分片操做在近乎真实的写负载下执行 从新分片操做基本上就是将某些节点上的哈希槽移动到另一些节点上面, 和建立集群同样, 从新分片也可使用 redis-trib 程序来执行 执行如下命令能够开始一次从新分片操做:
[AppleScript] 纯文本查看 复制代码
?
1
./redis-trib.rb reshard 127.0.0.1:7000

你只须要指定集群中其中一个节点的地址, redis-trib 就会自动找到集群中的其余节点。
目前 redis-trib 只能在管理员的协助下完成从新分片的工做, 要让 redis-trib 自动将哈希槽从一个节点移动到另外一个节点, 目前来讲还作不到
你想移动多少个槽( 从1 到 16384)?
咱们尝试从将100个槽从新分片, 若是 example.rb 程序一直运行着的话, 如今 1000 个槽里面应该有很多键了。
除了移动的哈希槽数量以外, redis-trib 还须要知道从新分片的目标, 也便是, 负责接收这 1000 个哈希槽的节点。
[AppleScript] 纯文本查看 复制代码
?
1
2
$ redis-cli -p 7000 cluster nodes | grep myself
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460

个人目标节点是 97a3a64667477371c4479320d683e4c8db5858b1.如今须要指定从哪些节点来移动keys到目标节点 我输入的是all ,这样就会从其余每一个master上取一些哈希槽。最后确认后你将会看到每一个redis-trib移动的槽的信息,每一个key的移动的信息也会打印出来 在从新分片的过程当中,你的例子程序是不会受到影响的,你能够中止或者从新启动屡次。在从新分片结束后你能够经过以下命令检查集群状态:更多技术资讯可关注:gzitcast

相关文章
相关标签/搜索