集群技术是构建高性能网站架构的重要手段,试想在网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,并快速响应,我们必然想到的是将数据进行切片,把数据根据某种规则放入多个不同的服务器节点,来降低单节点服务器的压力。上篇redis_主从我们讲到了 Redis 的主从复制技术,当实现了多节点的 master-slave 后,我们也可以把它叫做集群,但我们今天要讲的集群主要是利用切片技术来组建的集群。我们最后希望达到的是类似下图:
二、实现策略
因为从3.0开始以后官方已经支持了 redis cluster,http://redis.io/topics/cluster-tutorial
集群要实现的目的是要将不同的 key 分散放置到不同的 redis 节点,这里我们需要一个规则或者算法,通常的做法是获取 key 的哈希值,然后根据节点数来求模,但这种做法有其明显的弊端,当我们需要增加或减少一个节点时,会造成大量的 key 无法命中,这种比例是相当高的,所以就有人提出了一致性哈希的概念。
由于官方版本是基于哈希槽(hash slot)的概念来实现的,我们还是从其官方介绍中翻译解释一下:
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
使用哈希槽的好处就在于可以方便的添加或移除节点:
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;
集群模式下支持主从复制,但是在从服务器不支持读写操作,所有的读写操作都在主上完成,只有在对应的主发生故障不能对外提供服务时,从才会变成主,接替原来的主继续对外提供服务;
三、集群实现
对于我们来说,在新增或移除节点的能做到无缝(即不需要重启集群),这点它做到了。还是来看看具体的实现吧:
新建redis_cluster文件夹下面新建6000、6100、6200三个文件夹。
把src 目录下面的redis-server、redis.conf这两个文件分别拷贝到这三个目录里面,拷贝完之后就像这样子了:
[[email protected] 6000]$ ll -rw-rw-r--. 1 mysql mysql 116 Aug 21 05:37 redis.conf -rwxrwxr-x. 1 mysql mysql 7820101 Aug 21 05:29 redis-server
修改每个目录下面的redis.conf文件因为我们启动的端口是不一样的,vi redis.conf删除里面的所有添加如下的:
daemonize yes //指定以守护进程启动 port 6000 //启动端口 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
依次启动每一个实例文件夹下面的./redis-server ./redis.conf,这样的话3个实例就启动了,但是如何将3个实例维护成一个集群呢,在src目录下面执行:
./redis-trib.rb create --replicas 0 127.0.0.1:6000 127.0.0.1:6100 127.0.0.1:6200
执行以后是不是会发现执行失败提示没有redis-trib.rb文件,好吧我们还忘了装一个,因为我们执行的是ruby命令,所以还需要安装ruby:
https://rubygems.org/gems/redis下载,然后离线安装。sudo gem install redis-3.3.1.gem --local
安装完成以后再src目录下面就有了redis-trib.rb文件。
执行上面的集群命令控制台输出如下:
来试试集群的效果吧:
在6000端口实例上测试 [[email protected] src]$ ./redis-cli -c -p 6000 127.0.0.1:6000> get name -> Redirected to slot [5798] located at 127.0.0.1:6100 //发现当没有指定的key的时候会重定向到集群的其它机器去找 127.0.0.1:6100> set aaa 111 OK 127.0.0.1:6100> get aaa "111" //如上索性在6100实例的机器上面set aaa的值,然后在6200的机器上面去查 [[email protected] src]$ ./redis-cli -c -p 6200 127.0.0.1:6200> get aaa -> Redirected to slot [10439] located at 127.0.0.1:6100 //发现重定向去6100的端口的实例上找到了结果 "111" //在6200的机器上面直接重置aaa为1234 127.0.0.1:6200> set aaa 1234 -> Redirected to slot [10439] located at 127.0.0.1:6100 //还是重定向去6100的端口的实例上更新结果 OK
从上面的结果可以看出,集群中的节点是会进行通讯的,从而能找到不同的key在不同实例上,在机器固定的情况下,key唯一时后续对其的所有更新以及查询都会映射到这一台机器上面。
当然我们的集群的节点是不会一成不变的,我们随时有可能扩容、缩容那该怎么来实现呢?下面来讲讲集群节点的变化吧。
四、集群变更
我们先在上面的基础上试试添加端口为6300的实例,重复操作如上:
[[email protected] redis_cluster]$ mkdir 6300 [[email protected] redis_cluster]$ ll total 16 drwxrwxr-x. 2 mysql mysql 4096 Aug 21 05:40 6000 drwxrwxr-x. 2 mysql mysql 4096 Aug 21 05:40 6100 drwxrwxr-x. 2 mysql mysql 4096 Aug 21 05:40 6200 drwxrwxr-x. 2 mysql mysql 4096 Aug 21 06:27 6300 [[email protected] redis_cluster]$ cp ../redis/redis-3.2.3/src/redis-server ../redis/redis-3.2.3/redis.conf 6300/
不要忘了修改redis.conf,具体配置如上文提到的。
[[email protected] 6300]$ ./redis-server ./redis.conf [[email protected] 6300]$ netstat -an | grep 6300 tcp 0 0 0.0.0.0:16300 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:6300 0.0.0.0:* LISTEN tcp 0 0 :::16300 :::* LISTEN tcp 0 0 :::6300 :::* LISTEN
新的redis实例已经启动好了,然后需要把它加到已有的集群啦:
./redis-trib.rb add-node 127.0.0.1:6300 127.0.0.1:6000
如上已经能查看到新的实例啦,为什么6300这个新的实例的connected后面没有分配hash槽呢,别慌我们还没有reshard, 执行如下命令:
./redis-trib.rb reshard 127.0.0.1:6300
我们想从6000节点中分配1000个hash槽位出来:
这样有hash槽了,当crc16(key) / 16384 的结果在该节点的所在区间时,数据就放在该节点上。
redis 采用的crc16算法详见:http://blog.csdn.net/guodongxiaren/article/details/44706613
五、如何为每一个主节点增加从节点呢?
好吧,先创建6400端口实例看看,具体新建文件夹以及配置和重启实例我就不再讲了,上面以及有了。执行如下命令:
./redis-trib.rb add-node --slave 127.0.0.1:6400 127.0.0.1:6000
我们也可以试试在从节点6400上面做set操作:
127.0.0.1:6400> set da87 (error) ERR wrong number of arguments for ‘set‘ command 127.0.0.1:6400> set da (error) ERR wrong number of arguments for ‘set‘ command
很不幸,确实不能做set操作会报错,从节点只能用于做读工作。
六、我们如何来删除一个节点呢?
[[email protected] src]$ ./redis-trib.rb del-node 127.0.0.1:6000 ‘eb868ce2c102b897ee0a48cd8893288c36bacf5c‘ >>> Removing node eb868ce2c102b897ee0a48cd8893288c36bacf5c from cluster 127.0.0.1:6000 [ERR] Node 127.0.0.1:6000 is not empty! Reshard data away and try again. [[email protected] src]$
我们发现删除6000端口的实例失败了,应该是上面还有一些数据如果直接删除就有数据丢失,我们应该把数据移到别的节点去:
./redis-trib.rb reshard 127.0.0.1:6000 //移除6000实例的数据 然后输出了很多信息,很多数值和ID都可以从这段信息中找到。 How many slots do you want to move (from 1 to 16384)? 5461 会问你要移动多少个哈希槽,我们把 6000上的所有哈希槽都移走,5461 这个数字可以从终端上看到,或许你的实际情况不是这个数字。 What is the receiving node ID? d4467ece7cca245345b71cc1f639508f4f7831c4 //选定移动到6300端口的实例上面,实例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:eb868ce2c102b897ee0a48cd8893288c36bacf5c //选定移除掉6000端口的实例上面,实例ID Source node #2:done 之后,redis 列出了重新分片计划,最后问你 Do you want to proceed with the proposed reshard plan (yes/no)? yes
中间有一些重新分配的刷屏信息省略。
[[email protected] src]$ ./redis-cli -p 6300 127.0.0.1:6300> keys * 1) "aaa" 127.0.0.1:6300>
数据移到6300实例上面啦,我们再来试试删除6000端口实例吧:
[[email protected] src]$ ./redis-trib.rb del-node 127.0.0.1:6000 ‘eb868ce2c102b897ee0a48cd8893288c36bacf5c‘ >>> Removing node eb868ce2c102b897ee0a48cd8893288c36bacf5c from cluster 127.0.0.1:6000 >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node. [[email protected] src]$ ./redis-cli -c -p 6000 Could not connect to Redis at 127.0.0.1:6000: Connection refused Could not connect to Redis at 127.0.0.1:6000: Connection refused
最终我们还是删除成功啦!维护一个redis集群就是这么简单。
文章出自:http://mp.weixin.qq.com/s?__biz=MzIyMDA1MzgyNw==&mid=2651968549&idx=1&sn=af91a3975fd372525d230ddd5b6b24d6&chksm=8c349c09bb43151fb02bf64bea4915abb685a309c2cebe2c92df1f29cec43bd0a9ce9a462b7f&scene=1&srcid=09189zzjCEabRR0PCZqV8epT#wechat_redirect