这几天在看redis集群,之前官方没有集群方案,大多都是twitter的twemproxy和豌豆荚的codis,redis3.x版本开始支持集群。

redis cluster的设计重点是去中心化,去中间件,每个节点都是平等的,都和其他所有节点连接,保存着自己的数据和集群状态。

关注点在性能(p2p而非proxy方式、异步复制、客户端重定向,牺牲了部分一致性)、水平扩展(1000节点)、可用性(之前高可用方案是redis sentinel,集群自动具有监控功能)。

为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点负责数据存取,而从节点通过异步复制的方式(发生写操作时,客户端写入master,master返回ok给client,master更新到slave,问题在于如果第二步后主节点挂掉,那么从节点没来得更新就被选为主节点,会造成数据不一致)从主节点拉取数据,做备份,一旦主节点挂掉,通过选举的方式(其他主节点投票)找出一个从节点做新的主节点,如果没有从节点来接替,集群就会挂掉。

在分片方面,redis cluster将键空间分为16384个哈希槽,同时通过crc16算法来计算key属于哪个槽:HASH_SLOT = CRC16(key) mod 16384,然后将key分配到这个哈希槽所在的节点上。至于哈希槽跟节点的分配,比如三个节点的话,就平均分为三份,节点1覆盖0-5460、节点2覆盖5461-10922、节点3覆盖10923-16383;如果新加入一个节点,那么分配时就会分别从前面3个节点截取相同部分,分配到新节点。

 

接下来就是详细的配置了(在腾讯云上试的,系统CentOS 7.2 64位) 

首先下载redis3.x版本,之前的不支持集群

解压安装

新建集群目录cluster

因为cluster至少要有三个节点才可运行,建六个节点,三主三从,建六个文件夹,7000-7005

将redis.conf文件复制到六个文件夹下,并修改

port 7000  //7000-7005
cluster-enabled yes   //开启集群
cluster-config-file nodes.conf  //保存节点配置,自动创建,自动更新
cluster-node-timeout 5000    //集群超时时间,节点超过这个时间没反应就断定是宕机
appendonly yes   //存储方式,aof,将写操作记录保存到日志中

启动六个节点,命令:redis-server redis.conf

查看redis情况,命令:ps -ef|grep redis

 

redis cluster的集群管理功能没有写到redis代码中,而是用redis-trib的管理脚本,redis-trib依赖ruby和rubygems以及redis的扩展,所以正式建立集群之前要把这三个装上。

yum install ruby
yum install rubygems
gem install redis -v 3.0.0

 

环境安装完,就可以继续了

./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

 其中--replicas 1是指每个主节点的从节点数目是1,默认前面的是主节点,这样主节点就是7000-7002,从节点就是7003-7004。

可以查看集群状态,命令 ./redis-trib.rb check 127.0.0.1:7000  //这里可以是集群中的任意一个端口

 

接下来就是连接集群了,redis-cli是默认客户端工具,-c是连接集群

redis-cli -c -p 7000

set test "hello"

之前提到过分配数据时是通过crc16算法来计算的,所以这个slot=crc16(test)mod 16384,假装slot结果是在5461-10922之间,也就是port7001上。

那么这时集群就会重定向跳转到7001,然后把test存到7001端口。

这时get test,也会跳转到7001。

 

接下来就是添加删除新的节点了,假如新节点端口是7008和7009,先开启节点。

然后命令,./redis-trib.rb add-node 127.0.0.1:7008127.0.0.1:7000

后面的7000可以是集群内任意一个,同上,也同下

节点添加进去默认是master,但这时虽然添加了,但7008并没有分配到slot,slot是0,所以还要手动做迁移(这也算是redis cluster的一个缺点,不能自动发现新节点,不能自动迁移,全都要手动)。

./redis-trib.rb reshard 127.0.0.1:7000 //同上

这时会出现提示,Howdo(from116384)?

16384/4=4096,输入4096

又是提示,Whatis? 

前面会有7008的id,挺长的,复制过来

再然后,

PleaseIDs.
Type'all'useasfor.
Type'done'IDs.
Source#1:

也就是说slot的源节点从哪些取,可以输入特定id,也可以输入all,即表示从现在所有的主节点平均取,输入all

这样就可以了,7008就覆盖了0-1364,5461-6826,10923-12287,分别从前三个主节点里面取的。

或者直接一条命令

./redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port>

 

然后将7009加进去,因为是想让7009做7008的从节点,所以命令有变化:

./redis-trib.rb add-node --slave --master-id (7008id) 127.0.0.1:7009127.0.0.1:7000

--slave表示作为从节点加入,--master-id也就是指定他的主节点id。

PS:可在某从节点下通过cluster replicate  <new-master-node-id> 使其更换主节点

 

 

接下来是删除节点,

./redis-trib.rb del-node 127.0.0.1:7000(7008id)

如果7008中有数据,那就要麻烦一点,再重新分片,将7008的slot分配给其他节点,然后再删除。但是如果是删除从节点的话,就不用重新分片。

而剩下的7009会自动给被分配的节点做从节点。

 

有种情况是宕机了,比如7001死掉了,那么7004会被选举为新的主节点,get test时会跳转到7004,如果后面7001又启动了,那么他会作为7004的从节点。

 

redis cluster 有几个问题,一是一致性,暂时没看到有什么解决方案,或者client写操作时,更新到slave成功后才给client发ok?可是这样对性能就有影响,尤其当有多个从节点时。

再就是不支持多key操作,如果多个key分配在不同节点上,就会悲剧,看到的解决方法是hash tag方案(即对多个key的相同部分做hash),这样就可以保证他们进的是同一节点。

还有负载均衡,从节点只负责异步备份,负载不均。

 

 参考:Redis集群研究和实践(基于redis 3.0.5)

全面剖析Redis Cluster原理和应用 

        redis3.0.0 集群安装详细步骤