前言redis 是我们目前大规模使用的缓存中间件,由于它强大高效而又便捷的功能,得到了广泛的使用。现在的2.x的稳定版本是2.8.19,也是我们项目中普遍用到的版本。 redis在年初发布了3.0.0,官方支持了redis cluster,也就是集群。至此结束了redis 没有官方集群的时代,之前我们用redis cluster用的最多的应该是twitter 发布的 还有就是豌豆荚开发的 这2个我会在后续去使用它们。下面的文字,我尽量用通熟易懂的方式来阐述。 redis cluster 理论知识截止写这篇文章前,redis 3.x的最新版本是3.0.5。今天的学习和实践就是用这个版本。 Redis Cluster设计要点redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。 那么redis 是如何合理分配这些节点和数据的呢? Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做 注意的是:必须要 所以,我们假设现在有3个节点已经组成了集群,分别是:A, B, C 三个节点,它们可以是一台机器上的三个端口,也可以是三台不同的服务器。那么,采用
如下图所示: 那么,现在我想设置一个key ,比如叫
按照redis cluster的哈希槽算法: 同样,当我连接(A,B,C)任何一个节点想获取 这种
同样删除一个节点也是类似,移动完成后就可以删除这个节点了。 所以redis cluster 就是这样的一个形状: Redis Cluster主从模式redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。 上面那个例子里, 集群有ABC三个主节点, 如果这3个节点都没有加入从节点,如果B挂掉了,我们就无法访问整个集群了。A和C的slot也无法访问。 所以我们在集群建立的时候,一定要为每个主节点都添加了从节点, 比如像这样, 集群包含主节点A、B、C, 以及从节点A1、B1、C1, 那么即使B挂掉系统也可以继续正确工作。 B1节点替代了B节点,所以Redis集群将会选择B1节点作为新的主节点,集群将会继续正确地提供服务。 当B重新开启后,它就会变成B1的从节点。 不过需要注意,如果节点B和B1同时挂了,Redis集群就无法继续正确地提供服务了。 流程下图所示: redis cluster 动手实践网上有很多教程,我最先是按照这个教程(http://blog./nosql/1725.html) 一步步的按照这个教程来,可是在最后启动集群的时候第一台机器的6379端口死活启动不了,这样就没有3台主服务器,就完成不了集群。最后也没找到解决办法。[知道原因了:我把redis-trib.rb create --replicas 1的 这个1没有写!!!!] 现在,还是按照官方的教程,全程再演示一次,官方教程是在一台机器上启动6个节点,3个当主,3个当从(http:///topics/cluster-tutorial): 先下载官方的redis 版本(3.0.5) : http://download./releases/redis-3.0.5.tar.gz 下载不了,请自行FQ。我这次是在centos6.5上演示,用的是root 账户。 如果之前已经下载了redis的 2.x版本,只需要将 1.解压[root@web3 ~]# tar zxvf redis-3.0.5.tar.gz 2.安装[root@web3 ~]# cd redis-3.0.5 [root@web3 ~]# make && make install 3.将redis-trib.rb 复制到/usr/local/bin[root@web3 redis-3.0.5]# cd src/ [root@web3 src]# cp redis-trib.rb /usr/local/bin 4.开始集群搭建[root@web3 redis-3.0.5]# vi redis.conf #修改以下地方 port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
新建6个节点:
[root@web3 redis-3.0.5]# mkdir -p /usr/local/cluster-test [root@web3 redis-3.0.5]# cd /usr/local/cluster-test/ [root@web3 cluster-test]# mkdir 7000 [root@web3 cluster-test]# mkdir 7001 [root@web3 cluster-test]# mkdir 7002 [root@web3 cluster-test]# mkdir 7003 [root@web3 cluster-test]# mkdir 7004 [root@web3 cluster-test]# mkdir 7005 将redis.conf 分别拷贝到这6个文件夹中,并修改成对应的端口号 #拷贝配置文件 [root@web3 cluster-test]# cp /root/redis-3.0.5/redis.conf /usr/local/cluster-test/7000 [root@web3 cluster-test]# cp /root/redis-3.0.5/redis.conf /usr/local/cluster-test/7001 [root@web3 cluster-test]# cp /root/redis-3.0.5/redis.conf /usr/local/cluster-test/7002 [root@web3 cluster-test]# cp /root/redis-3.0.5/redis.conf /usr/local/cluster-test/7003 [root@web3 cluster-test]# cp /root/redis-3.0.5/redis.conf /usr/local/cluster-test/7004 [root@web3 cluster-test]# cp /root/redis-3.0.5/redis.conf /usr/local/cluster-test/7005 #修改端口号 root@web3 cluster-test]# sed -i "s/7000/7001/g" /usr/local/cluster-test/7001/redis.conf [root@web3 cluster-test]# sed -i "s/7000/7002/g" /usr/local/cluster-test/7002/redis.conf [root@web3 cluster-test]# sed -i "s/7000/7003/g" /usr/local/cluster-test/7003/redis.conf [root@web3 cluster-test]# sed -i "s/7000/7004/g" /usr/local/cluster-test/7004/redis.conf [root@web3 cluster-test]# sed -i "s/7000/7005/g" /usr/local/cluster-test/7005/redis.conf 分别,启动这6个节点 [root@web3 cluster-test]# cd /usr/local/cluster-test/7000/ [root@web3 7000]# redis-server redis.conf [root@web3 7000]# cd ../7001 [root@web3 7001]# redis-server redis.conf [root@web3 7001]# cd ../7002 [root@web3 7002]# redis-server redis.conf [root@web3 7002]# cd ../7003 [root@web3 7003]# redis-server redis.conf [root@web3 7003]# cd ../7004 [root@web3 7004]# redis-server redis.conf [root@web3 7004]# cd ../7005 [root@web3 7005]# redis-server redis.conf [root@web3 7005]# 查看6个节点的启动进程情况: [root@web3 7005]# ps -ef|grep redis root 11380 1 0 07:37 ? 00:00:00 redis-server *:7000 [cluster] root 11384 1 0 07:37 ? 00:00:00 redis-server *:7001 [cluster] root 11388 1 0 07:37 ? 00:00:00 redis-server *:7002 [cluster] root 11392 1 0 07:37 ? 00:00:00 redis-server *:7003 [cluster] root 11396 1 0 07:37 ? 00:00:00 redis-server *:7004 [cluster] root 11400 1 0 07:37 ? 00:00:00 redis-server *:7005 [cluster] root 11404 8259 0 07:38 pts/0 00:00:00 grep redis 将6个节点连在一起构招成集群 需要用到的命令就是 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 因为我们要新建集群, 所以这里使用create命令. 由于我机子上没安装ruby,所以,会报错: /usr/bin/env: ruby: No such file or directory 那先安装ruby和rubygems: [root@web3 7005]# yum install ruby ruby-devel rubygems rpm-build 然后,再执行一次,发现还是报错: [root@web3 7005]# 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 /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- redis (LoadError) from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require' from /usr/local/bin/redis-trib.rb:25 [root@web3 7005]# 原来是ruby和redis的连接没安装好:
[root@web3 7005]# gem install redis Successfully installed redis-3.2.1 1 gem installed Installing ri documentation for redis-3.2.1... Installing RDoc documentation for redis-3.2.1... 好了。最重要的时刻来临了,胜败成举在此了,啊啊啊啊啊啊,好激动: [root@web3 7005]# 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 >>> Creating cluster Connecting to node 127.0.0.1:7000: OK Connecting to node 127.0.0.1:7001: OK Connecting to node 127.0.0.1:7002: OK Connecting to node 127.0.0.1:7003: OK Connecting to node 127.0.0.1:7004: OK Connecting to node 127.0.0.1:7005: OK >>> Performing hash slots allocation on 6 nodes... Using 3 masters: 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 Adding replica 127.0.0.1:7003 to 127.0.0.1:7000 Adding replica 127.0.0.1:7004 to 127.0.0.1:7001 Adding replica 127.0.0.1:7005 to 127.0.0.1:7002 M: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slots:0-5460 (5461 slots) master M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:5461-10922 (5462 slots) master M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:10923-16383 (5461 slots) master S: d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 replicates 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 replicates dfa0754c7854a874a6ebd2613b86140ad97701fc Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join... >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slots:0-5460 (5461 slots) master M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:5461-10922 (5462 slots) master M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:10923-16383 (5461 slots) master M: d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 slots: (0 slots) master replicates 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 M: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slots: (0 slots) master replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c M: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slots: (0 slots) master replicates dfa0754c7854a874a6ebd2613b86140ad97701fc [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈。终于他妈的成功了!!!! Redis-trib会提示你做了什么配置, 输入yes接受. 集群就被配置和加入了, 意思是, 实例会经过互相交流后启动。 测试集群的状态: [root@web3 7000]# redis-trib.rb check 127.0.0.1:7000 Connecting to node 127.0.0.1:7000: OK Connecting to node 127.0.0.1:7002: OK Connecting to node 127.0.0.1:7003: OK Connecting to node 127.0.0.1:7005: OK Connecting to node 127.0.0.1:7001: OK Connecting to node 127.0.0.1:7004: OK >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slots:0-5460 (5461 slots) master 1 additional replica(s) M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s) S: d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 slots: (0 slots) slave replicates 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slots: (0 slots) slave replicates dfa0754c7854a874a6ebd2613b86140ad97701fc M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s) S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slots: (0 slots) slave replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
可以看到有3个主节点,3个从节点。每个节点都是成功的连接状态。 3个主节点[M]是:
3个从节点[S]是:
5. 测试连接集群刚才集群搭建成功了。按照redis cluster的特点,它是去中心化,每个节点都是对等的,所以,你连接哪个节点都可以获取和设置数据,我们来试一下。
连接任意一个节点端口: [root@web3 7000]# redis-cli -c -p 7000 127.0.0.1:7000> 设置一个值: 127.0.0.1:7000> set my_name yangyi -> Redirected to slot [12803] located at 127.0.0.1:7002 OK 127.0.0.1:7002> get my_name "yangyi" 127.0.0.1:7002> 前面理论知识我们知道了,分配key的时候,它会使用 Redirected to slot [12803] located at 127.0.0.1:7002 redis cluster 采用的方式很直接,它直接跳转到7002 节点了,而不是还在自身的7000节点。 好,现在我们连接 [root@web3 7000]# redis-cli -c -p 7005 127.0.0.1:7005> get my_name -> Redirected to slot [12803] located at 127.0.0.1:7002 "yangyi" 127.0.0.1:7002> 我们同样是获取 我们再尝试一些其他的可以: 127.0.0.1:7002> set age 123 -> Redirected to slot [741] located at 127.0.0.1:7000 OK 127.0.0.1:7000> set height 565 -> Redirected to slot [8223] located at 127.0.0.1:7001 OK 127.0.0.1:7001> set sex 1 -> Redirected to slot [2584] located at 127.0.0.1:7000 OK 127.0.0.1:7000> set home china -> Redirected to slot [10814] located at 127.0.0.1:7001 OK 127.0.0.1:7001> set city shanghai -> Redirected to slot [11479] located at 127.0.0.1:7002 OK 127.0.0.1:7002> set citylocate shanghaipudong OK 127.0.0.1:7001> set wsdwxzx hhh -> Redirected to slot [15487] located at 127.0.0.1:7002 OK 127.0.0.1:7002> zadd erew 333 rrr -> Redirected to slot [10576] located at 127.0.0.1:7001 (integer) 1 127.0.0.1:7000> zrange erew 0 -1 -> Redirected to slot [10576] located at 127.0.0.1:7001 1) "rrr" 127.0.0.1:7001> 可以看出,数据都会在7000-7002 这3个主节点来跳转存粗。 6. 测试集群中的节点挂掉上面我们建立来了一个集群。3个主节点[7000-7002]提供数据存粗和读取,3个从节点[7003-7005]则是负责把[7000-7002]的数据同步到自己的节点上来,我们来看一下[7003-7005]的 我们看下,的确是从7000节点上同步过来的数据,7004,7005也是。 下面,我们先来模拟其中一台Master主服务器挂掉的情况,那就7000挂掉吧: [root@web3 7003]# ps -ef|grep redis root 11380 1 0 07:37 ? 00:00:03 redis-server *:7000 [cluster] root 11384 1 0 07:37 ? 00:00:03 redis-server *:7001 [cluster] root 11388 1 0 07:37 ? 00:00:03 redis-server *:7002 [cluster] root 11392 1 0 07:37 ? 00:00:03 redis-server *:7003 [cluster] root 11396 1 0 07:37 ? 00:00:04 redis-server *:7004 [cluster] root 11400 1 0 07:37 ? 00:00:03 redis-server *:7005 [cluster] [root@web3 7003]# kill 11380 [root@web3 7003]# 好,安装前面的理论,7000主节点挂掉了,那么这个时候,7000的从节点只有7003一个,肯定7003就会被选举称Master节点了: [root@web3 7003]# redis-trib.rb check 127.0.0.1:7000 Connecting to node 127.0.0.1:7000: [ERR] Sorry, can't connect to node 127.0.0.1:7000 [root@web3 7003]# redis-trib.rb check 127.0.0.1:7001 Connecting to node 127.0.0.1:7001: OK Connecting to node 127.0.0.1:7004: OK Connecting to node 127.0.0.1:7005: OK Connecting to node 127.0.0.1:7003: OK Connecting to node 127.0.0.1:7002: OK >>> Performing Cluster Check (using node 127.0.0.1:7001) M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s) S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slots: (0 slots) slave replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slots: (0 slots) slave replicates dfa0754c7854a874a6ebd2613b86140ad97701fc M: d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 slots:0-5460 (5461 slots) master 0 additional replica(s) M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@web3 7003]# 看了下,上面有了三个M 节点了,果真,7003被选取成了替代7000成为主节点了。那我们来获取原先存在7000节点的数据: [root@web3 7003]# redis-cli -c -p 7001 127.0.0.1:7001> get sex -> Redirected to slot [2584] located at 127.0.0.1:7003 "1" 127.0.0.1:7003> 数据果真没有丢失,而是从7003上面获取了。 OK。我们再来模拟 7000节点重新启动了的情况,那么它还会自动加入到集群中吗?那么,7000这个节点上充当什么角色呢? 我们试一下: 重新启动 7000 节点: [root@web3 7003]# cd ../7000 [root@web3 7000]# ll total 56 -rw-r--r-- 1 root root 114 Oct 17 08:16 appendonly.aof -rw-r--r-- 1 root root 43 Oct 17 08:37 dump.rdb -rw-r--r-- 1 root root 745 Oct 17 08:00 nodes.conf -rw-r--r-- 1 root root 41550 Oct 17 07:37 redis.conf [root@web3 7000]# redis-server redis.conf
启动好了,现在,再来检查一下集群:
redis-trib.rb check 127.0.0.1:700` [root@web3 7000]# redis-trib.rb check 127.0.0.1:7001 Connecting to node 127.0.0.1:7001: OK Connecting to node 127.0.0.1:7004: OK Connecting to node 127.0.0.1:7005: OK Connecting to node 127.0.0.1:7003: OK Connecting to node 127.0.0.1:7000: OK Connecting to node 127.0.0.1:7002: OK >>> Performing Cluster Check (using node 127.0.0.1:7001) M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s) S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slots: (0 slots) slave replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slots: (0 slots) slave replicates dfa0754c7854a874a6ebd2613b86140ad97701fc M: d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 slots:0-5460 (5461 slots) master 1 additional replica(s) S: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slots: (0 slots) slave replicates d2237fdcfbba672de766b913d1186cebcb6e1761 M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. 你看,7000节点启动起来了,它却作为了 70003 的从节点了。验证了之前的这张图: 一定要保证有3个master 节点,不然,集群就挂掉了。 7. 集群中新加入节点我们再来测试一下,新加入一个节点,分2种情况,1是作为主节点,2是作为一个节点的从节点。我们分别来试一下: 1. 新建一个
|
名词 | 逻辑结构 | 物理结构 |
主节点 | redis主服务/数据库 | 一个独立的redis进程 |
从节点 | redis从服务/数据库 | 一个独立的redis进程 |
sentinel节点 | 监控redis数据节点 | 一个独立的sentinel进程 |
sentinel节点集合 | 若干sentinel节点的抽象集合 | 若干sentinel节点进程 |
应用方 | 泛指一个或多个客户端 | 一个或多个客户端线程或进程 |
每个sentinel节点其实就是一个redis实例,与主从节点不同的是sentinel节点作用是用于监控redis数据节点的,而sentinel节点集合则表示监控一组主从redis实例多个sentinel监控节点的集合,比如有主节点master和从节点slave-1、slave-2,为了监控这三个主从节点,这里配置N个sentinel节点sentinel-1,sentinel-2,...,sentinel-N。如下图是sentinel监控主从节点的示例图:
从图中可以看出,对于一组主从节点,sentinel只是在其外部额外添加的一组用于监控作用的redis实例。在主从节点和sentinel节点集合配置好之后,sentinel节点之间会相互发送消息,以检测其余sentinel节点是否正常工作,并且sentinel节点也会向主从节点发送消息,以检测监控的主从节点是否正常工作。。前面讲到,sentinel架构的主要作用是解决主从模式下主节点的故障转移工作的。这里如果主节点因为故障下线,那么某个sentinel节点发送检测消息给主节点时,如果在指定时间内收不到回复,那么该sentinel就会主观的判断该主节点已经下线,那么其会发送消息给其余的sentinel节点,询问其是否“认为”该主节点已下线,其余的sentinel收到消息后也会发送检测消息给主节点,如果其认为该主节点已经下线,那么其会回复向其询问的sentinel节点,告知其也认为主节点已经下线,当该sentinel节点最先收到超过指定数目(配置文件中配置的数目和当前sentinel节点集合数的一半,这里两个数目的较大值)的sentinel节点回复说当前主节点已下线,那么其就会对主节点进行故障转移工作,故障转移的基本思路是在从节点中选取某个从节点向其发送slaveof no one(假设选取的从节点为127.0.0.1:6380),使其称为独立的节点(也就是新的主节点),然后sentinel向其余的从节点发送slaveof 127.0.0.1 6380命令使它们重新成为新的主节点的从节点。重新分配之后sentinel节点集合还会继续监控已经下线的主节点(假设为127.0.0.1:6379),如果其重新上线,那么sentinel会向其发送slaveof命令,使其成为新的主机点的从节点,如此故障转移工作完成。
上面我们讲到了,每个sentinel节点在本质上还是一个redis实例,只不过和redis数据节点不同的是,其主要作用是监控redis数据节点。在redis安装目录下有个默认的sentinel配置文件sentinel.conf,和配置主从节点类似,这里复制三个配置文件:sentinel-26379.conf,sentinel-26380.conf和sentinel-26381.conf。分别按照如下示例编辑这三个配置文件:
port 26379
daemonize yes
logfile "26379.log"
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel myid mm55d2d712b1f3f312b637f9b546f00cdcedc787
对于端口为26380和26381的sentinel,其配置和上述类似,只需要把相应的端口号修改为对应的端口号即可。这里注意两点:①每个sentinel的myid参数也要进行修改,因为sentinel之间是通过该属性来唯一区分其他sentinel节点的;②参数中sentinel monitor mymaster 127.0.0.1 6379 2这里的端口号6379是不用更改的,因为sentinel是通过检测主节点的状态来得知当前主节点的从节点有哪些的,因而设置为主节点的端口号即可。配置完成后我们首先启动三个主从节点,然后分别使用三个配置文件使用如下命令启用sentinel:
./src/redis-sentinel sentinel-26379.conf
./src/redis-sentinel sentinel-26380.conf
./src/redis-sentinel sentinel-26381.conf
由于sentinel节点也是一个redis实例,因而我们可以通过如下命令使用redis-cli连接sentinel节点:
./src/redis-cli -p 26379
连上sentinel节点之后我们可以通过如下命令查看sentinel状态:
127.0.0.1:26379> info sentinel
结果如下:
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
可以看到,sentinel检测到主从节点总共有三个,其中一个主节点,两个从节点,并且sentinel节点总共也有三个。启动完成之后,我们可以通过主动下线主节点来模拟sentinel的故障转移过程。首先我们连接上端口为6379的主节点,使用如下命令查看主从节点状态:
127.0.0.1:6379> info replication
结果如下:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=45616,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=45616,lag=1
master_repl_offset:45616
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:45615
可以看到,当前主节点有两个从节点,端口分别为6380和6381。然后我们对主节点执行如下命令:
127.0.0.1:6379> shutdown save
然后我们连接上端口号为6380的从节点,并执行如下命令:
127.0.0.1:6380> info replication
结果如下:
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=12344,lag=0
master_repl_offset:12477
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:12476
可以看到,当端口为6379的实例下线之后,端口为6380的实例被重新竞选为新的主节点,并且端口为6381的实例被设置为6380的实例的从节点。如果我们此时重新启用端口为6379的节点,然后再查看主从状态,结果如下:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=59918,lag=0
slave1:ip=127.0.0.1,port=6379,state=online,offset=59918,lag=1
master_repl_offset:60051
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:60050
可以看到,端口为6379的redis实例重新连接后,sentinel节点检测到其重新连接,那么对其发送命令,使其成为新的主节点的从节点。
4.redis集群的配置
redis集群是在redis 3.0版本推出的一个功能,其有效的解决了redis在分布式方面的需求。当遇到单机内存,并发和流量瓶颈等问题时,可采用Cluster方案达到负载均衡的目的。并且从另一方面讲,redis中sentinel有效的解决了故障转移的问题,也解决了主节点下线客户端无法识别新的可用节点的问题,但是如果是从节点下线了,sentinel是不会对其进行故障转移的,并且连接从节点的客户端也无法获取到新的可用从节点,而这些问题在Cluster中都得到了有效的解决。
redis集群中数据是和槽(slot)挂钩的,其总共定义了16384个槽,所有的数据根据一致哈希算法会被映射到这16384个槽中的某个槽中;另一方面,这16384个槽是按照设置被分配到不同的redis节点上的,比如启动了三个redis实例:cluster-A,cluster-B和cluster-C,这里将0-5460号槽分配给cluster-A,将5461-10922号槽分配给cluster-B,将10923-16383号槽分配给cluster-C(总共有16384个槽,但是其标号类似数组下标,是从0到16383)。也就是说数据的存储只和槽有关,并且槽的数量是一定的,由于一致hash算法是一定的,因而将这16384个槽分配给无论多少个redis实例,对于确认的数据其都将被分配到确定的槽位上。redis集群通过这种方式来达到redis的高效和高可用性目的。
这里需要进行说明的一点是,一致哈希算法根据数据的key值计算映射位置时和所使用的节点数量有非常大的关系。一致哈希分区的实现思路是为系统中每个节点分配一个token,范围一般在0~2^32,这些token构成一个哈希环,数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该hash值的token节点,需要操作的数据就保存在该节点上。通过分析可以发现,一致哈希分区存在如下问题:
正是由于一致哈希分区的这些问题,redis使用了虚拟槽来处理分区时节点变化的问题,也即将所有的数据映射到16384个虚拟槽位上,当redis节点变化时数据映射的槽位将不会变化,并且这也是redis进行节点扩张的基础。
对于redis集群的配置,首先将redis安装目录下的redis.conf文件复制六份,分别取名为:cluster-6379.conf、cluster-6380.conf、cluster-6381.conf、cluster-6382.conf、cluster-6383.conf、cluster-6384.conf。对于一个高可用的集群方案,集群每个节点都将为其分配一个从节点,以防止数据节点因为故障下线,这里使用六份配置文件定义六个redis实例,其中三个作为主节点,剩余三个分别作为其从节点。对于这六份配置文件,以其中一份为例,以下是其需要修改的参数:
port 6379
cluster-enabled yes
cluster-node-timeout 15000
cluster-config-file "nodes-6379.conf"
pidfile /var/run/redis_6379.pid
logfile "cluster-6379.log"
dbfilename dump-cluster-6379.rdb
appendfilename "appendonly-cluster-6379.aof"
对于其余的配置文件,只需要将其中对应项的端口号和带有端口号的文件名修改为当前要指定的端口号和端口号的文件名即可。配置文件配置好之后使用如下命令启动集群中的每个实例:
./src/redis-server cluster-6379.conf
./src/redis-server cluster-6380.conf
./src/redis-server cluster-6381.conf
./src/redis-server cluster-6382.conf
./src/redis-server cluster-6383.conf
./src/redis-server cluster-6384.conf
仔细阅读上述配置文件可发现,当前配置和启动过程中并没有指定这六个实例的主从关系,也没有对16384个槽位进行分配。因而我们还需要进行进一步的配置,槽位的分配和主从关系的设定有两种方式进行,一种是使用redis-cli连接到集群节点上后使用cluster meet命令连接其他的节点,如我们首先执行如下命令连接到6379端口的节点:
./src/redis-cli -p 6379
连接上后使用cluster meet命令分别连接其余节点:
127.0.0.1:6379>cluster meet 127.0.0.1 6380
127.0.0.1:6379>cluster meet 127.0.0.1 6381
127.0.0.1:6379>cluster meet 127.0.0.1 6382
127.0.0.1:6379>cluster meet 127.0.0.1 6383
127.0.0.1:6379>cluster meet 127.0.0.1 6384
连接好后可以使用cluster nodes命令查看当前集群状态:
127.0.0.1:6379> cluster nodes
4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 master - 0 1468073975551 5 connected
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0 connected
be9485a6a729fc98c5151374bc30277e89a461d8 127.0.0.1:6383 master - 0 1468073978579 4 connected
40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 master - 0 1468073980598 3 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0 1468073974541 1 connected
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 master - 0 1468073979589 2 connected
可以看到配置的六个节点都已经加入到了集群中,但是其现在还不能使用,因为还没有将16384个槽分配到集群节点中。虚拟槽的分配可以使用redis-cli分别连接到6379,6380和6381端口的节点中,然后分别执行如下命令:
127.0.0.1:6379>cluster addslots {0...5461}
127.0.0.1:6380>cluster addslots {5462...10922}
127.0.0.1:6381>cluster addslots {10923...16383}
添加完槽位后可使用cluster info命令查看当前集群状态:
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:4874
cluster_stats_messages_received:4726
这里我们将16384个虚拟槽位分配给了三个节点,而剩余的三个节点我们通过如下命令将其配置为这三个节点的从节点,从而达到高可用的目的:
127.0.0.1:6382>cluster replicate cfb28ef1deee4e0fa78da86abe5d24566744411e
OK
127.0.0.1:6383>cluster replicate 8e41673d59c9568aa9d29fb174ce733345b3e8f1
OK
127.0.0.1:6384>cluster replicate 40b8d09d44294d2e23c7c768efc8fcd153446746
OK
如此,所有的集群节点都配置完毕,并且处于可用状态。这里可以使用cluster nodes命令查看当前节点的状态:
127.0.0.1:6379> cluster nodes
4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 slave 40b8d09d44294d2e23c7c768efc8fcd153446746 0 1468076865939 5 connected
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0 connected 0-5461
be9485a6a729fc98c5151374bc30277e89a461d8 127.0.0.1:6383 slave 8e41673d59c9568aa9d29fb174ce733345b3e8f1 0 1468076868966 4 connected
40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 slave cfb28ef1deee4e0fa78da86abe5d24566744411e 0 1468076869976 3 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0 1468076870987 1 connected 5462-10922
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 master - 0 1468076867957 2 connected 10923-16383
我们使用redis-cli使用如下命令连接集群:
./src/redis-cli -c -p 6380
注意连接集群模式的redis实例时需要加上参数-c,表示连接的是集群模式的实例。连接上后执行get命令:
127.0.0.1:6380> get hello
-> Redirected to slot [866] located at 127.0.0.1:6379
(nil)
可以看到,在6380端口的实例上执行get命令时,其首先会为当前的键通过一致哈希算法计算其所在的槽位,并且判断该槽位不在当前redis实例中,因而重定向到目标实例上执行该命令,最后发现没有该键对应的值,因而返回了一个(nil)。
|