Redis 的主从复制配置非常容易,但我们先来了解一下它的一些特性。
- redis 使用异步复制。从 redis 2.8 开始,slave 也会周期性的告诉 master 现在的数据量。可能只是个机制,用途应该不大。
-
一个 master 可以拥有多个 slave,废话,这也是业界的标配吧。
-
slave 可以接收来自其他 slave 的连接。意思是不是就是说 slave 在接收其他的slave的连接之后成为 master ?等下我们来验证。
- redis 复制在 master 这一端是非阻塞的,也就是说在和 slave 同步数据的时候,master 仍然可以执行客户端的操作命令而不受其影响。这点都不能保证,要你干嘛?
- redis 复制在 slave 这一端也是非阻塞的。在配置文件里面有 slave-serve-stale-data 这一项,如果它为 yes ,slave 在执行同步时,它可以使用老版本的数据来处理查询请求,如果是 no ,slave 将返回一个错误。在完成同步后,slave 需要删除老数据,加载新数据,在这个阶段,slave 会阻止连接进来。
-
Replication can be used both for scalability, in order to have multiple slaves for read-only queries (for example, heavy SORT operations can be offloaded to slaves), or simply for data redundancy.这句话我也没理解什么意思。
- 使用复制可以避免 master 因为需要把全部的数据集写入磁盘而造成的开销,因此可以把 master 中 save 配置项全部注释掉,不让它进行保存,然后配置 slave ,让 slave 保存。虽然有这个特性,但是我们好像一般不这么做。
好吧,我们做几个例子练习一下。
先打开三个终端,然后起三个实例,分别用三个 client 去连接它们:
zhaoguihuadediannao:src zhaogh$ ./redis-server --port 10000 --daemonize yes
zhaoguihuadediannao:src zhaogh$
zhaoguihuadediannao:src zhaogh$ ./redis-cli -p 10000
端口10000的做 master。
slave 01:
zhaoguihuadediannao:src zhaogh$ ./redis-server --port 10001 --daemonize yes
zhaoguihuadediannao:src zhaogh$
zhaoguihuadediannao:src zhaogh$ ./redis-cli -p 10001
slave 02:
zhaoguihuadediannao:src zhaogh$ ./redis-server --port 10002 --daemonize yes
zhaoguihuadediannao:src zhaogh$
zhaoguihuadediannao:src zhaogh$ ./redis-cli -p 10002
上面只是让它们的实例启动了并用客户端去连接它,并没有设置主从关系。在 slave 01 和 slave 02 上执行下面的命令:
127.0.0.1:10001> slaveof 127.0.0.1 10000
OK
127.0.0.1:10001>
这样就设置好了主从关系。我们来试试有没有效果。
127.0.0.1:10001> get testkey001
(nil)
127.0.0.1:10001>
这个时候是没有值的。
master 上执行:
127.0.0.1:10000> set testkey001 testvalue001
OK
127.0.0.1:10000>
然后看看 slave 上有没有:
127.0.0.1:10001> get testkey001
"testvalue001"
127.0.0.1:10001>
127.0.0.1:10002> get testkey001
"testvalue001"
127.0.0.1:10002>
有了,是不是比***点读机还 easy ?已经有了感性的认识,我们来介绍一下它的原理吧。
当你设置了主从关系后,slave 在第一次连接或者重新连接 master 时,slave 都会发送一条同步指令给 master ;
master 接到指令后,开始启动后台保存进程保存数据,接着收集所有的数据修改指令。后台保存完了,master 就把这份数据发送给 slave,slave 先把数据保存到磁盘,然后把它加载到内存中,master 接着就把收集的数据修改指令一行一行的发给 slave,slave 接收到之后重新执行该指令,这样就实现了数据同步。
slave 在与 master 失去联系后,自动的重新连接。如果 master 收到了多个 slave 的同步请求,它会执行单个后台保存来为所有的 slave 服务。
一旦 master 和 slave 在失去联系并重新连接上,总是会重新进行一次完整的同步。不过从 redis 2.8 开始,只是部分重新同步也是可以的。具体请大家参考官方文档。
祝大家端午节快乐。
昨天晚上钓鱼回来,大发神经,写了篇概括程序员生活现状的文章,没想到招来众多人的口诛笔伐,大有上升到政治层面的趋势。
我也许不会再发表任何冲击心灵的文章,我希望给大家带来更多的正能量,所以那篇文章已被我删除。
我的本意只是想让各位看过文章之后能冷静地思考自己的程序人生,不管是对是错,人都有选择的权力,走好自己的路。
我没有你们想象中那么悲观,我也在不懈的努力,哪怕一时的跌倒,我也要重新站起。
生活无时无刻不是压力,让我们背起行囊,迈出踏实的一步,走起!
我们继续我们的 redis 缓存之旅。
前一篇我们实现了一个简单的 redis 集群,redis 也为了让我们方便的维护集群提供了非常好的工具。
首先请大家参照我的上一篇文章,把集群环境搭建起来。
OK,我用了分分钟的时间已经搭建好了。
[OK] All 16384 slots covered.
zhaoguihuadediannao:src zhaogh$ ./redis-cli -c -p 9001
127.0.0.1:9001> get testkey001
-> Redirected to slot [12786] located at 127.0.0.1:9003
(nil)
127.0.0.1:9003>
如何增加节点:
首先我们创建一个叫做 9004 的目录:
zhaoguihuadediannao:redis-cluster zhaogh$ mkdir 9004
zhaoguihuadediannao:redis-cluster zhaogh$
接着我们将 9001 目录下的 redis-server 、 redis.conf 两个文件拷贝到 9004 目录:
zhaoguihuadediannao:redis-cluster zhaogh$ cp 9001/redis-server 9004
zhaoguihuadediannao:redis-cluster zhaogh$ cp 9001/redis.conf 9004
然后我们打开 redis.conf 文件修改里面的端口配置项,将其改为 9004 。
启动 9004 实例:
zhaoguihuadediannao:redis-cluster zhaogh$ cd 9004
zhaoguihuadediannao:9004 zhaogh$ ./redis-server ./redis.conf
zhaoguihuadediannao:9004 zhaogh$
想要把这个实例加入到集群,我们只需要执行 redis-trib.rb 命令:
zhaoguihuadediannao:src zhaogh$ ./redis-trib.rb add-node 127.0.0.1:9004 127.0.0.1:9001
第一个参数是我们刚才启动的新实例,第二个参数是集群中已有的节点。
检查一下新节点是否已经加入:
zhaoguihuadediannao:src zhaogh$ ./redis-cli -c -p 9001
127.0.0.1:9001> cluster nodes
0e8f980bfe7a682e3d71b15523a41293535b8ccd :0 myself,master - 0 0 1 connected 0-5460
cbb01bdfdc265b190496956354d84aaae6e7d54d 127.0.0.1:9004 master - 0 1401952316346 0 connected
708e6e14474e3a99677b05ff89bd857375884437 127.0.0.1:9002 master - 0 1401952314325 2 connected 5461-10922
a7f9d3c64540cc3fc8cd3072e573bb8ab0bf1e6f 127.0.0.1:9003 master - 0 1401952315334 3 connected 10923-16383
127.0.0.1:9001>
我们可以发现 9004 并不包含任何哈希槽,因为它还没有数据。
我们还可以为集群中的主节点增加从节点用于只读查询。
如何增加从节点:
我们还是要创建目录,拷贝那两个文件,修改配置,然后启动实例:
zhaoguihuadediannao:redis-cluster zhaogh$ mkdir 9005
zhaoguihuadediannao:redis-cluster zhaogh$ cp 9001/redis-server 9005
zhaoguihuadediannao:redis-cluster zhaogh$ cp 9001/redis.conf 9005
修改 port 为 9005
zhaoguihuadediannao:redis-cluster zhaogh$ cd 9005
zhaoguihuadediannao:9005 zhaogh$ ./redis-server ./redis.conf
zhaoguihuadediannao:9005 zhaogh$
执行下面的命令,增加从节点:
zhaoguihuadediannao:src zhaogh$ ./redis-trib.rb add-node --slave 127.0.0.1:9005 127.0.0.1:9001
第一个参数为从节点,第二个参数为主节点。
如何删除一个节点:
zhaoguihuadediannao:src zhaogh$ ./redis-trib.rb del-node 127.0.0.1:9001 'cbb01bdfdc265b190496956354d84aaae6e7d54d'
这里要注意一下,第一个参数是集群中的任何一个主节点地址,而第二个参数是要删除节点的 ID,这个ID如果你不知道的话,可以通过 cluster nodes 命令查看。
还有一点就是要删除的节点必须是空的,也就是不能缓存任何数据,否则会删除不成功。对于非空节点,在删除之前需要重新分片,将缓存的数据转移到别的节点。
如何重新分片:
我们先给某个节点做点数据:
zhaoguihuadediannao:src zhaogh$ ./redis-cli -c -p 9001
127.0.0.1:9001> set testkey001 testvalue001
-> Redirected to slot [12786] located at 127.0.0.1:9003
OK
127.0.0.1:9003>
现在 9003 上已经有数据了,我们尝试删除一下:
zhaoguihuadediannao:src zhaogh$ ./redis-trib.rb del-node 127.0.0.1:9001 '78ec1fd6647b79627d7c29bb2b22d04a4a6c43b3'
>>> Removing node 78ec1fd6647b79627d7c29bb2b22d04a4a6c43b3 from cluster 127.0.0.1:9001
Connecting to node 127.0.0.1:9001: OK
Connecting to node 127.0.0.1:9002: OK
Connecting to node 127.0.0.1:9003: OK
[ERR] Node 127.0.0.1:9003 is not empty! Reshard data away and try again.
zhaoguihuadediannao:src zhaogh$
没有删除成功,我们来重新分片,把 9003 上的数据转移:
zhaoguihuadediannao:src zhaogh$ ./redis-trib.rb reshard 127.0.0.1:9003
然后输出了很多信息,很多数值和ID都可以从这段信息中找到。
How many slots do you want to move (from 1 to 16384)? 5461
会问你要移动多少个哈希槽,我们把 9003 上的所有哈希槽都移走,5461 这个数字可以从终端上看到,或许你的实际情况不是这个数字。
What is the receiving node ID? 4d2e0a8360795ce7ce8381c68746034aeba3c9b9
然后问你你要把这些哈希槽移到哪儿去,我指定了 9001 的 节点 ID。
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:78ec1fd6647b79627d7c29bb2b22d04a4a6c43b3
Source node #2:done
之后,redis 列出了重新分片计划,最后问你
Do you want to proceed with the proposed reshard plan (yes/no)? yes
执行完成后,我们看看 9003 上还有没有 key:
zhaoguihuadediannao:src zhaogh$ ./redis-cli -p 9003
127.0.0.1:9003> keys *
(empty list or set)
127.0.0.1:9003>
再看看 9001 上是不是有这个 key 了
zhaoguihuadediannao:src zhaogh$ ./redis-cli -p 9001
127.0.0.1:9001> keys *
1) "testkey001"
127.0.0.1:9001>
没错,果然转移过来了。
最后我们试试能不能把 9003 删除:
zhaoguihuadediannao:src zhaogh$ ./redis-trib.rb del-node 127.0.0.1:9001 '78ec1fd6647b79627d7c29bb2b22d04a4a6c43b3'
>>> Removing node 78ec1fd6647b79627d7c29bb2b22d04a4a6c43b3 from cluster 127.0.0.1:9001
Connecting to node 127.0.0.1:9001: OK
Connecting to node 127.0.0.1:9002: OK
Connecting to node 127.0.0.1:9003: OK
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
zhaoguihuadediannao:src zhaogh$ ./redis-cli -p 9003
Could not connect to Redis at 127.0.0.1:9003: Connection refused
not connected>
非常好,it’s very good.
上篇文章说过,目前c#客户端还不能很好的支持 redis 集群,下一篇我将介绍如何使用代理来实现 redis 集群。