【redis cluster】配置详解
这几天在看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:7008 127.0.0.1:7000
后面的7000可以是集群内任意一个,同上,也同下
节点添加进去默认是master,但这时虽然添加了,但7008并没有分配到slot,slot是0,所以还要手动做迁移(这也算是redis cluster的一个缺点,不能自动发现新节点,不能自动迁移,全都要手动)。
./redis-trib.rb reshard 127.0.0.1:7000 //同上
这时会出现提示,How many slots do you want to move (from 1 to 16384)?
16384/4=4096,输入4096
又是提示,What is the receiving node ID?
前面会有7008的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:
也就是说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:7009 127.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)