Redis Cluster介绍

Redis集群解决方案:

目前官方支持redis 集群化解决方案主要有2种:sentinel 和 cluster。

Redis 的 Sentinel 系统用于管理多个 Redis 实例,sentinel自身也是由多台redis组成的集群(一般情况下 redis实例>=3), 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, sentinel集群通过投票将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

redis cluster 动态 redis cluster sentinel_2d

Redis 的 Cluster 是redis 3.0版本后新加入的集群功能,有如下特点:

  • 数据共享:所有集群中的节点直接相连,访问任意一个节点都可以获取到整个集群的数据。
  • 数据分片存储:Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽。
  • 自动故障迁移:当一个主服务器不能正常工作时,能自动切换主节点,保证在部分节点失效的情况下集群依然可用。
  • 动态扩容和收缩:可以在不影响集群正常使用的情况下动态扩容和收缩。

redis cluster 动态 redis cluster sentinel_服务器_02

相比之下很明显,cluster相比sentinel跟具有优势。

 

Redis Cluster命令

CLUSTER info:打印集群的信息。
CLUSTER nodes:列出集群当前已知的所有节点(node)的相关信息。
CLUSTER meet <ip> <port>:将ip和port所指定的节点添加到集群当中。
CLUSTER addslots <slot> [slot ...]:将一个或多个槽(slot)指派(assign)给当前节点。
CLUSTER delslots <slot> [slot ...]:移除一个或多个槽对当前节点的指派。
CLUSTER slots:列出槽位、节点信息。
CLUSTER slaves <node_id>:列出指定节点下面的从节点信息。
CLUSTER replicate <node_id>:将当前节点设置为指定节点的从节点。
CLUSTER saveconfig:手动执行命令保存保存集群的配置文件,集群默认在配置修改的时候会自动保存配置文件。
CLUSTER keyslot <key>:列出key被放置在哪个槽上。
CLUSTER flushslots:移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
CLUSTER countkeysinslot <slot>:返回槽目前包含的键值对数量。
CLUSTER getkeysinslot <slot> <count>:返回count个槽中的键。

CLUSTER setslot <slot> node <node_id> 将槽指派给指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽,然后再进行指派。  
CLUSTER setslot <slot> migrating <node_id> 将本节点的槽迁移到指定的节点中。  
CLUSTER setslot <slot> importing <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。  
CLUSTER setslot <slot> stable 取消对槽 slot 的导入(import)或者迁移(migrate)。 

CLUSTER failover:手动进行故障转移。
CLUSTER forget <node_id>:从集群中移除指定的节点,这样就无法完成握手,过期时为60s,60s后两节点又会继续完成握手。
CLUSTER reset [HARD|SOFT]:重置集群信息,soft是清空其他节点的信息,但不修改自己的id,hard还会修改自己的id,不传该参数则使用soft方式。

CLUSTER count-failure-reports <node_id>:列出某个节点的故障报告的长度。
CLUSTER SET-CONFIG-EPOCH:设置节点epoch,只有在节点加入集群前才能设置。

  

Redis Cluster部署

redis下载与安装:

redis最新安装包可以在redis中文官网上下载http://www.redis.cn,目前最新版本为http://download.redis.io/releases/redis-4.0.8.tar.gz。

tar zxvf redis-4.0.8.tar.gz
cd redis-4.0.8
make && make install

  

下载并安装Ruby(redis部署需要2.2.2以上版本的Ruby,而linux默认安装的是2.0.0),目前最新版本为https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.7.tar.gz。

tar zxvf ruby-2.2.7.tar.gz
cd ruby-2.2.7
./configure --prefix=/usr/local/ruby-2.2.7
make && make install
ln -s /usr/local/ruby-2.2.7/bin/ruby /usr/local/bin/ 
ln -s /usr/local/ruby-2.2.7/bin/gem /usr/local/bin/

 

安装ruby的redis扩展包。

gem install redis

  

redis部署:

 现在创建redis实例的目录,由于集群最少需要3个节点,且为每个主节点分配一个从节点,则一共需要创建6个redis实例

mkdir -p /usr/local/redis_cluster && cd /usr/local/redis_cluster
mkdir 7000 7001 7002 7003 7004 7005
cp ~/redis-4.0.8/redis.conf 7000
cp ~/redis-4.0.8/redis.conf 7001
cp ~/redis-4.0.8/redis.conf 7002
cp ~/redis-4.0.8/redis.conf 7003
cp ~/redis-4.0.8/redis.conf 7004
cp ~/redis-4.0.8/redis.conf 7005

  

修改redis.conf配置文件

bind 0.0.0.0                              #ip 
port 7000                                 #port
daemonize yes                             #后台运行
pidfile /var/run/redis_7000.pid           #pid文件
logfile /var/log/redis/redis_7000.log     #log文件
dir /usr/local/redis_cluster/7000         #redis运行目录
appendonly yes                            #开启aof持久化
cluster-enabled yes                       #开启集群模式
cluster-config-file nodes-7000.conf       #集群信息文件
cluster-node-timeout 5000                 #超时时间

  

启动redis实例

redis-server redis.conf

  

创建redis集群

cd redis-4.0.8/src
./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

 注意:

  1. 因为我的多个redis实例都在同一台机器上,且我只在这台机器上访问集群,所以这里使用127.0.0.1创建集群,但是如果希望在其他机器能访问到这个集群,请使用具体的ip地址。
  2. 若果长时间卡在创建集群上,检查防火墙规则,开放端口7000~7005和17000~17005。

 

登入集群

redis-cli -c -h 127.0.0.1 -p 7000

 

查看集群信息

127.0.0.1:7000> 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:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:1500
cluster_stats_messages_pong_sent:1495
cluster_stats_messages_sent:2995
cluster_stats_messages_ping_received:1490
cluster_stats_messages_pong_received:1500
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:2995

  

查看集群节点

127.0.0.1:7000> cluster nodes
f75f8bd1343f60674724420f82a56d5c2a8e4c61 127.0.0.1:7002@17002 master - 0 1522316252551 3 connected 10923-16383
0a0f35b90aa5a01eebcf7ee4c86d301a70596326 127.0.0.1:7003@17003 slave f75f8bd1343f60674724420f82a56d5c2a8e4c61 0 1522316252000 4 connected
4ef77f0aa963d1ec2dc4abd2ddb1aa755515c63b 127.0.0.1:7004@17004 slave 891535cb8f80911c86759622f296d5f5502d5c52 0 1522316252756 5 connected
c12c6957bbc267dfa2dc117cd0f72162154c6574 127.0.0.1:7001@17001 master - 0 1522316253598 2 connected 5461-10922
891535cb8f80911c86759622f296d5f5502d5c52 127.0.0.1:7000@17000 myself,master - 0 1522316251000 1 connected 0-5460
125e2dcf5b9ea707108663c6995a27f66a71ffda 127.0.0.1:7005@17005 slave c12c6957bbc267dfa2dc117cd0f72162154c6574 0 1522316252961 6 connected

 

设置集群密码

127.0.0.1:7000> config set masterauth 999
OK
127.0.0.1:7000> config set requirepass 999
OK
127.0.0.1:7000> config rewrite
OK

注意:密码需要到每个实例上都设置。

 

附上python修改redis密码脚本

import rediscluster
import redis
import sys 

startup_nodes = [{"host": "127.0.0.1", "port": "7000"}, {"host": "127.0.0.1", "port": "7001"}, {"host": "127.0.0.1", "port": "7002"}]

if __name__ == "__main__":
    action = sys.argv[1]
    if action == "add":
        conn = rediscluster.StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)
        cluster_list = conn.cluster("info").keys()
        for cluster in cluster_list:
            host,port = cluster.split(":")
            sub_conn = redis.StrictRedis(host,int(port))
            sub_conn.config_set("masterauth","943013827")
            sub_conn.config_set("requirepass","943013827")
    elif action == "del":
        conn = rediscluster.StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True,password="943013827")
        cluster_list = conn.cluster("info").keys()
        for cluster in cluster_list:
            host,port = cluster.split(":")
            sub_conn = redis.StrictRedis(host,int(port),password="943013827")
            sub_conn.config_set("masterauth","")
            sub_conn.config_set("requirepass","")

 

redis-trib.rb默认不使用密码,需要修改redis-trib.rb使其能对有密码的集群进行扩容或者收缩。

redis cluster 动态 redis cluster sentinel_2d_03

在第99行 Redis.new 中增加 password参数的传入。

 

redis主从切换:

主从切换需要在从节点进行

127.0.0.1:7004> cluster failover
OK

 

redis集群扩容:

将实例 127.0.0.1:7006 和 127.0.0.1:7007加入集群,7006为主节点,7007为从节点

./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
./redis-trib.rb add-node --slave --master-id e32c0773543248e6e52968e00b115a4fa0349215 127.0.0.1:7007 127.0.0.1:7000
127.0.0.1:7000> cluster nodes
f75f8bd1343f60674724420f82a56d5c2a8e4c61 127.0.0.1:7002@17002 master - 0 1522316252551 3 connected 10923-16383
0a0f35b90aa5a01eebcf7ee4c86d301a70596326 127.0.0.1:7003@17003 slave f75f8bd1343f60674724420f82a56d5c2a8e4c61 0 1522316252000 4 connected
4ef77f0aa963d1ec2dc4abd2ddb1aa755515c63b 127.0.0.1:7004@17004 slave 891535cb8f80911c86759622f296d5f5502d5c52 0 1522316252756 5 connected
c12c6957bbc267dfa2dc117cd0f72162154c6574 127.0.0.1:7001@17001 master - 0 1522316253598 2 connected 5461-10922
891535cb8f80911c86759622f296d5f5502d5c52 127.0.0.1:7000@17000 myself,master - 0 1522316251000 1 connected 0-5460
125e2dcf5b9ea707108663c6995a27f66a71ffda 127.0.0.1:7005@17005 slave c12c6957bbc267dfa2dc117cd0f72162154c6574 0 1522316252961 6 connected
399a273af3c0562af79da69b57d813c38f6a6aa9 47.96.162.39:7006@17006 master - 0 1522640993798 7 connected
65c7af1e2e7bc238ec1c49648c1517dceeb2adee 47.96.162.39:7007@17007 slave 03fa05d789a28976ce8988b3035e9c6f6acd30ce 0 1522640992848 8 connected

  

重新分配哈希槽

在重新分配哈希槽之前需要将集群的密码设置为空,否则迁移的时候会报错。

127.0.0.1:7000> config set masterauth ""
OK
127.0.0.1:7000> config set requirepass ""
OK
./redis-trib.rb reshard 127.0.0.1:7000

 

redis cluster 动态 redis cluster sentinel_服务器_04

在迁移的时候出错:[ERR] Calling MIGRATE: ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY)

脚本报语法错误,在网上没有找到相关资料,尝试自己定位,由于是migrate命令失败,在redis-trib.rb中查找migrate调用的地方。

redis cluster 动态 redis cluster sentinel_服务器_05

这个函数批量migrate多个key,由于他调用失败导致的,使用循环migrate单个key代替批量migate。

redis cluster 动态 redis cluster sentinel_redis_06

 

修复集群

./redis-trib.rb fix 127.0.0.1:7000

redis cluster 动态 redis cluster sentinel_redis_07

 

再次分配哈希槽

./redis-trib.rb reshard 127.0.0.1:7000

redis cluster 动态 redis cluster sentinel_2d_08

 

成功迁移哈希槽,集群扩容成功

127.0.0.1:7000> cluster nodes
c3ccc725f4d3e0a15f5206dc29744f556d585802 47.96.162.39:7002@17002 master - 0 1524128799018 3 connected 12256-16383
65c7af1e2e7bc238ec1c49648c1517dceeb2adee 47.96.162.39:7007@17007 slave 399a273af3c0562af79da69b57d813c38f6a6aa9 0 1524128799519 13 connected
c8e6b1f3d0a1d152d45136ecca0cd04950cbe313 47.96.162.39:7004@17004 slave 03fa05d789a28976ce8988b3035e9c6f6acd30ce 0 1524128799000 16 connected
399a273af3c0562af79da69b57d813c38f6a6aa9 47.96.162.39:7006@17006 master - 0 1524128801020 13 connected 0-10
e4774f3db84138733f81422dd3aed7d27a523f6d 47.96.162.39:7003@17003 slave c3ccc725f4d3e0a15f5206dc29744f556d585802 0 1524128800019 4 connected
b00e5ce4c4fd87224d386d131e92c149c1bfc639 47.96.162.39:7001@17001 master - 0 1524128800520 17 connected 6795-10922
03fa05d789a28976ce8988b3035e9c6f6acd30ce 172.16.1.163:7000@17000 myself,master - 0 1524128800000 16 connected 11-6794 10923-12255
9f8311c7b0d7e4bdec43e9caa94a20bb79b31f84 47.96.162.39:7005@17005 slave b00e5ce4c4fd87224d386d131e92c149c1bfc639 0 1524128800520 17 connected

 

redis集群收缩:

将实例 127.0.0.1:7000从集群中移除

./redis-trib.rb del-node 127.0.0.1:7000 399a273af3c0562af79da69b57d813c38f6a6aa9

注意:删除节点前需要保证,该节点的所有哈希槽均已迁移给其他节点。

 

====================================================================分割线==========================================================================

接下来介绍下如何手动迁移哈希槽,因为使用redis-trib.rb进行迁移的时候,有些key会失败(key过大,key冲突),此时就如要我们手动进行迁移。

现在我们要将 11 号哈希槽从 redis-7000 迁移到 redis-7006:

1.首先查看节点信息

127.0.0.1:7000> cluster nodes
c3ccc725f4d3e0a15f5206dc29744f556d585802 47.96.162.39:7002@17002 master - 0 1524128799018 3 connected 12256-16383
65c7af1e2e7bc238ec1c49648c1517dceeb2adee 47.96.162.39:7007@17007 slave 399a273af3c0562af79da69b57d813c38f6a6aa9 0 1524128799519 13 connected
c8e6b1f3d0a1d152d45136ecca0cd04950cbe313 47.96.162.39:7004@17004 slave 03fa05d789a28976ce8988b3035e9c6f6acd30ce 0 1524128799000 16 connected
399a273af3c0562af79da69b57d813c38f6a6aa9 47.96.162.39:7006@17006 master - 0 1524128801020 13 connected 0-10
e4774f3db84138733f81422dd3aed7d27a523f6d 47.96.162.39:7003@17003 slave c3ccc725f4d3e0a15f5206dc29744f556d585802 0 1524128800019 4 connected
b00e5ce4c4fd87224d386d131e92c149c1bfc639 47.96.162.39:7001@17001 master - 0 1524128800520 17 connected 6795-10922
03fa05d789a28976ce8988b3035e9c6f6acd30ce 172.16.1.163:7000@17000 myself,master - 0 1524128800000 16 connected 11-6794 10923-12255
9f8311c7b0d7e4bdec43e9caa94a20bb79b31f84 47.96.162.39:7005@17005 slave b00e5ce4c4fd87224d386d131e92c149c1bfc639 0 1524128800520 17 connected

redis-7000 实例id为 03fa05d789a28976ce8988b3035e9c6f6acd30ce

redis-7006 实例id为 399a273af3c0562af79da69b57d813c38f6a6aa9

 

2.在redis-7006上将 11 号哈希槽设置为 importing 状态。

127.0.0.1:7006> cluster setslot 11 importing 03fa05d789a28976ce8988b3035e9c6f6acd30ce
OK

 

3.在redis-7000上将 11 号哈希槽设置为 migrating 状态。

127.0.0.1:7000> cluster setslot 11 migrating 399a273af3c0562af79da69b57d813c38f6a6aa9
OK

 

4.在redis-7000上查询 11 号哈希槽中所有的key

127.0.0.1:7000> cluster getkeysinslot 11 100
1) "k11988"
2) "k49373"
3) "k58332"
4) "k8310"
5) "k89069"
6) "k98028"

 

5.在redis-7000上将上述的key迁移到redis-7006上

127.0.0.1:7000> migrate 127.0.0.1 7006 "" 0 1000 keys k11988 k49373 k58332 k8310 k89069 k98028
OK

  如果出现错误:BUSYKEY Target key name already exists,则选择覆盖方式迁移

127.0.0.1:7000> migrate 127.0.0.1 7006 "" 0 1000 replace keys k11988 k49373 k58332 k8310 k89069 k98028
OK

 

6.分别在redis-7000和redis-7006上声明11槽新的实例id

127.0.0.1:7000> cluster setslot 11 node 399a273af3c0562af79da69b57d813c38f6a6aa9
OK
127.0.0.1:7006> cluster setslot 11 node 399a273af3c0562af79da69b57d813c38f6a6aa9
OK

 

7.查看节点信息,迁移成功

127.0.0.1:7000> cluster nodes
c3ccc725f4d3e0a15f5206dc29744f556d585802 47.96.162.39:7002@17002 master - 0 1524128799018 3 connected 12256-16383
65c7af1e2e7bc238ec1c49648c1517dceeb2adee 47.96.162.39:7007@17007 slave 399a273af3c0562af79da69b57d813c38f6a6aa9 0 1524128799519 13 connected
c8e6b1f3d0a1d152d45136ecca0cd04950cbe313 47.96.162.39:7004@17004 slave 03fa05d789a28976ce8988b3035e9c6f6acd30ce 0 1524128799000 16 connected
399a273af3c0562af79da69b57d813c38f6a6aa9 47.96.162.39:7006@17006 master - 0 1524128801020 13 connected 0-11
e4774f3db84138733f81422dd3aed7d27a523f6d 47.96.162.39:7003@17003 slave c3ccc725f4d3e0a15f5206dc29744f556d585802 0 1524128800019 4 connected
b00e5ce4c4fd87224d386d131e92c149c1bfc639 47.96.162.39:7001@17001 master - 0 1524128800520 17 connected 6795-10922
03fa05d789a28976ce8988b3035e9c6f6acd30ce 172.16.1.163:7000@17000 myself,master - 0 1524128800000 16 connected 12-6794 10923-12255
9f8311c7b0d7e4bdec43e9caa94a20bb79b31f84 47.96.162.39:7005@17005 slave b00e5ce4c4fd87224d386d131e92c149c1bfc639 0 1524128800520 17 connected