redis实战 migrate异常NOAUTH Authentication required.

当redis cluster集群设置密码的时候,使用migirate需要加上密码,否则会报如下异常:

(error) ERR Target instance replied with error: NOAUTH Authentication required.
1
redis在3.0之前是不支持migrate带有password的,在3.0之后可以在migrate中加入auth参数来做权限校验

127.0.0.1:6380> migrate 192.168.0.33 6380 “” 0 2000 auth mypassword keys user:{info}:age user:{info}:id
OK
1
2
注意不要把auth password放到最后面,否则migrate会将会auth mypassword都当做key,结果报出如下错误,该错误是说migrate要迁移的多个key不在同一个slot中

(error) CROSSSLOT Keys in request don’t hash to the same slot

(error) ERR Target instance replied with error: NOAUTH Authentication required.

Redis Cluster部署、管理和测试

背景:

Redis 3.0之后支持了Cluster,大大增强了Redis水平扩展的能力。Redis Cluster是Redis官方的集群实现方案,在此之前已经有第三方Redis集群解决方案,如Twenproxy、Codis,与其不同的是:Redis Cluster并非使用Porxy的模式来连接集群节点,而是使用无中心节点的模式来组建集群。在Cluster出现之前,只有Sentinel保证了Redis的高可用性。

Redis Cluster实现在多个节点之间进行数据共享,即使部分节点失效或者无法进行通讯时,Cluster仍然可以继续处理请求。若每个主节点都有一个从节点支持,在主节点下线或者无法与集群的大多数节点进行通讯的情况下, 从节点提升为主节点,并提供服务,保证Cluster正常运行,Redis Cluster的节点分片是通过哈希槽(hash slot)实现的,每个键都属于这 16384(0~16383) 个哈希槽的其中一个,每个节点负责处理一部分哈希槽。

环境:

Ubuntu 14.04
Redis 3.2.8
主节点:192.168.100.134/135/136:17021
从节点:192.168.100.134/135/136:17022

对应主从节点:

   主        从 
134:17021  135:17022
135:17021  136:17022
136:17021  134:17022

手动部署:

①:安装
按照Redis之Sentinel高可用安装部署文章中的说明,装好Redis。只需要修改一下Cluster相关的配置参数:

 View Code

安装好之后开启Redis:均运行在集群模式下

root@redis-cluster1:~# ps -ef | grep redis
redis      4292      1  0 00:33 ?        00:00:03 /usr/local/bin/redis-server 192.168.100.134:17021 [cluster]
redis      4327      1  0 01:58 ?        00:00:00 /usr/local/bin/redis-server 192.168.100.134:17022 [cluster]

②:配置主节点

添加节点: cluster meet ip port

复制代码
复制代码
进入其中任意17021端口的实例,进入集群模式需要参数-c:
~# redis-cli -h 192.168.100.134 -p 17021 -c
192.168.100.134:17021> cluster meet 192.168.100.135 17021
OK
192.168.100.134:17021> cluster meet 192.168.100.136 17021
OK
节点添加成功
复制代码
复制代码

查看集群状态:cluster info

复制代码
复制代码
192.168.100.134:17021> cluster info
cluster_state:fail                        #集群状态
cluster_slots_assigned:0                  #被分配的槽位数
cluster_slots_ok:0                        #正确分配的槽位             
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3                     #当前3个节点
cluster_size:0
cluster_current_epoch:2                  
cluster_my_epoch:1
cluster_stats_messages_sent:83
cluster_stats_messages_received:83
复制代码
复制代码

上面看到集群状态是失败的,原因是槽位没有分配,而且需要一次性把16384个槽位完全分配了,集群才可用。接着开始分配槽位:需要登入到各个节点,进行槽位的分配,如:
node1分配:0~5461
node2分配:5462~10922
node3分配:10923~16383

分配槽位:cluster addslots 槽位,一个槽位只能分配一个节点,16384个槽位必须分配完,不同节点不能冲突。

192.168.100.134:17021> cluster addslots 0
OK
192.168.100.135:17021> cluster addslots 0   #冲突
(error) ERR Slot 0 is already busy

目前还没有支持区间范围的添加槽位操作,所以添加16384个槽位的需要写一个批量脚本(addslots.sh):

复制代码
复制代码
node1:
#!/bin/bash
n=0
for ((i=n;i<=5461;i++))
do
   /usr/local/bin/redis-cli -h 192.168.100.134 -p 17021 -a dxy CLUSTER ADDSLOTS $i
done

node2:
#!/bin/bash
n=5462
for ((i=n;i<=10922;i++))
do
   /usr/local/bin/redis-cli -h 192.168.100.135 -p 17021 -a dxy CLUSTER ADDSLOTS $i
done

node3:
#!/bin/bash
n=10923
for ((i=n;i<=16383;i++))
do
   /usr/local/bin/redis-cli -h 192.168.100.136 -p 17021 -a dxy CLUSTER ADDSLOTS $i
done
复制代码
复制代码

连接3个节点分别执行:bash addslots.sh。所有槽位得到分配之后,在看下集群状态:

复制代码
复制代码
192.168.100.134:17021> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:3
cluster_current_epoch:2
cluster_my_epoch:1
cluster_stats_messages_sent:4193
cluster_stats_messages_received:4193
复制代码
复制代码

看到集群已经成功,那移除一个槽位看看集群会怎么样:cluster delslots 槽位

复制代码
复制代码
192.168.100.134:17021> cluster delslots 0
OK
192.168.100.134:17021> cluster info
cluster_state:fail
cluster_slots_assigned:16383
cluster_slots_ok:16383
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:3
cluster_current_epoch:2
cluster_my_epoch:1
cluster_stats_messages_sent:4482
cluster_stats_messages_received:4482
复制代码
复制代码

看到16384个槽位如果没有分配完全,集群是不成功的。 到这里为止,一个简单的Redis Cluster已经搭建完成,这里每个节点都是一个单点,若出现一个节点不可用,会导致整个集群的不可用,如何保证各个节点的高可用呢?这可以对每个主节点再建一个从节点来保证。

添加从节点(集群复制): 复制的原理和单机的Redis复制原理一样,区别是:集群下的从节点也需要运行在cluster模式下,要先添加到集群里面,再做复制。

①:添加从节点到集群中

复制代码
复制代码
192.168.100.134:17021> cluster meet 192.168.100.134 17022
OK
192.168.100.134:17021> cluster meet 192.168.100.135 17022
OK
192.168.100.134:17021> cluster meet 192.168.100.136 17022
OK
192.168.100.134:17021> 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:1
cluster_stats_messages_sent:13438
cluster_stats_messages_received:13438
复制代码
复制代码

②:创建从节点 cluster replicate node_id ,通过cluster nodes得到node_id,需要在要成为的从节点的Redis(17022)上执行。

复制代码
复制代码
192.168.100.134:17022> cluster nodes #查看节点信息
7438368ca8f8a27fdf2da52940bb50098a78c6fc 192.168.100.136:17022 master - 0 1488255023528 5 connected
e1b78bb74970d0353832b2913e9b35eba74a2a1a 192.168.100.134:17022 myself,master - 0 0 0 connected
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 master - 0 1488255022526 2 connected 10923-16383
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488255026533 3 connected 5462-10922
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 master - 0 1488255025531 1 connected 0-5461
2b8b518324de0990ca587b47f6316e5f07b1df59 192.168.100.135:17022 master - 0 1488255024530 4 connected

#成为135:17021的从节点
192.168.100.134:17022> cluster replicate b461a30fde28409c38ee6c32db1cd267a6cfd125
OK
复制代码
复制代码

处理其他2个节点:

复制代码
#成为136:17021的从节点
192.168.100.135:17022> cluster replicate 05e72d06edec6a920dd91b050c7a315937fddb66
OK
#成为134:17021的从节点
192.168.100.136:17022> cluster replicate 11f9169577352c33d85ad0d1ca5f5bf0deba3209
OK
复制代码

查看节点状态:cluster nodes

复制代码
复制代码
2b8b518324de0990ca587b47f6316e5f07b1df59 192.168.100.135:17022 slave 05e72d06edec6a920dd91b050c7a315937fddb66 0 1488255859347 4 connected
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 1 connected 0-5461
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 master - 0 1488255860348 2 connected 10923-16383
e1b78bb74970d0353832b2913e9b35eba74a2a1a 192.168.100.134:17022 slave b461a30fde28409c38ee6c32db1cd267a6cfd125 0 1488255858344 3 connected
7438368ca8f8a27fdf2da52940bb50098a78c6fc 192.168.100.136:17022 slave 11f9169577352c33d85ad0d1ca5f5bf0deba3209 0 1488255856341 5 connected
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488255857343 3 connected 5462-10922
复制代码
复制代码

可以通过查看slave对应的node_id找出它的master节点,如以上操作遇到问题可以查看/var/log/redis/目录下的日志。到此Redis Cluster分片、高可用部署完成,接着继续说明一下集群的相关管理命令。

管理:cluster xxx

上面已经介绍了一部分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,只有在节点加入集群前才能设置。
复制代码
复制代码

为了更好的展示上面命令,先为这个新集群插入一些数据:通过脚本插入:

 View Code

这里说明一下上面没有介绍过的管理命令:

①:cluster slots 列出槽位和对应节点的信息

复制代码
复制代码
192.168.100.134:17021> cluster slots
1) 1) (integer) 0
   2) (integer) 5461
   3) 1) "192.168.100.134"
      2) (integer) 17021
      3) "11f9169577352c33d85ad0d1ca5f5bf0deba3209"
   4) 1) "192.168.100.136"
      2) (integer) 17022
      3) "7438368ca8f8a27fdf2da52940bb50098a78c6fc"
2) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "192.168.100.136"
      2) (integer) 17021
      3) "05e72d06edec6a920dd91b050c7a315937fddb66"
   4) 1) "192.168.100.135"
      2) (integer) 17022
      3) "2b8b518324de0990ca587b47f6316e5f07b1df59"
3) 1) (integer) 5462
   2) (integer) 10922
   3) 1) "192.168.100.135"
      2) (integer) 17021
      3) "b461a30fde28409c38ee6c32db1cd267a6cfd125"
   4) 1) "192.168.100.134"
      2) (integer) 17022
      3) "e1b78bb74970d0353832b2913e9b35eba74a2a1a"
复制代码
复制代码

②:cluster slaves:列出指定节点的从节点

192.168.100.134:17021> cluster slaves 11f9169577352c33d85ad0d1ca5f5bf0deba3209
1) "7438368ca8f8a27fdf2da52940bb50098a78c6fc 192.168.100.136:17022 slave 11f9169577352c33d85ad0d1ca5f5bf0deba3209 0 1488274385311 5 connected"

③:cluster keyslot:列出key放在那个槽上

192.168.100.134:17021> cluster keyslot 9223372036854742675
(integer) 10310

④:cluster countkeysinslot:列出指定槽位的key数量

192.168.100.134:17021> cluster countkeysinslot 1
(integer) 19

⑤:cluster getkeysinslot :列出指定槽位中的指定数量的key

192.168.100.134:17021> cluster getkeysinslot 1 3
1) "9223372036854493093"
2) "9223372036854511387"
3) "9223372036854522344"

⑥:cluster setslot …手动迁移192.168.100.134:17021的0槽位到192.168.100.135:17021

复制代码
复制代码
1:首先查看各节点的槽位
192.168.100.134:17021> cluster nodes
2b8b518324de0990ca587b47f6316e5f07b1df59 192.168.100.135:17022 slave 05e72d06edec6a920dd91b050c7a315937fddb66 0 1488295105089 4 connected
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 7 connected 0-5461
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 master - 0 1488295107092 2 connected 10923-16383
e1b78bb74970d0353832b2913e9b35eba74a2a1a 192.168.100.134:17022 slave b461a30fde28409c38ee6c32db1cd267a6cfd125 0 1488295106090 6 connected
7438368ca8f8a27fdf2da52940bb50098a78c6fc 192.168.100.136:17022 slave 11f9169577352c33d85ad0d1ca5f5bf0deba3209 0 1488295104086 7 connected
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488295094073 6 connected 5462-10922

2:查看要迁移槽位的key
192.168.100.134:17021> cluster getkeysinslot 0 100
1) "9223372012094975807"
2) "9223372031034975807"

3:到目标节点执行导入操作
192.168.100.135:17021> cluster setslot 0 importing 11f9169577352c33d85ad0d1ca5f5bf0deba3209
OK
192.168.100.135:17021> cluster nodes
...
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 myself,master - 0 0 6 connected 5462-10922 [0-<-11f9169577352c33d85ad0d1ca5f5bf0deba3209]
...

4:到源节点进行迁移操作
192.168.100.134:17021> cluster setslot 0 migrating b461a30fde28409c38ee6c32db1cd267a6cfd125
OK
192.168.100.134:17021> cluster nodes
...
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 7 connected 0-5461 [0->-b461a30fde28409c38ee6c32db1cd267a6cfd125]
...

5:在源节点迁移槽位中的key到目标节点:MIGRATE host port key destination-db timeout [COPY] [REPLACE]
192.168.100.134:17021> migrate 192.168.100.135 17021 9223372031034975807 0 5000 replace
OK
192.168.100.134:17021> migrate 192.168.100.135 17021 9223372012094975807 0 5000 replace
OK
192.168.100.134:17021> cluster getkeysinslot 0 100     #key迁移完之后,才能进行下一步
(empty list or set)

6:最后设置槽位到指定节点,命令将会广播给集群其他节点,已经将Slot转移到目标节点
192.168.100.135:17021> cluster setslot 0 node b461a30fde28409c38ee6c32db1cd267a6cfd125
OK
192.168.100.134:17021> cluster setslot 0 node b461a30fde28409c38ee6c32db1cd267a6cfd125
OK

7:验证是否迁移成功:
192.168.100.134:17021> cluster nodes
...
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 9 connected 1-5461 #变了
...
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488300965322 10 connected 0 5462-10922

查看槽位信息:
192.168.100.134:17021> cluster slots
1) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "192.168.100.136"
      2) (integer) 17021
      3) "05e72d06edec6a920dd91b050c7a315937fddb66"
2) 1) (integer) 1
   2) (integer) 5461
   3) 1) "192.168.100.134"
      2) (integer) 17021
      3) "11f9169577352c33d85ad0d1ca5f5bf0deba3209"
3) 1) (integer) 0
   2) (integer) 0
   3) 1) "192.168.100.135"
      2) (integer) 17021
      3) "b461a30fde28409c38ee6c32db1cd267a6cfd125"
4) 1) (integer) 5462
   2) (integer) 10922
   3) 1) "192.168.100.135"
      2) (integer) 17021
      3) "b461a30fde28409c38ee6c32db1cd267a6cfd125"

查看数据是否迁移成功:
192.168.100.134:17021> cluster getkeysinslot 0 100
(empty list or set)
192.168.100.135:17021> cluster getkeysinslot 0 100
1) "9223372012094975807"
2) "9223372031034975807"
复制代码
复制代码

对于大量slot要迁移,而且slot里也有大量的key的话,可以按照上面的步骤写个脚本处理,或则用后面脚本部署里介绍的处理。

大致的迁移slot的步骤如下:

复制代码
复制代码
1,在目标节点上声明将从源节点上迁入Slot CLUSTER SETSLOT <slot> IMPORTING <source_node_id>
2,在源节点上声明将往目标节点迁出Slot CLUSTER SETSLOT <slot> migrating <target_node_id>
3,批量从源节点获取KEY CLUSTER GETKEYSINSLOT <slot> <count>
4,将获取的Key迁移到目标节点 MIGRATE <target_ip> <target_port> <key_name> 0 <timeout>
重复步骤3,4直到所有数据迁移完毕,MIGRATE命令会将所有的指定的key通过RESTORE key ttl serialized-value REPLACE迁移给target
5,分别向双方节点发送 CLUSTER SETSLOT <slot> NODE <target_node_id>,该命令将会广播给集群其他节点,取消importing和migrating。
6,等待集群状态变为OK CLUSTER INFO 中的 cluster_state = ok
复制代码
复制代码

注意:这里在操作migrate的时候,若各节点有认证,执行的时候会出现:

(error) ERR Target instance replied with error: NOAUTH Authentication required.

若确定执行的迁移,本文中是把所有节点的masterauth和requirepass注释掉之后进行的,等进行完之后再开启认证。

⑦:cluster forget:从集群中移除指定的节点,这样就无法完成握手,过期时为60s,60s后两节点又会继续完成握手。

复制代码
复制代码
192.168.100.134:17021> cluster nodes
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 master - 0 1488302330582 2 connected 10923-16383
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 9 connected 1-5461
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488302328576 10 connected 0 5462-10922
...

192.168.100.134:17021> cluster forget 05e72d06edec6a920dd91b050c7a315937fddb66
OK
192.168.100.134:17021> cluster nodes
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 9 connected 1-5461
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488302376718 10 connected 0 5462-10922
...

一分钟之后:
192.168.100.134:17021> cluster nodes
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 master - 0 1488302490107 2 connected 10923-16383
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 9 connected 1-5461
b461a30fde28409c38ee6c32db1cd267a6cfd125 192.168.100.135:17021 master - 0 1488302492115 10 connected 0 5462-10922
复制代码
复制代码

⑧:cluster failover:手动进行故障转移,在下一节会详解。需要注意的是在需要故障转移的节点上执行,必须在slave节点上执行,否则报错:

(error) ERR You should send CLUSTER FAILOVER to a slave

⑨:cluster flushslots:需要在没有key的节点执行,移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点,该节点所有数据丢失。

复制代码
复制代码
192.168.100.136:17022> cluster nodes
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 master - 0 1488255398859 2 connected 10923-16383
...

192.168.100.136:17021> cluster flushslots
OK

192.168.100.136:17021> cluster nodes
05e72d06edec6a920dd91b050c7a315937fddb66 192.168.100.136:17021 myself,master - 0 0 2 connected
...
复制代码
复制代码

⑩:cluster reset :需要在没有key的节点执行,重置集群信息。

192.168.100.134:17021> cluster reset
OK
192.168.100.134:17021> cluster nodes
11f9169577352c33d85ad0d1ca5f5bf0deba3209 192.168.100.134:17021 myself,master - 0 0 9 connected

脚本部署(redis-trib.rb)

Redis Cluster有一套管理脚本,如:创建集群、迁移节点、增删槽位等,这些脚本都存放在源码包里,都是用ruby编写的。现在测试用下脚本完成集群的部署。

①:按照需求创建Redis实例,6个实例(3主3从)。

②:安全需要ruby模块:

apt-get install ruby
gem install redis

③:脚本redis-trib.rb(/usr/local/src/redis-3.2.8/src)

复制代码
复制代码
./redis-trib.rb help
Usage: redis-trib <command> <options> <arguments ...>

#创建集群
create          host1:port1 ... hostN:portN  
                  --replicas <arg> #带上该参数表示是否有从,arg表示从的数量
#检查集群
check           host:port
#查看集群信息
info            host:port
#修复集群
fix             host:port
                  --timeout <arg>
#在线迁移slot  
reshard         host:port       #个是必传参数,用来从一个节点获取整个集群信息,相当于获取集群信息的入口
                  --from <arg>  #需要从哪些源节点上迁移slot,可从多个源节点完成迁移,以逗号隔开,传递的是节点的node id,还可以直接传递--from all,这样源节点就是集群的所有节点,不传递该参数的话,则会在迁移过程中提示用户输入
                  --to <arg>    #slot需要迁移的目的节点的node id,目的节点只能填写一个,不传递该参数的话,则会在迁移过程中提示用户输入。
                  --slots <arg> #需要迁移的slot数量,不传递该参数的话,则会在迁移过程中提示用户输入。
                  --yes         #设置该参数,可以在打印执行reshard计划的时候,提示用户输入yes确认后再执行reshard
                  --timeout <arg>  #设置migrate命令的超时时间。
                  --pipeline <arg> #定义cluster getkeysinslot命令一次取出的key数量,不传的话使用默认值为10。
#平衡集群节点slot数量  
rebalance       host:port
                  --weight <arg>
                  --auto-weights
                  --use-empty-masters
                  --timeout <arg>
                  --simulate
                  --pipeline <arg>
                  --threshold <arg>
#将新节点加入集群 
add-node        new_host:new_port existing_host:existing_port
                  --slave
                  --master-id <arg>
#从集群中删除节点
del-node        host:port node_id
#设置集群节点间心跳连接的超时时间
set-timeout     host:port milliseconds
#在集群全部节点上执行命令
call            host:port command arg arg .. arg
#将外部redis数据导入集群
import          host:port
                  --from <arg>
                  --copy
                  --replace
#帮助
help            (show this help)

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
复制代码
复制代码

1)创建集群 cretate :6个节点,每个节点一个从库,这里有个问题是不能指定那个从库属于哪个主库,不过可以先添加3个主库,通过新增节点(add-node)来添加从库到指定主库。

./redis-trib.rb create --replicas 1 192.168.100.134:17021 192.168.100.135:17021 192.168.100.136:17021 192.168.100.134:17022 192.168.100.135:17022 192.168.100.136:17022
 View Code

2)测试集群 check ip:port:测试集群是否分配完了slot

./redis-trib.rb check 192.168.100.134:17021
 View Code

3)查看集群信息 info ip:port:查看集群信息:包括slot、slave、和key的数量分布

./redis-trib.rb info 192.168.100.134:17021
 View Code

4)平衡节点的slot数量 rebalance ip:port:平均各个节点的slot数量

./redis-trib.rb rebalance 192.168.100.134:17021

流程:

 View Code

5)删除集群节点 del-node ip:port <node_id>:只能删除没有分配slot的节点,从集群中删出之后直接关闭实例

./redis-trib.rb del-node 192.168.100.135:17022 77d02fef656265c9c421fef425527c510e4cfcb8
 View Code

流程:

 View Code

6)添加集群节点 add-node :新节点加入集群,节点可以为master,也可以为某个master节点的slave。

添加一个主节点:134:17022 加入到134:17021的集群当中

./redis-trib.rb add-node 192.168.100.134:17022 192.168.100.134:17021
 View Code

添加一个从节点:135:17022加入到134:17021的集群当中,并且作为指定<node_id>的从库

./redis-trib.rb add-node --slave --master-id 7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.135:17022 192.168.100.134:17021
 View Code

最后集群的信息:

复制代码
复制代码
192.168.100.134:17021> cluster nodes
77d02fef656265c9c421fef425527c510e4cfcb8 192.168.100.135:17022 slave 7fa64d250b595d8ac21a42477af5ac8c07c35d83 0 1488346523944 5 connected
5476787f31fa375fda6bb32676a969c8b8adfbc2 192.168.100.134:17022 master - 0 1488346525949 4 connected
7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.134:17021 myself,master - 0 0 1 connected 0-5460
51bf103f7cf6b5ede6e009ce489fdeec14961be8 192.168.100.135:17021 master - 0 1488346522942 2 connected 5461-10922
0191a8b52646fb5c45323ab0c1a1a79dc8f3aea2 192.168.100.136:17021 master - 0 1488346524948 3 connected 10923-16383
复制代码
复制代码

流程:

 View Code

7)在线迁移slot reshard :在线把集群的一些slot从集群原来slot节点迁移到新的节点,即可以完成集群的在线横向扩容和缩容。

提示执行:迁移134:17021集群

./redis-trib.rb reshard 192.168.100.134:17021
复制代码
复制代码
>>> Performing Cluster Check (using node 192.168.100.134:17021)
M: 7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.134:17021
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 77d02fef656265c9c421fef425527c510e4cfcb8 192.168.100.135:17022
   slots: (0 slots) slave
   replicates 7fa64d250b595d8ac21a42477af5ac8c07c35d83
M: 5476787f31fa375fda6bb32676a969c8b8adfbc2 192.168.100.134:17022
   slots: (0 slots) master
   0 additional replica(s)
M: 51bf103f7cf6b5ede6e009ce489fdeec14961be8 192.168.100.135:17021
   slots:5461-10922 (5462 slots) master
   0 additional replica(s)
M: 0191a8b52646fb5c45323ab0c1a1a79dc8f3aea2 192.168.100.136:17021
   slots:10923-16383 (5461 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
#迁移几个槽位?
How many slots do you want to move (from 1 to 16384)? 1 
#迁移到那个node_id?
What is the receiving node ID? 5476787f31fa375fda6bb32676a969c8b8adfbc2
#从哪些node_id迁移?
Please enter all the source node IDs.
#输入all,集群里的所有节点
  Type 'all' to use all the nodes as source nodes for the hash slots.
#输入源节点,回车后再输入done开始迁移
  Type 'done' once you entered all the source nodes IDs.
Source node #1:7fa64d250b595d8ac21a42477af5ac8c07c35d83
Source node #2:done

Ready to move 1 slots.
  Source nodes:
    M: 7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.134:17021
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
  Destination node:
    M: 5476787f31fa375fda6bb32676a969c8b8adfbc2 192.168.100.134:17022
   slots: (0 slots) master
   0 additional replica(s)
  Resharding plan:
    Moving slot 0 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
#是否看迁移计划?
Do you want to proceed with the proposed reshard plan (yes/no)? yes 
Moving slot 0 from 192.168.100.134:17021 to 192.168.100.134:17022: ..........
复制代码
复制代码

参数执行:从from指定的node迁移10个slots到to指定的节点

./redis-trib.rb reshard --from 7fa64d250b595d8ac21a42477af5ac8c07c35d83 --to 5476787f31fa375fda6bb32676a969c8b8adfbc2 --slots 10 192.168.100.134:17021
复制代码
复制代码
>>> Performing Cluster Check (using node 192.168.100.134:17021)
M: 7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.134:17021
   slots:2-5460 (5459 slots) master
   1 additional replica(s)
S: 77d02fef656265c9c421fef425527c510e4cfcb8 192.168.100.135:17022
   slots: (0 slots) slave
   replicates 7fa64d250b595d8ac21a42477af5ac8c07c35d83
M: 5476787f31fa375fda6bb32676a969c8b8adfbc2 192.168.100.134:17022
   slots:0-1 (2 slots) master
   0 additional replica(s)
M: 51bf103f7cf6b5ede6e009ce489fdeec14961be8 192.168.100.135:17021
   slots:5461-10922 (5462 slots) master
   0 additional replica(s)
M: 0191a8b52646fb5c45323ab0c1a1a79dc8f3aea2 192.168.100.136:17021
   slots:10923-16383 (5461 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ready to move 10 slots.
  Source nodes:
    M: 7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.134:17021
   slots:2-5460 (5459 slots) master
   1 additional replica(s)
  Destination node:
    M: 5476787f31fa375fda6bb32676a969c8b8adfbc2 192.168.100.134:17022
   slots:0-1 (2 slots) master
   0 additional replica(s)
  Resharding plan:
    Moving slot 2 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 3 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 4 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 5 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 6 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 7 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 8 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 9 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 10 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
    Moving slot 11 from 7fa64d250b595d8ac21a42477af5ac8c07c35d83
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 2 from 192.168.100.134:17021 to 192.168.100.134:17022: ....................
Moving slot 3 from 192.168.100.134:17021 to 192.168.100.134:17022: ..........
Moving slot 4 from 192.168.100.134:17021 to 192.168.100.134:17022: ..................
Moving slot 5 from 192.168.100.134:17021 to 192.168.100.134:17022: ..
Moving slot 6 from 192.168.100.134:17021 to 192.168.100.134:17022: ..
Moving slot 7 from 192.168.100.134:17021 to 192.168.100.134:17022: ...............................
Moving slot 8 from 192.168.100.134:17021 to 192.168.100.134:17022: ..........
Moving slot 9 from 192.168.100.134:17021 to 192.168.100.134:17022: ..........................
Moving slot 10 from 192.168.100.134:17021 to 192.168.100.134:17022: ........................................
Moving slot 11 from 192.168.100.134:17021 to 192.168.100.134:17022: ..........
复制代码
复制代码

流程:

 View Code

迁移后的slots分布:

复制代码
复制代码
192.168.100.135:17021> cluster nodes
5476787f31fa375fda6bb32676a969c8b8adfbc2 192.168.100.134:17022 master - 0 1488349695628 7 connected 0-11
7fa64d250b595d8ac21a42477af5ac8c07c35d83 192.168.100.134:17021 master - 0 1488349698634 1 connected 12-5460
51bf103f7cf6b5ede6e009ce489fdeec14961be8 192.168.100.135:17021 myself,master - 0 0 2 connected 5461-10922
77d02fef656265c9c421fef425527c510e4cfcb8 192.168.100.135:17022 slave 7fa64d250b595d8ac21a42477af5ac8c07c35d83 0 1488349697631 1 connected
0191a8b52646fb5c45323ab0c1a1a79dc8f3aea2 192.168.100.136:17021 master - 0 1488349696631 3 connected 10923-16383
复制代码
复制代码

新增的节点,slot分布不均匀,可以通过上面说的rebalance进行平衡slot。

这里需要注意的是:要是Redis Server 配置了认证,需要密码登入,这个脚本就不能执行了,脚本执行的Server之间都是无密码。若确定需要登陆,则:可以暂时修改成无认证状态:

复制代码
192.168.100.134:17022> config set masterauth ""  
OK
192.168.100.134:17022> config set requirepass ""
OK
#正常来讲是没有权限写入的。
#192.168.100.134:17022> config rewrite
复制代码

等到处理完毕之后,可以再把密码设置回去。到此,通过脚本部署也介绍完了,通过手动和脚本部署发现在数据迁移的时候服务器都不能设置密码,否则认证失败。在设置了认证的服务器上操作时,需要注意一下。

故障检测和转移

在上面管理中介绍过failover的命令,现在可以用这个命令模拟故障检测转移,当然也可以stop掉Redis Server来实现模拟。进行failover节点必须是slave节点,查看集群里各个节点和slave的信息:

复制代码
复制代码
192.168.100.134:17021> cluster nodes
93a030d6f1d1248c1182114c7044b204aa0ee022 192.168.100.136:17021 master - 0 1488378411940 4 connected 10923-16383
b836dc49206ac8895be7a0c4b8ba571dffa1e1c4 192.168.100.135:17022 slave 23c2bb6fc906b55fb59a051d1f9528f5b4bc40d4 0 1488378410938 1 connected
5980546e3b19ff5210057612656681b505723da4 192.168.100.134:17022 slave 93a030d6f1d1248c1182114c7044b204aa0ee022 0 1488378408935 4 connected
23c2bb6fc906b55fb59a051d1f9528f5b4bc40d4 192.168.100.134:17021 myself,master - 0 0 1 connected 0-5461
526d99b679229c8003b0504e27ae7aee4e9c9c3a 192.168.100.135:17021 master - 0 1488378412941 2 connected 5462-10922
39bf42b321a588dcd93efc4b4cc9cb3b496cacb6 192.168.100.136:17022 slave 526d99b679229c8003b0504e27ae7aee4e9c9c3a 0 1488378413942 5 connected
192.168.100.134:17021> cluster slaves 23c2bb6fc906b55fb59a051d1f9528f5b4bc40d4
1) "b836dc49206ac8895be7a0c4b8ba571dffa1e1c4 192.168.100.135:17022 slave 23c2bb6fc906b55fb59a051d1f9528f5b4bc40d4 0 1488378414945 1 connected"
复制代码
复制代码

在134:17021上模拟故障,要到该节点的从节点135:17022上执行failover,通过日志看如何进行故障转移

复制代码
复制代码
192.168.100.135:17022> cluster failover
OK
192.168.100.135:17022> cluster nodes
39bf42b321a588dcd93efc4b4cc9cb3b496cacb6 192.168.100.136:17022 slave 526d99b679229c8003b0504e27ae7aee4e9c9c3a 0 1488378807681 5 connected
23c2bb6fc906b55fb59a051d1f9528f5b4bc40d4 192.168.100.134:17021 slave b836dc49206ac8895be7a0c4b8ba571dffa1e1c4 0 1488378804675 6 connected
526d99b679229c8003b0504e27ae7aee4e9c9c3a 192.168.100.135:17021 master - 0 1488378806679 2 connected 5462-10922
5980546e3b19ff5210057612656681b505723da4 192.168.100.134:17022 slave 93a030d6f1d1248c1182114c7044b204aa0ee022 0 1488378808682 4 connected
b836dc49206ac8895be7a0c4b8ba571dffa1e1c4 192.168.100.135:17022 myself,master - 0 0 6 connected 0-5461
93a030d6f1d1248c1182114c7044b204aa0ee022 192.168.100.136:17021 master - 0 1488378809684 4 connected 10923-16383
复制代码
复制代码

通过上面结果看到从库已经提升变成了主库,而老的主库起来之后变成了从库。在日志里也可以看到这2个节点同步的过程。当然有兴趣的可以模拟一下stop的过程。

整个集群的部署、管理和测试到这里全部结束,下面附上几个生成数据的测试脚本:

①:操作集群(cluster_write_test.py)

 View Code

②:pipeline操作集群(cluster_write_pipe_test.py)

 View Code

③:操作单例(single_write_test.py)

 View Code

④:pipeline操作单例(single_write_pipe_test.py)

 View Code

总结:

Redis Cluster采用无中心节点方式实现,无需proxy代理,客户端直接与redis集群的每个节点连接,根据同样的hash算法计算出key对应的slot,然后直接在slot对应的Redis上执行命令。从CAP定理来看,Cluster支持了AP(Availability&Partition-Tolerancy),这样让Redis从一个单纯的NoSQL内存数据库变成了分布式NoSQL数据库。

参考文档:

Redis Cluster 实现介绍

Redis cluster tutorial

集群教程

Redis cluster管理工具redis-trib.rb详解

全面剖析Redis Cluster原理和应用

Redis Cluster实现原理

[ERR] ERR Target instance replied with error: NOAUTH Authentication required.

前言: 很多文章及自带管理工具都是免密码扩容,但是线上环境怎么能少了密码呢。以下为针对带密码集群扩容的探索:

概念

去中心、去中间件,各节点平等,保存各自数据和集群状态,节点间活跃互连。

传统用一致性哈希分配数据,集群用哈希槽(hash slot)分配。 算法为CRC16。

默认分配16384个slot, 用CRC16算法取模{ CRC16(key)%16384 }计算所属slot。

最少3个主节点
migrating 与 importing

默认slots分配

image.png

部署

ruby:

yum:

/opt/rh/${RUBY_VERSION}
}/root/usr/local/share/gems/gems/${GEM_REDIS_VERSION}/lib/redis/client.rb
修改 password => nil 为 password => "abc123"

编译:

创建

redis-trib.rb create --replicas 1 172.20.133.39:7701 172.20.133.39:7702 172.20.133.39:7703 172.20.133.39:7704 172.20.133.39:7705 172.20.133.39:7706
[root@asiskskek ~]# ./redis-trib.rb create --password abc123 --replicas 1 172.20.133.39:7701 172.20.133.39:7702 172.20.133.39:7703 172.20.133.39:7704 172.20.133.39:7705 172.20.133.39:7706
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
172.20.133.39:7701
172.20.133.39:7702
172.20.133.39:7703
Adding replica 172.20.133.39:7704 to 172.20.133.39:7701
Adding replica 172.20.133.39:7705 to 172.20.133.39:7702
Adding replica 172.20.133.39:7706 to 172.20.133.39:7703
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 slots) master
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
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 172.20.133.39:7701)
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   slots: (0 slots) slave
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   slots: (0 slots) slave
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   slots: (0 slots) slave
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 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@asiskskek ~]# 

维护

1. 配置优化

timeout = 3000
cluster-node-timeout 3000

慢日志记录

2. 分析统计

集群管理工具

  1. 官方自带 redis-trib.rb
  2. 半支持认证版:

下载地址: https://github.com/otherpirate/redis/blob/382867aaafeffcdd542688df49e8a1518e9738b5/src/redis-trib.rb

pr地址:
https://github.com/antirez/redis/pull/4288

redis-trib.rb command –password thepw xxxx

分析dump文件及内存使用量

https://github.com/sripathikrishnan/redis-rdb-tools

性能分析工具

https://github.com/facebookarchive/redis-faina.git

redis-cli -c -h 172.20.133.39 -p 7701 -a abc123 MONITOR | head -10000 | ./redis-faina.py –redis-version=3.0 >faina-date +"%Y%m%d_%H%M%S".txt

时间分析能力有限:由于是通过请求开始时间和下个请求开始时间间隔计算请求执行时间,并不精确,特别是请求量很少的时候

3. 命令防身:

cluster 命令

CLUSTER INFO   # 打印集群的信息
CLUSTER NODES   # 列出集群当前已知的所有节点(node),以及这些节点的相关信息。

节点

CLUSTER MEET <ip> <port>   # 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
CLUSTER FORGET <node_id>   # 从集群中移除 node_id 指定的节点。
CLUSTER REPLICATE <node_id>   # 将当前节点设置为 node_id 指定的节点的从节点。
CLUSTER SAVECONFIG   # 将节点的配置文件保存到硬盘里面。
CLUSTER ADDSLOTS <slot> [slot ...]   # 将一个或多个槽(slot)指派(assign)给当前节点。
CLUSTER DELSLOTS <slot> [slot ...]   # 移除一个或多个槽对当前节点的指派。
CLUSTER FLUSHSLOTS   # 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
CLUSTER SETSLOT <slot> NODE <node_id>   # 将槽 slot 指派给 node_id 指定的节点。
CLUSTER SETSLOT <slot> MIGRATING <node_id>   # 将本节点的槽 slot 迁移到 node_id 指定的节点中。
CLUSTER SETSLOT <slot> IMPORTING <node_id>   # 从 node_id 指定的节点中导入槽 slot 到本节点。
CLUSTER SETSLOT <slot> STABLE   # 取消对槽 slot 的导入(import)或者迁移(migrate)。

CLUSTER KEYSLOT <key>   # 计算键 key 应该被放置在哪个槽上。
CLUSTER COUNTKEYSINSLOT <slot>   # 返回槽 slot 目前包含的键值对数量。
CLUSTER GETKEYSINSLOT <slot> <count>   # 返回 count 个 slot 槽中的键。

新增

CLUSTER SLAVES node-id   # 返回一个master节点的slaves 列表
  • 查看集群节点情况
redis-cli -c -h 172.20.133.39 -p 7701 -a abc123 cluster nodes
  • 添加master节点:
redis-trib.rb add-node 172.20.133.39:7706(新节点) 172.20.133.39:7701(已有节点)
  • 添加slave节点
redis-trib.rb add-node --slave --master-id xxxxxx  172.20.133.37:7707(添加slave) 172.20.133.37:7701(已有节点)
  • 删除节点
redis-trib.rb del-node 172.20.133.37:7701(已有节点)  172.20.133.37:7707(删除节点) 
  • 迁移:
redis-trib.rb reshard 172.20.133.39:7701
4096
id
all
  • 删除槽
redis-cli -c -h 172.20.133.39 -p 7701 -a abc123 cluster delslots 5461

在线扩容

核心概念:

扩容的核心就是迁移slot并同步迁移key-value,不管是用工具还是集群命令,核心都是以下几步:

> 在迁移目的节点执行cluster setslot <slot> IMPORTING <node ID>命令,指明需要迁移的slot和迁移源节点。
> 在迁移源节点执行cluster setslot <slot> MIGRATING <node ID>命令,指明需要迁移的slot和迁移目的节点。
> 在迁移源节点执行cluster getkeysinslot获取该slot的key列表。
> 在迁移源节点执行对每个key执行migrate命令,该命令会同步把该key迁移到目的节点。
> 在迁移源节点反复执行cluster getkeysinslot命令,直到该slot的列表为空。
> 在迁移源节点和目的节点执行cluster setslot <slot> NODE <node ID>,完成迁移操作。

前提:

迁移与redis版本及是否开启认证有关:
1. 未开启认证,任何方式都可以
2. 开启认证,只有4.0.7之后的版本支持migrate auth
3. 最新稳定版4.0.9redis-trib.rb未支持migrate auth,坊间未找到支持版,这是个给redis贡献代码的机会😄
4. 有密码最简单的扩容办法就是迁移的时候去掉密码,前提是业务代码要支持,去掉密码能正常使用。想折腾就写脚本实现带密码用集群命令实现,见附4

https://github.com/antirez/redis/commit/47717222b64b9437b12d76f39ac694f7454e3c7c

本次commit之后开始支持 cluster migate auth,但是好像忘了更新redis-trib.rb,应该用不了多久就会支持

image

1. 环境:

  • Centos 6.8
  • Redis 4.0.9 —- { redis version(<4.0.7) doesn’t support migrate command in the cluster with auth mode }
  • Ruby 2.0.5
  • redis gem 3.3.3 —- { 要小于4.0 版本,否则会报:[ERR] Calling MIGRATE: ERR Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name) ,但是 v3.3.3 don’t support redis cluter with password }

2. 扩容过程:

2.1. 添加节点
  • 添加master节点:

将7707添加进集群

redis-trib.rb add-node 172.20.133.39:7707 172.20.133.39:7701
[root@asiskskek ~]# ./redis-trib.rb add-node --password abc123 172.20.133.39:7707 172.20.133.39:7701
>>> Adding node 172.20.133.39:7707 to cluster 172.20.133.39:7701
>>> Performing Cluster Check (using node 172.20.133.39:7701)
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   slots: (0 slots) slave
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   slots: (0 slots) slave
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   slots: (0 slots) slave
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 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.
>>> Send CLUSTER MEET to node 172.20.133.39:7707 to make it join the cluster.
[OK] New node added correctly.
[root@asiskskek ~]# 
  • 检查集群状态
[root@asiskskek ~]# ./redis-trib.rb check --password abc123 172.20.133.39:7701    
>>> Performing Cluster Check (using node 172.20.133.39:7701)
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   slots: (0 slots) slave
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
M: c713e819cf41b1c79faab18c93398c510dfc314d 172.20.133.39:7707
   slots: (0 slots) master
   0 additional replica(s)
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   slots: (0 slots) slave
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   slots: (0 slots) slave
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 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@asiskskek ~]#
  • 添加slave节点

将7707添加进集群作为7706的slave节点

redis-trib.rb add-node --slave --master-id 7707's-id  172.20.133.39:7708 172.20.133.39:7701
[root@asiskskek ~]# ./redis-trib.rb add-node --password abc123 --slave --master-id c713e819cf41b1c79faab18c93398c510dfc314d  172.20.133.39:7708 172.20.133.39:7701
>>> Adding node 172.20.133.39:7708 to cluster 172.20.133.39:7701
>>> Performing Cluster Check (using node 172.20.133.39:7701)
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   slots: (0 slots) slave
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
M: c713e819cf41b1c79faab18c93398c510dfc314d 172.20.133.39:7707
   slots: (0 slots) master
   0 additional replica(s)
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   slots: (0 slots) slave
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   slots: (0 slots) slave
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 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.
>>> Send CLUSTER MEET to node 172.20.133.39:7708 to make it join the cluster.
Waiting for the cluster to join.
>>> Configure node as replica of 172.20.133.39:7707.
[OK] New node added correctly.
[root@asiskskek ~]# 

2.2. 迁移槽位

如果有密码认证,先批量去掉密码,附2

redis-trib.rb reshard 172.20.133.39:7701
4096 (分配给新master的slots数)
masterid
all
[root@asiskskek ~]# ./redis-trib.rb reshard --password abc123 172.20.133.39:7701 
>>> Performing Cluster Check (using node 172.20.133.39:7701)
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   slots: (0 slots) slave
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
M: c713e819cf41b1c79faab18c93398c510dfc314d 172.20.133.39:7707
   slots: (0 slots) master
   1 additional replica(s)
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   slots: (0 slots) slave
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: d0fb6ebab8ea6d795917f5b0a385ff2736e7c9b9 172.20.133.39:7708
   slots: (0 slots) slave
   replicates c713e819cf41b1c79faab18c93398c510dfc314d
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   slots: (0 slots) slave
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 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.
How many slots do you want to move (from 1 to 16384)? 4096  <=====
What is the receiving node ID? c713e819cf41b1c79faab18c93398c510dfc314d  
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:all      <=====

Ready to move 4096 slots.
  Source nodes:
    M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
    M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
    M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
  Destination node:
    M: c713e819cf41b1c79faab18c93398c510dfc314d 172.20.133.39:7707
   slots: (0 slots) master
   1 additional replica(s)
  Resharding plan:
    Moving slot 5461 from d2f155f3ea1506c9ac26c39b78925cd31278da67
    Moving slot 5462 from d2f155f3ea1506c9ac26c39b78925cd31278da67
    ...
    Moving slot 6825 from d2f155f3ea1506c9ac26c39b78925cd31278da67
    Moving slot 6826 from d2f155f3ea1506c9ac26c39b78925cd31278da67
    Moving slot 0 from c76f037234f873f69b3dff981b82e37b7e98b7b2
    Moving slot 1 from c76f037234f873f69b3dff981b82e37b7e98b7b2
    ...
    Moving slot 1363 from c76f037234f873f69b3dff981b82e37b7e98b7b2
    Moving slot 1364 from c76f037234f873f69b3dff981b82e37b7e98b7b2
    Moving slot 10923 from 3af53debec41cf4ddace3f568538e0e5062d11a2
    Moving slot 10924 from 3af53debec41cf4ddace3f568538e0e5062d11a2
    ...
    Moving slot 12286 from 3af53debec41cf4ddace3f568538e0e5062d11a2
    Moving slot 12287 from 3af53debec41cf4ddace3f568538e0e5062d11a2
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 5461 from 172.20.133.39:7702 to 172.20.133.39:7707: 
Moving slot 5462 from 172.20.133.39:7702 to 172.20.133.39:7707: 
...
Moving slot 6825 from 172.20.133.39:7702 to 172.20.133.39:7707: 
Moving slot 6826 from 172.20.133.39:7702 to 172.20.133.39:7707: 
Moving slot 0 from 172.20.133.39:7701 to 172.20.133.39:7707: 
Moving slot 1 from 172.20.133.39:7701 to 172.20.133.39:7707: 
...
Moving slot 1363 from 172.20.133.39:7701 to 172.20.133.39:7707: 
Moving slot 1364 from 172.20.133.39:7701 to 172.20.133.39:7707: 
Moving slot 10923 from 172.20.133.39:7703 to 172.20.133.39:7707: 
Moving slot 10924 from 172.20.133.39:7703 to 172.20.133.39:7707: 
...
Moving slot 12286 from 172.20.133.39:7703 to 172.20.133.39:7707: 
Moving slot 12287 from 172.20.133.39:7703 to 172.20.133.39:7707: 
[root@asiskskek ~]# 
  • 检查集群状态
[root@asiskskek ~]# ./redis-trib.rb check --password abc123 172.20.133.39:7701        
>>> Performing Cluster Check (using node 172.20.133.39:7701)
M: c76f037234f873f69b3dff981b82e37b7e98b7b2 172.20.133.39:7701
   slots:1365-5460 (4096 slots) master
   1 additional replica(s)
M: 3af53debec41cf4ddace3f568538e0e5062d11a2 172.20.133.39:7703
   slots:12288-16383 (4096 slots) master
   1 additional replica(s)
S: 87a3f5546ed9d8000e725edc7ebd879e219d351b 172.20.133.39:7705
   slots: (0 slots) slave
   replicates d2f155f3ea1506c9ac26c39b78925cd31278da67
M: c713e819cf41b1c79faab18c93398c510dfc314d 172.20.133.39:7707
   slots:0-1364,5461-6826,10923-12287 (4096 slots) master
   1 additional replica(s)
S: 12659ccc59a3b4e28a9cbbc978f19a79fdee0fdd 172.20.133.39:7704
   slots: (0 slots) slave
   replicates c76f037234f873f69b3dff981b82e37b7e98b7b2
S: d0fb6ebab8ea6d795917f5b0a385ff2736e7c9b9 172.20.133.39:7708
   slots: (0 slots) slave
   replicates c713e819cf41b1c79faab18c93398c510dfc314d
S: c61a72b37f25542f5eaa257211a31b512d763a03 172.20.133.39:7706
   slots: (0 slots) slave
   replicates 3af53debec41cf4ddace3f568538e0e5062d11a2
M: d2f155f3ea1506c9ac26c39b78925cd31278da67 172.20.133.39:7702
   slots:6827-10922 (4096 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@asiskskek ~]# 

异常处理:

1. gem install redis

ERROR: Loading command: install (LoadError)
cannot load such file — zlib
ERROR: While executing gem … (NoMethodError)
undefined method `invoke_with_build_args’ for nil:NilClass

解决:

编译安装的ruby缺少库文件,从ruby源码编译安装:
ruby目录:
cd ext/zlib
修改Makefile:
$(top_srcdir)/include/ruby.h  -->  zlib.o: ../../include/ruby.h
ruby extconf.rb
make && make install
如果有openssl 报错,处理方法类似:
Makefile 添加 top_srcdir = ../..

2. redis-trib.rb xxx

[WARNING] Node 172.20.133.39:7701 has slots in migrating state (0).
[WARNING] Node 172.20.133.39:7709 has slots in importing state (0).
[WARNING] The following slots are open: 0

解决:

redis-cli -h 172.20.133.39 -p 7701 -a abc123 cluster setslot 0 stable
redis-cli -h 172.20.133.39 -p 7709 -a abc123 cluster setslot 0 stable

[ERR] Calling MIGRATE: ERR Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)

解决:

需小于4.0的版本

gem uninstall redis
gem install redis -v 3.3.3

3. redis-trib reshard ip:port

  • 认证失败

Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 1365 from 172.20.133.39:7701 to 172.20.133.39:7705:
[ERR] ERR Target instance replied with error: NOAUTH Authentication required.

解决:

目前只能去掉密码

redis-cli -c -h 172.20.133.39 -p $i -a abc123 config set masterauth ""
redis-cli -c -h 172.20.133.39 -p $i -a abc123 config set requirepass ""
见附2

4. redis-trib reshard –password thepw ip:port

  • 语法错误

Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 1365 from 172.20.133.39:7701 to 172.20.133.39:7705:
[ERR] Calling MIGRATE: ERR syntax error


附:

脚本:

1. 批量向redis插入数据:
    1. 单shell脚本版:
#!/bin/bash
for ((i=0;i<1000000;i++))
do
echo -en "hello" | redis-cli -c -h 172.20.133.39 -p 7701 -a coocaa set name$i $i >> keyset.log
done
    1. pipeline方式,高效版:
#!/usr/bin/python
for i in range(1000000):
    print 'set name'+str(i),'helloworld'

python 1.py > redis_commands.txt

#!/bin/bash

while read CMD; do
  # each command begins with *{number arguments in command}\r\n
  XS=($CMD); printf "*${#XS[@]}\r\n"
  # for each argument, we append ${length}\r\n{argument}\r\n
  for X in $CMD; do printf "\$${#X}\r\n$X\r\n"; done
done < redis_commands.txt

bash 2.sh > redis_data.txt

cat redis_data.txt | redis-cli -c -h 172.20.133.39 -p 7701 -a abc123 –pipe

集群需要连每个master执行一遍。

2. 批量密码操作
#!/bin/bash
for ((i=7701;i<7713;i++))
do
# 增加认证
redis-cli -c -h 172.20.133.39 -p $i -a abc123 config set masterauth abc123
redis-cli -c -h 172.20.133.39 -p $i -a abc123 config set requirepass abc123
# 取消认证
#redis-cli -c -h 172.20.133.39 -p $i -a abc123 config set masterauth ""
#redis-cli -c -h 172.20.133.39 -p $i -a abc123 config set requirepass ""
done
3.redis重启脚本
#!/bin/sh

#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.

REDISPORT=7701
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli

PIDFILE=/tmp/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF
        fi
        ;;
    stop)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                PID=$(cat $PIDFILE)
                echo "Stopping ..."
                $CLIEXEC -p $REDISPORT -a abc123 -h 172.20.133.39 shutdown
                while [ -x /proc/${PID} ]
                do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
    *)
        echo "Please use start or stop as first argument"
        ;;
esac
4.redis迁移脚本
#!/bin/bash
source_ip=$1
source_port=$2
target_ip=$3
target_port=$4
startSlot=$5
endSlot=$6
password=$7

#目标节点执行importing命令,目标节点将从源节点迁移slot

for slot in `seq ${startSlot} ${endSlot}`  
do  
    redis-cli -c -h ${target_ip} -p ${target_port} -a ${password} cluster setslot ${slot} IMPORTING `redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster nodes | grep ${source_ip} | grep ${source_port} | awk '{print $1}'`  
done

#源节点执行migrating命令,源节点将向目标节点迁移slot

for slot in `seq ${startSlot} ${endSlot}`  
do  
    redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster setslot ${slot} MIGRATING `redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster nodes | grep ${target_ip} | grep ${target_port} | awk '{print $1}'`  
done


for slot in `seq ${startSlot} ${endSlot}`  
do  
    while [ 1 -eq 1 ]  
    do  

#源节点执行getkeysinslot命令,从slot中取出count个键值对的key

        allkeys=`redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster getkeysinslot ${slot} 20`  
#        if [ !-z ${allkeys} ]  
        if [ -z "${allkeys}" ]
        then  

#源节点和目标节点执行setslot命令,将slot分配给目标节点

            redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster setslot ${slot} NODE `redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster nodes | grep ${target_ip} | grep ${target_port} | awk '{print $1}'`  
            redis-cli -c -h ${target_ip} -p ${target_port} -a ${password} cluster setslot ${slot} NODE `redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} cluster nodes | grep ${target_ip} | grep ${target_port} | awk '{print $1}'`  
            break  
        else  
            for key in ${allkeys}  
            do  
                echo "slot ${slot} key ${key}"  

#源节点执行migrate命令,将key迁移到目标节点

                redis-cli -c -h ${source_ip} -p ${source_port} -a ${password} MIGRATE ${target_ip} ${target_port} ${key} 0 5000 AUTH ${password}
            done  
        fi
    done
done

 

(error) ERR Target instance replied with error: NOAUTH Authentication required

部署架构:
192.168.65.31  M1(6379)  S2(6380)
192.168.65.32  M2(6379)  S3(6380)
192.168.65.33  M2(6379)  S1(6380)
一.数据存储测试 :
连到31的6379上操作:
redis-cli -a “abc” -h 192.168.65.31 -p 6379
192.168.65.31:6379> set b 3
OK
192.168.65.31:6379> get b
“3”
再连到192.168.65.33:6379
192.168.65.33:6379> get b
(error) MOVED 3300 192.168.65.31:6379
说明数据是随机按hash算法存储的,当一个key存储在一个节点中,而在另一个节点去查询这个key会报告key存储的目标节点。

二.高可用测试:
1.挂掉一个主库
连到从库查看:redis-cli -a “abc” -h 192.168.65.32 -p 6380
192.168.65.32:6380> randomkey
“b”
192.168.65.32:6380> get b
(error) MOVED 3300 192.168.65.31:6379
由于从库是只读的,可以看到该key,但不能获取key值。
31的6379关闭前的集群状态:
redis-trib.py list –password abc –addr  192.168.65.32:6379
Total 6 nodes, 3 masters, 0 fail
M  192.168.65.31:6379 master 5462
S 192.168.65.32:6380 slave 192.168.65.31:6379
M  192.168.65.32:6379 myself,master 5461
S 192.168.65.33:6380 slave 192.168.65.32:6379
M  192.168.65.33:6379 master 5461
S 192.168.65.31:6380 slave 192.168.65.33:6379
redis-trib.rb info 192.168.65.32:6379
出现以下报错:
[apps@0782 bin]$ ./redis-trib.rb info 192.168.65.31:6379
/usr/local/ruby/lib/ruby/site_ruby/1.9.1/rubygems/core_ext/kernel_require.rb:55:in `require’: no such file to load — redis (LoadError)
from /usr/local/ruby/lib/ruby/site_ruby/1.9.1/rubygems/core_ext/kernel_require.rb:55:in `require’
from ./redis-trib.rb:25:in `<main>’
解决:
gem install -l /apps/software/redis-3.2.2.gem
[apps@0782 bin]$ ./redis-trib.rb info 192.168.65.31:6379
[ERR] Sorry, can’t connect to node 192.168.65.31:6379
./redis-trib.rb help查看到所有命令及参数暂不支持密码,在使用方面不如redis-trib.py方便实用。
关闭31的6379:
[apps@ bin]$ ps -ef|grep redis
apps     12302     1  0 16:05 ?        00:00:22 /apps/svr/redis3.0/bin/redis-server 0.0.0.0:6379 [cluster]
apps     12398     1  0 16:28 ?        00:00:19 /apps/svr/redis3.0/bin/redis-server 0.0.0.0:6380 [cluster]
apps     15166 14995  0 22:14 pts/0    00:00:00 grep redis
[apps@bin]$ kill -9 12302
[apps@bin]$ ps -ef|grep redis
apps     12398     1  0 16:28 ?        00:00:19 /apps/svr/redis3.0/bin/redis-server 0.0.0.0:6380 [cluster]
apps     15201 14995  0 22:15 pts/0    00:00:00 grep redis
关闭31的6379后集群状态:
redis-trib.py list –password abc –addr  192.168.65.32:6379
Total 6 nodes, 4 masters, 1 fail
M  192.168.65.31:6379 master,fail 0
M  192.168.65.32:6379 myself,master 5461
S 192.168.65.33:6380 slave 192.168.65.32:6379
M  192.168.65.32:6380 master 5462
M  192.168.65.33:6379 master 5461
S 192.168.65.31:6380 slave 192.168.65.33:6379
上面说明从库有自动切换为主库。
现在可以成功查看key b的值:
192.168.65.32:6380> get b
“3”
再将31的6379启动后,查看集群状态:
Total 6 nodes, 3 masters, 0 fail
M  192.168.65.32:6379 myself,master 5461
S 192.168.65.33:6380 slave 192.168.65.32:6379
M  192.168.65.32:6380 master 5462
S 192.168.65.31:6379 slave 192.168.65.32:6380
M  192.168.65.33:6379 master 5461
S 192.168.65.31:6380 slave 192.168.65.33:6379
发现6379为新主库的从库。
说明在从库正常情况下,挂掉一个主库后,从库会被选举为新的主库。

2.挂掉两个主库
将32和33的6379关闭:
约5分钟后状态如下:
redis-trib.py list –password abc –addr  192.168.65.31:6379
Total 6 nodes, 5 masters, 2 fail
M  192.168.65.31:6380 master 5461
M  192.168.65.32:6379 master,fail 0
M  192.168.65.32:6380 myself,master 5462
S 192.168.65.31:6379 slave 192.168.65.32:6380
M  192.168.65.33:6379 master,fail 0
M  192.168.65.33:6380 master 5461
启动两个主库后:
redis-trib.py list –password abc –addr  192.168.65.31:6379
Total 6 nodes, 3 masters, 0 fail
M  192.168.65.31:6380 master 5461
S 192.168.65.33:6379 slave 192.168.65.31:6380
M  192.168.65.32:6380 master 5462
S 192.168.65.31:6379 myself,slave 192.168.65.32:6380
M  192.168.65.33:6380 master 5461
S 192.168.65.32:6379 slave 192.168.65.33:6380
说明在从库正常情况下,挂掉两个主库后,从库会被选举为新的主库。

3.挂掉一台主机(挂掉一主一从)
ps -ef|grep redis|grep -v grep|awk ‘{print $2}’|xargs kill -9
状态如下:
redis-trib.py list –password abc –addr  192.168.65.32:6379
Total 6 nodes, 4 masters, 2 fail
M  192.168.65.31:6380 master,fail 0
M  192.168.65.32:6380 master 5462
S 192.168.65.31:6379 slave,fail 192.168.65.32:6380
M  192.168.65.33:6379 master 5461
M  192.168.65.33:6380 master 5461
S 192.168.65.32:6379 myself,slave 192.168.65.33:6380
说明在从库正常情况下,挂掉一台主机后,从库会被选举为新的主库。
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:10
cluster_my_epoch:7
cluster_stats_messages_sent:464420
cluster_stats_messages_received:47526
info cluster
# Cluster
cluster_enabled:1

4.挂掉两台主机(6个节点挂掉二主二从)
ps -ef|grep redis|grep -v grep|awk ‘{print $2}’|xargs kill -9
Total 6 nodes, 3 masters, 4 fail
M  192.168.65.31:6380 master,fail? 5461
S 192.168.65.33:6379 slave 192.168.65.31:6380
M  192.168.65.32:6379 master,fail? 5461
S 192.168.65.33:6380 myself,slave 192.168.65.32:6379
M  192.168.65.32:6380 master,fail? 5462
S 192.168.65.31:6379 slave,fail? 192.168.65.32:6380
192.168.65.33:6380> get a
(error) CLUSTERDOWN The cluster is down

5.挂掉两台主机(9个节点挂掉二主二从)
增加实例到集群中:
原部署架构:
192.168.65.31  M1(6379)  S2(6380)
192.168.65.32  M2(6379)  S3(6380)
192.168.65.33  M2(6379)  S1(6380)
改成如下:
部署架构:
192.168.65.31  M1(6379)  S2(6380) S3(6381)
192.168.65.32  M2(6379)  S3(6380) S1(6381)
192.168.65.33  M2(6379)  S1(6380) S2(6381)
在原来三主三从的架构上变成三主六从,添加三个从库后的cluster状态:
redis-trib.py list –password abc –addr  192.168.65.32:6379
Total 9 nodes, 3 masters, 0 fail
M  192.168.65.31:6380 master 5461
S 192.168.65.32:6381 slave 192.168.65.31:6380
S 192.168.65.33:6379 slave 192.168.65.31:6380
M  192.168.65.32:6380 master 5462
S 192.168.65.31:6379 slave 192.168.65.32:6380
S 192.168.65.33:6381 slave 192.168.65.32:6380
M  192.168.65.33:6380 master 5461
S 192.168.65.31:6381 slave 192.168.65.33:6380
S 192.168.65.32:6379 myself,slave 192.168.65.33:6380
上面是很明显的一个主库带两个从库,而且三台机是相互交叉分布的。
挂掉两台机后,如下状态:
Total 9 nodes, 3 masters, 6 fail
M  192.168.65.31:6380 master,fail? 5461
S 192.168.65.32:6381 slave,fail? 192.168.65.31:6380
S 192.168.65.33:6379 myself,slave 192.168.65.31:6380
M  192.168.65.32:6380 master,fail? 5462
S 192.168.65.31:6379 slave,fail? 192.168.65.32:6380
S 192.168.65.33:6381 slave 192.168.65.32:6380
M  192.168.65.33:6380 master 5461
S 192.168.65.31:6381 slave,fail? 192.168.65.33:6380
S 192.168.65.32:6379 slave,fail? 192.168.65.33:6380
192.168.65.33:6380> get a
(error) CLUSTERDOWN The cluster is down

三.数据迁移测试
192.168.65.32:6380> info cluster
# Cluster
cluster_enabled:1
192.168.65.32:6380> cluster node
(error) ERR Wrong CLUSTER subcommand or number of arguments
192.168.65.32:6380> cluster nodes
b06da1f508686c326b8c65856c680ee47cdd7582 192.168.65.31:6380 master – 0 1505900948523 9 connected 10923-16383
c3af50d219c8bfbe05cd5d20cfcb78234c90faa7 192.168.65.31:6379 slave cd798125b14aafe82d7d5e3d68a2b5014a9e7dfc 0 1505900954550 6 connected
edc8139c2dff1628e94ec0a4a93d3536b3ca4440 192.168.65.32:6379 master,fail – 1505899972878 1505899967967 0 disconnected
cd798125b14aafe82d7d5e3d68a2b5014a9e7dfc 192.168.65.32:6380 myself,master – 0 0 6 connected 0-5461
4868d82eaed0fea4983609c82933e5d6a883e782 192.168.65.33:6379 master,fail – 1505900006588 1505900005081 1 disconnected
6172b5c24acc213ba6b0a983f3f497cc0658b6cd 192.168.65.33:6380 master – 0 1505900953545 7 connected 5462-10922
192.168.65.32:6380> 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:9
cluster_my_epoch:6
cluster_stats_messages_sent:403235
cluster_stats_messages_received:39290
cluster slots
1) 1) (integer) 10923
2) (integer) 16383
3) 1) “192.168.65.31”
2) (integer) 6380
3) “b06da1f508686c326b8c65856c680ee47cdd7582”
2) 1) (integer) 0
2) (integer) 5461
3) 1) “192.168.65.32”
2) (integer) 6380
3) “cd798125b14aafe82d7d5e3d68a2b5014a9e7dfc”
4) 1) “192.168.65.31”
2) (integer) 6379
3) “c3af50d219c8bfbe05cd5d20cfcb78234c90faa7”
3) 1) (integer) 5462
2) (integer) 10922
3) 1) “192.168.65.33”
2) (integer) 6380
3) “6172b5c24acc213ba6b0a983f3f497cc0658b6cd”

如何查看键放在哪些slot上
192.168.65.32:6380> keys *
1) “aaaa”
2) “b”
192.168.65.32:6380> cluster keyslot b
(integer) 3300
将3300号slot从192.168.65.32:6380迁移到192.168.65.33:6380:
a.在192.168.65.33:6380上执行cluster setslot 3300 importing cd798125b14aafe82d7d5e3d68a2b5014a9e7dfc (run ID)
b.在192.168.65.32:6380上执行cluster setslot 3300 migrating 6172b5c24acc213ba6b0a983f3f497cc0658b6cd (run ID)
c.在192.168.65.32:6380上执行cluster getkeysinslot 3300 3(返回键的数量)
192.168.65.32:6380> cluster getkeysinslot 3300 3
1) “b”
d.将第c步获取的每个键执行migrate操作:
migrate 192.168.65.33 6380 b 0 1599 replace
注意:这里在操作migrate的时候,若各节点有认证,执行的时候会出现:
(error) ERR Target instance replied with error: NOAUTH Authentication required.
若确定执行的迁移,把所有节点的masterauth和requirepass注释掉之后进行的,等进行完之后再开启认证。
e.执行cluster setslot 3300 node 6172b5c24acc213ba6b0a983f3f497cc0658b6cd
如何确认键是在哪个节点上
192.168.65.33:6380> get b
(error) MOVED 3300 192.168.65.32:6380
上面表示key b是在192.168.65.32:6380的3300 slot中。
客户端支持自动重定向:
redis-cli -h 192.168.65.33 -p 6380 -c -a abc
192.168.65.33:6380> get b
-> Redirected to slot [3300] located at 192.168.65.32:63802″
如果在至少负责一个slot的主库下线且 没有相应的从库可以故障恢复,则整个cluster会下线无法工作,如果想让cluster能正常工作,可以更改:
cluster-require-full-coverage为no(默认为Yes)。
———————
版权声明:本文为CSDN博主「zengxuewen2045」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zengxuewen2045/article/details/78065160

redis集群密码设置

redis集群密码设置

1、密码设置(推荐)
方式一:修改所有Redis集群中的redis.conf文件加入: 

masterauth passwd123 
requirepass passwd123

说明:这种方式需要重新启动各节点

方式二:进入各个实例进行设置:

./redis-cli -c -p 7000 
config set masterauth passwd123 
config set requirepass passwd123 
config rewrite

之后分别使用./redis-cli -c -p 7001,./redis-cli -c -p 7002…..命令给各节点设置上密码。

注意:各个节点密码都必须一致,否则Redirected就会失败, 推荐这种方式,这种方式会把密码写入到redis.conf里面去,且不用重启。

用方式二修改密码,./redis-trib.rb check 10.104.111.174:6379执行时可能会报[ERR] Sorry, can’t connect to node 10.104.111.174:6379,因为6379的redis.conf没找到密码配置。

2、设置密码之后如果需要使用redis-trib.rb的各种命令
如:./redis-trib.rb check 127.0.0.1:7000,则会报错ERR] Sorry, can’t connect to node 127.0.0.1:7000
解决办法:vim /usr/local/rvm/gems/ruby-2.3.3/gems/redis-4.0.0/lib/redis/client.rb,然后修改passord

复制代码
class Client
    DEFAULTS = {
      :url => lambda { ENV["REDIS_URL"] },
      :scheme => "redis",
      :host => "127.0.0.1",
      :port => 6379,
      :path => nil,
      :timeout => 5.0,
      :password => "passwd123",
      :db => 0,
      :driver => nil,
      :id => nil,
      :tcp_keepalive => 0,
      :reconnect_attempts => 1,
      :inherit_socket => false
    }
复制代码

注意:client.rb路径可以通过find命令查找:find / -name ‘client.rb’

带密码访问集群

./redis-cli -c -p 7000 -a passwd123

Redis笔记(二、Redis单实例到集群)

Redis 部署方案
单节点
默认情况下Redis 为单示例模式,安装简单,使用也便捷. 生产环境中存在单节点问题,受限于单机的内存,不具备扩展的能力。
Redis 安装
1. 下载安装包 https://redis.io 下载最最新版本
2. 解压缩 tar -zxf redis-3.0.1.tar.gz
3. cd redis-3.0.1 ;make install;
4. 修改配置文件配置守护后台运行
vi redis.conf; daemonize yes
5. 启动Redis
redis-server   /app/redis-3.0.1/redis.conf
6. 使用redis-cli 环境连接,默认的端口为6379,不修改可以不填
redis-cli  -p 6379

7. 安装完毕后可使用Java客户端实现Jedis来操作Redis.指令可参考官网文档和Jedis Api.
Redis 主从复制
预备知识
主从架构–> 主[Master],从[Slave]
基于Redis的复制特性,允许Slave节点从Master节点中精确进行数据复制,类似于关系型数据库Oracle\Mysql的数据复制.链接断开时Slave节点自动重连到Master节点,始终尝试进行精确数据复制.有以下三种复制策略.
主从节点状态良好–>主节点发送一系列的命令到从节点保持对从节点的更新[写入\过期\删除]等
主从网络中断–>从节点重新连上后先试图同步自网络断开期间错过的数据流.
局部同步不可用–>从节点要求重新同步,执行更复杂的操作,主节点创建所有数据的完整快照并发送到从节点,并在数据集更改时继续发送命令流.
Redis 默认使用异步复制,从节点异步确认接收到来自于主节点的数据.
客户端可以使用WAIT命令请求同步复制某些数据,只能确保在另一个Redis实例中有指定数量的公认副本.在故障转移期间由于不同原因造成的故障转移,或者依赖于Redis持久性的配置,仍然可能在故障期间丢失数据.
 Redis 复制采用异步方式,从节点异步的确认回复主节点同步到从节点的数据量
 一个Master可以有多个Slaves
 Slaves 节点也可以拥有Slaves节点
 Master节点上复制是非阻塞的
 复制很大程度上也是非阻塞的
 复制可用于扩展伸缩性,也可用于为只读查询提供多个Slave
 可以使用复制来避免主节点向磁盘写入数据,***   –>实验特性

主节点关闭持久化时安全复制
主从模式中建议对主从都进行持久化,当由于磁盘缓慢导致延迟的时候这是不可取的,如果对主节点采用非持久化则应该配置在机器重启后避免主节点自动重启.
以下故障模式,数据将从主从以及所有从节点中删除
A作为主节点关闭其持久化,B、C作为A的从节点.
节点A奔溃,系统重启后A节点配置了自动重启,由于关闭了持久化,导致A节点以一个空的数据集启动.
节点B\C从A节点复制数据,从而销毁自己的数据副本.数据丢失!
复制的工作原理
Master节点存储了副本的ID,一个大的随机字符串,用于标记数据集发送情况,主节点持有发送给从节点的复制流的偏移,主节点将对新数据集的修改更新到从节点,偏移量由主节点进行维护,偏移量保持递增的   –>  [Replication ID, offset]
定义主节点数据集的版本
Slave连接到Master时使用自己的PSYNC命令发送旧的在Master中的复制ID,和自己当前消费的偏移量,通过这种方式Master只需发送所需的增量部分,如果Master缓存区没有足够的backlog,或者Slave引用了一个过期的偏移,这种情况下从头开始获取数据集的完整副本.
完整的数据同步过程:
Master启动一个后台进程将数据写入并生成RDB文件,并开始缓存从客户端收到的所有的写命令,后台进程保存完成后,Master将数据库文件传输给Slave节点,Slave将数据保存在磁盘上加载到内从中.Master向Slave发送缓存的命令,复制正常运行.
无磁盘复制
2.8.18后的版本支持直接将RDB文件发送到Slave节点从而绕靠缓慢的磁盘同步.
见redis.conf配置
主从复制配置
在Slave节点中配置 slaveof 192.168.1.1 6379
其它参数见redis.conf说明
只读的副本
默认情况下副本为只读的 –>slave-read-only 选项控制 –>可以通过进一步的禁用Redis命令来提高只读的安全性[网络不一定被禁用–>Redis-cli可访问].
可写的Slave是本地化的,其写指令不具备传播性.
Master-Slave权限验证
Master可要求Slave认证
只在满足最小副本数时写
Redis2.8+可以配置只有至少N个Slave节点连接到主服务器的情况下才允许Master执行写操作.
原理
Reids 每秒向Master服务器发送ping信号,并确认复制流的数量.
Master记录Slave发送ping信号的时间作为最后的心跳时间.
用户可配置最小Master数量,以及不得大于的最大延迟时间
min-slaves-to-write –>Master写要求的最小Slaves数量
min-slaves-max-lag –>Slave ping请求的最大延迟时间
Redis Replication 处理Keys过期
Keys的过期依赖于节点计算过期时间的能力.即使使用Lua 脚本修改这些Keys Redis也可以正确的处理Keys的过期
Reidis 不依赖于Master\Slave的时钟同步
1. Slaves 不过期keys,相反等待master将keys过期,当Master中的一个key过期[或者被LRU淘汰],会生成一个DEL 命令发送给所有的Slaves.
2. Master不能实时提供DEL 指令,Slaves使用自己的逻辑时钟检查并为只读操作报告Key并不存在,这样不违反数据一致性[Slave随后将收到DEL指令],通过这种逻辑,Slave避免过期的key任然存在并被客户端感知。
3. Lua Scripting 执行期间不执行任何keys过期操作,Lua 运行期间Master中时间被冻结,给定的key只处于存在和不存在状态,Lua脚本被完全复制到Slaves中执行.
如果一个Slave被提升为Master,将开始独立过期Keys,不在依赖于Master
Info & Role
info –>info [replication] –>显示复制相关的信息
rele –>显示当前节点的角色信息
Redis 持久化
Reids为内从数据库,运行时数据被加载到内存中加速读写速度,Redis提供了一定的策略用于数据的持久化,防止数据丢失.
 RDB在指定时间间隔持续性的创建数据集在指定时间点的快照
 AOF[Append-only file]记录服务记录接收到的每一个写操作,在服务意外重启后进行数据集重建.AOF记录的格式为Redis协议相同–>数据量非常大的情况下快速重建数据集
 Redis 允许禁用持久性,
 同一Reids示例,可以结合RDB和AOF,这种情况下AOF文件将被用来重建完整数据集 –>AOF文件保存的是最完整的
RDB优势
 非常紧凑的单文件时间节点表示,指定时间间隔进行数据快照.
 非常适于灾备恢复,将单一压缩文件传输到远程数据中心.
 使Redis性能最大化,快照由独立线程维护
 允许更快速的重新启动大型集群
RDB劣势
 不能将数据丢失的几率降低到0,
 需要子进程fork()完成快照任务,数据集很大的话可能会耗费大量时间
AOF优势
 使Redis持久化更有保证,可以有不同的同步策略,[不同步,每秒,每次查询],默认每秒同步一次到磁盘,刷新使用后台线程执行,不影响写入,默认的配置最多损失1S的写入.
 AOF是附加日志文件,如果断电不用查找,不会出现损坏问题,Redis提供了AOF文件修复工具,
 当AOF文件过大的时候Redis会自动重写它,重写是完全安全的,Redis 继续追加到旧文件,会创建一个能容下当前数据集的最小文件,一旦第二个文件准备好Redis交换两文件.开始追加到新的文件.
 AOF易于理解,包含了一个一个的操作日志.AOF文件可以被导出.
AOF缺点
对于同一数据集,AOF通常被RDB大
按精确的语意AOF比RDB慢
罕见的BUG[BRPOPLPUSH导致AOF产生不能在重新加载时复制完全相同的数据集]
快照
默认情况下Reids的快照文件名为dump.rdb,可以配置Redis在N秒有M个变化就执行快照.或者手动调用SAVA \BGSAVE 命令
fork()子进程–>写新的快照文件–>替换旧的快照文件
追加文件
完全持久化的保证,使用一下配置启用
appendonly yes
日志重写
Redis支持在不中断响应的情况下重写AOF日志文件,2.4+可自动触发日志重写.
Redis可配置AOF缓存刷新到磁盘的策略:
每个命令都刷新到磁盘 –> Very very slow  very safe.
每秒刷新一次 –> 足够块,灾难时可能丢失1S的数据
从不刷新 –>
AOF文件修复
创建AOF文件的备份,–> redis-check-aof –fix 修复–> diff -u 查看区别 –>使用修复的文件重启集群
日志重写工作原理
fork()子进程
子进程写新的AOF文件到缓存目录,
父进程在内存中缓存所有新的更改,(同时在旧文件总写入更改)
子进程重写完成后,父进程收到信号,在新的文件末尾追加缓冲区的内容
重写完成,开始将新数据添加到新文件中.
备份Redis 数据
直接定期的备份RDB文件即可.
灾备
将RDB定期上传云存储
定期上传远端存储
Redis HA
Reids Sentinel(哨兵)用来为Redis提供高可用特性,Sentinel还提供了监控\通知\为客户端提供配置等特性.
 监控–> Sentinel不断检查Redis Master实例的工作状态
 通知–>Sentinel通过API方式通知系统管理员\其它的计算机程序–>Redis实例故障
 自动故障转移–>Master节点故障后,Sentinel可以启动故障转移过程,将一个Slave提升为Master,其它的Slave也修改使用行的Master来复制数据,并且告知使用Redis服务期的应用程序使用新的地址.
 提供配置–>充当发现客户端的角色,客户端连接到Sentinel–>请求当前Redis Master节点的地址,如果发生故障转移将报告客户端新的Master地址。
Sentinel 分布式特性
Redis Sentinel 是一个分布式系统,Sensinel可以同时运行多个实例之间互相协调
多个Sensinel 同时确认一个给定的Master是否故障,确认后执行故障检测–>降低误报概率
即使不是所有的Sentinel都正常工作,也可以使系统更强壮
Sentinel入门
基础知识
1. Sentinel支持分布式部署,使用最好三个以上的Sentinel节点
2. Sentinel应该以独立的虚拟机或者计算机进行隔离 –>独立失败
3. Sentinel +Redis 分布式部署不能保证在故障期间保留Master已经确认的写入,由于Redis使用异步复制
4. 客户端需要支持Sentinel连接
5. 配置需要在开发环境中配置测试好在上线
6. 涉及Docker, 或者其它网络地址转换或端口映射的时候需要谨慎配置
创建Sentinel
最新版本为Sentinel2 –>使用更强大和简单的预测算法重写最初的Sentinel实现.
使用2.8+版本.
运行Sentinel
两种启动方式
redis-sentinel /path/to/sentinel.conf –>
redis-server /path/to/sentinel.conf –sentinel –>
Sentinel运行时必须使用自己的配置文件,默认情况下Sentinel监听26379端口,
配置Sentinel
参考Reids提供的默认的Sentinel的配置文件sentinel.conf
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
指定监控的Master,给不同的Master一个指定的名字,不需要配置Slave节点[由Sentinel自动发现]
Sentinel自动发现并更新配置,并提供相关Slave的信息(重启时保留),每次故障转移一个Slave被提升为Master,每发现一个新的哨兵都会重写配置文件.
配置Sentinel监控
sentinel monitor <master-group-name> <ip> <port> <quorum> –>第一个参数指定Master的名字–>第二个指定Master的IP–>第三个指定Master的端口
quorum 指的是需要判定Master不可用的Sentinel的数量,该参数只是用于检测故障,为执行故障转移其中一个Sentinel将被选举为Leader执行故障转移.Leader通过选举投票产生,负责执行故障转移的Sentinel至少要获得半数以上Senginel的投票.
其它配置
sentinel <option_name> <master_name> <option_value>,
down-after-milliseconds –>毫秒,超过该时间若收不到Master的ping信号或者收到一个错误,则Sentinel认为Master节点失败.
parallel-syncs –>设置在失败后有多少个Slave可以同步新的Master的数据
Sentinal标准配置

如上所示,有三个隔离的节点,一个节点部署Master+Sentinel,另外两节点部署Slave+Sentinel.

M1故障–>S2+S3判定M1故障–>进行故障转移–>客户端正常访问 –>可能会存在以下情况

由于网络分区M1S1被隔离,R2被提升为Master客户端C1任然和M1在同一个网络分区,C1写入的这部分数据将永久丢失. 可通过一下配置来尽可能的避免:

min-slaves-to-write 1 –>至少需要一个Slave可用
min-slaves-max-lag 10 –>Slave超时时间

Sentinal在客户端
如果只有两个节点,一个Master一个Slave,可以将Sentinel放在客户端 –>quorum = 2

Sentinel和客户端在一起,大多数客户端都能连接到Master就没什么问题,M1故障可进行自动故障转移,
Sentinel和客户端绑定在一起,客户机和M1网络断开Sentinel无法进行设置.
混合设置
如果客户端数量少于三个,可以使用混合设置.

案例

A节点–>Master+Sentinel
B 节点–>Slave+Sentinel
C 节点–>Slave+Sentinel
A配置
1. redis.conf –>
min-slaves-to-write 1
min-slaves-max-lag 10
2. sentinel.conf–>
port 26379
sentinel monitor mymaster 111.111.111.224 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 2
4. 启动Redis  &Sentinel
redis-server  /app/redis-3.0.1/redis.conf
redis-server  /app/redis-3.0.1/sentinel.conf  –sentinel 或者
redis-sentinel  /app/redis-3.0.1/sentinel.conf
B配置
1. redis.conf
slaveof 111.111.111.224 6379
slave-read-only yes
2. sentinel.conf
port 26379
sentinel monitor mymaster 111.111.111.224 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 2
3. 启动Redis&Sentinal
redis-server /app/redis-3.0.1/redis.conf
redis-sentinel  /app/redis-3.0.1/sentinel.conf
C配置
1. redis.conf
slaveof 111.111.111.224 6379
slave-read-only yes
2. sentinel.conf
port 26379
sentinel monitor mymaster 111.111.111.224 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 2
3. 启动Redis&Sentinal
redis-server /app/redis-3.0.1/redis.conf
redis-sentinel  /app/redis-3.0.1/sentinel.conf
查看Sentinel状态
使用Redis-cli连接Sentinel
redis-cli  -p 26379
1. 查看指定master的状态,–> sentinel master mymaster

2. 查看Slave状态 –> SENTINEL slaves mymaster

3. 查看Sentinel状态 –> SENTINEL sentinels mymaster

4. 获取当前主机的地址 –> SENTINEL get-master-addr-by-name mymaster
5. 其它命令
redis-cli  -p 6379 DEBUG sleep 50 –>阻塞当前节点50秒

Redis 集群
3.0+
Redis集群解决方案–>数据自动分布在多个Redis节点中共享,Redis集群为Redis节点提供了一定的可用性,大多数节点不可用时集群故障.
多个节点之间自动分割数据的能力.
节点的子集或者部分出现通信故障时,能够继续操作.
Redis 集群TCP端口
每个Redis集群节点都需要打开两个TCP端口,一个用于给客户端连接[6379],和一个数据端口[连接端口+10000]用于集群的数据总线,是一个使用二进制协议的点到点的通信协议.总线用于故障检测、配置更新、故障转移授权等.
Redis 集群数据分片
Redis没有使用一致性哈希,使用”hash slot”的概念进行数据切分.Redis 集群中有16384个散列槽,Redis的键经过取模对应到16384槽中的一个上.Redis集群中的每个节点都负责Hash solt中的一个子集[0-16383].
只要所有涉及到单个命令执行(事物、Lua脚本)的键都属于同一个Hash Solt,那么Redis集群就支持多个键的操作.用户可以使用一个名为 hash tags强制多个键成为同一个Hash Solt的一部分. hash tags使用{}标识,放在key的最后部分,
Redis 集群主从模型
Reids集群使用主从复制,每个Hash Solt 使用一个Master(1)主节点 –> Slaves(N)从节点的主备模型.
Redis 集群一致性保证
Redis集群不能保证数据的强一致性,Redis集群使用异步的复制方式,允许同步复制,需要在性能和一致性之间取得平衡.
Redis 集群配置参数
cluster-enabled <yes/no> –> 是否启用Redis集群
cluster-config-file <filename> –>集群自动维护,不需要手动维护
cluster-node-timeout <milliseconds> –>集群节点最大超时时间,如果主节点超时将被Slave节点替换.
cluster-slave-validity-factor <factor> –>0,Slave将始终尝试进行故障转移对应的Master,
如果为正数用来判断slave与master断开的时间,如果为正数则Slave节点断开Master节点的时间大于节点超时时间(cluster-node-timeout)*有效因子(cluster-slave-validity-factor)的值则该节点不能被选为Master节点,防止过期的Slave节点被选为Master而导致数据丢失.
cluster-migration-barrier <count> –>Master的Slave数大于该值时,Slave才能迁移到其它节点.
cluster-require-full-coverage <yes/no> –> 默认yes,只有所有的Hash Solt都被分配出去集群才允许接受写入,no允许在Hash Solt没有完全分配时提供查询服务,只有部分键可以被处理.
创建Redis集群
以下配置一个Redis集群,建议至少使用6个节点,三个主节点每个主节点一个从节点.
#后台运行
daemonize yes
#pid文件位置
pidfile “/app/redis-3.0.1/7000/redis.pid”
#节点端口
port 7000
#节点日志文件位置
logfile “/app/redis-3.0.1/7000/7000.log”
#数据库数量
databases 1
#快照文件位置
dbfilename “7000.rdb”
#实例的工作目录
dir “/app/redis-3.0.1/7000”
#启用AOF文件
appendonly yes
#启用Redis集群模式
cluster-enabled yes
#集群节点配置文件–>不需要手动配置
cluster-config-file nodes.conf
#集群中节点超时
cluster-node-timeout 5000
下面流程在一个Linux主机上创建一个有6个节点的Redis集群.
1. mkdir 7000 7001 7002 7003 7004 7005
2. 拷贝redis.conf到7000 7001 7002 7003 7004 7005目录参考以上配置修改700*为对应的目录,其余保持一致.
3. 启动Redis节点
redis-server  /app/redis-3.0.1/7000/redis.conf
分别启动6个节点

4. 通过redis-trib 创建Redis集群,是一个Ruby程序,需要安装redis gem去运行redis-trib脚本.
gem install redis

5. 检查当前集群状态
./redis-trib.rb  check localhost:7000
当前集群16384个槽都未被分配出去

6. 创建Redis集群
./redis-trib.rb  create  –replicas  1  localhost:7000 localhost:7003 localhost:7001 localhost:7004  localhost:7002 localhost:7005

使用Redis默认的Solt分配方案,集群创建失败… 总结下不能使用主机名…

7. 删除节点,同时会将节点下线,在此加入的时候需要将nodes.conf文件清理掉! 删除7001,7003,7004,7005节点并清理对应的nodes.conf文件后重启.
redis-trib.rb del-node  localhost:7000  32607595f0e718ee97692eb762e9152d38ffbb5a
8. 添加一个节点作为指定节点的Slave,需要使用ip地址而不能使用主机名…
src/redis-trib.rb  add-node –slave –master-id 9d6ebdac22053d5923f193437aa1c8ed806d9f1d  111.111.111.224:7004 111.111.111.224:7000
9. 重复上述步骤,知道每个Master都有对应的Salve且所有Hash Solt被分配出去

重新切分集群
查看当前节点信息
redis-cli  -p 7000 cluster nodes
添加一个主节点到集群
src/redis-trib.rb  add-node –master-id e47fe240eb6aefc8fa0099d6908b6c2cbf0612a2  111.111.111.224:7005 111.111.111.224:7000
自动副本迁移
默认情况下对于一个每个Redis的每个主节点都应该有至少一个副本,当主节点异常后提升副本为主节点,如果发生副本在次异常的场景将导致集群不可用,或者使用了更多的副本,但是需要更多的资源,Redis 集群可以实现自动的副本迁移,在集群内部自动的将副本数多的节点的副本转移给副本少的节点作为副本,寻求一个平衡,遵循以下规则.
集群尝试从副本最多的主节点中迁移副本
为从中获益,只需要向集群中的几个主节点多添加几个副本即可
使用cluster-migration-barrier进行配置,主节点的备份大于该值其从节点才可以迁移为其它节点[孤立的主节点]的从节点
———————
作者:it小奋
来源:CSDN
原文:https://blog.csdn.net/u010820702/article/details/79615444
版权声明:本文为博主原创文章,转载请附上博文链接!

redis 的两种持久化方式及原理

Redis是一种高级key-value数据库。它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富。有字符串,链表,集 合和有序集合。支持在服务器端计算集合的并,交和补集(difference)等,还支持多种排序功能。所以Redis也可以被看成是一个数据结构服务 器。
Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”);也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。

 

 

 

 

第一种方法filesnapshotting:默认redis是会以快照的形式将数据持久化到磁盘的(一个二进 制文件,dump.rdb,这个文件名字可以指定),在配置文件中的格式是:save N M表示在N秒之内,redis至少发生M次修改则redis抓快照到磁盘。当然我们也可以手动执行save或者bgsave(异步)做快照。

工作原理简单介绍一下:当redis需要做持久化时,redis会fork一个子进程;子进程将数据写到磁盘上一个临时RDB文件中;当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处就是可以copy-on-write

还有一种持久化方法是Append-only:filesnapshotting方法在redis异常死掉时, 最近的数据会丢失(丢失数据的多少视你save策略的配置),所以这是它最大的缺点,当业务量很大时,丢失的数据是很多的。Append-only方法可 以做到全部数据不丢失,但redis的性能就要差些。AOF就可以做到全程持久化,只需要在配置文件中开启(默认是no),appendonly yes开启AOF之后,redis每执行一个修改数据的命令,都会把它添加到aof文件中,当redis重启时,将会读取AOF文件进行“重放”以恢复到 redis关闭前的最后时刻。

LOG Rewriting随着修改数据的执行AOF文件会越来越大,其中很多内容记录某一个key的变化情况。因此redis有了一种比较有意思的特性:在后台重建AOF文件,而不会影响client端操作。在任何时候执行BGREWRITEAOF命令,都会把当前内存中最短序列的命令写到磁盘,这些命令可以完全构建当前的数据情况,而不会存在多余的变化情况(比如状态变化,计数器变化等),缩小的AOF文件的大小。所以当使用AOF时,redis推荐同时使用BGREWRITEAOF

AOF文件刷新的方式,有三种,参考配置参数appendfsync :appendfsync always每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全;appendfsync everysec每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据;appendfsync no依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾。

可能由于系统原因导致了AOF损坏,redis无法再加载这个AOF,可以按照下面步骤来修复:首先做一个AOF文件的备份,复制到其他地方;修复原始AOF文件,执行:$ redis-check-aof –fix ;可以通过diff –u命令来查看修复前后文件不一致的地方;重启redis服务。

LOG Rewrite的工作原理:同样用到了copy-on-write:首先redis会fork一个子进程;子进程将最新的AOF写入一个临时文件;父进程 增量的把内存中的最新执行的修改写入(这时仍写入旧的AOF,rewrite如果失败也是安全的);当子进程完成rewrite临时文件后,父进程会收到 一个信号,并把之前内存中增量的修改写入临时文件末尾;这时redis将旧AOF文件重命名,临时文件重命名,开始向新的AOF中写入。

最后,为以防万一(机器坏掉或磁盘坏掉),记得定期把使用 filesnapshotting 或 Append-only 生成的*rdb *.aof文件备份到远程机器上。我是用crontab每半小时SCP一次。我没有使用redis的主从功能 ,因为半小时备份一次应该是可以了,而且我觉得有如果做主从有点浪费机器。这个最终还是看应用来定了。

 

 

========================

 

 

数据持久化通俗讲就是把数据保存到磁盘上,保证不会因为断电等因素丢失数据。

redis 需要经常将内存中的数据同步到磁盘来保证持久化。redis支持两种持久化方式,一种是 Snapshotting(快照)也是默认方式,另一种是Append-only file(缩写aof)的方式。先介绍下这两种dump方式再讲讲自己遇到的一些现象和想法,前面的内容是从网上整理出来的。

Snapshotting
快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久 化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置

save 900 1  #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000

下面介绍详细的快照保存过程

1.redis调用fork,现在有了子进程和父进程。

2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。

3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。

client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式。下面介绍

Append-only file

aof 比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)

appendonly yes              //启用aof持久化方式
# appendfsync always      //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec     //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
# appendfsync no    //完全依赖os,性能最好,持久化没保证

aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下

1. redis调用fork ,现在有父子两个进程
2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
3.父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
4.当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
5.现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

运维上的想法

其实快照和aof一样,都使用了Copy-on-write技术。多次试验发现每次做数据dump的时候,内存都会扩大一倍(关于这个问题可以参考我去年写的redis的内存陷阱,很多人用redis做为缓存,数据量小,dump耗时非常短暂,所以不太容易发现),这个时候会有三种情况:

一:物理内存足以满足,这个时候dump非常快,性能最好

二:物理内存+虚拟内存可以满足,这个时候dump速度会比较慢,磁盘swap繁忙,服务性能也会下降。所幸的是经过一段比较长的时候数据dump完成了,然后内存恢复正常。这个情况系统稳定性差。

三: 物理内存+虚拟内存不能满足,这个时候dump一直死着,时间久了机器挂掉。这个情况就是灾难!

如果数据要做持久化又想保证稳定性,建议留空一半的物理内存。如果觉得无法接受还是有办法,下面讲:

快照和aof虽然都使用Copy-on-write,但有个不同点,快照你无法预测redis什么时候做dump,aof可以通过bgrewriteaof命令控制dump的时机。

根据这点我可以在一个服务器上开启多个redis节点(利用多CPU),使用aof的持久化方式。

例 如在24G内存的服务器上开启3个节点,每天用bgrewriteaof定期重新整理数据,每个节点dump的时间都不一样,这 样理论上每个节点可以消耗6G内存,一共使用18G内存,另外6G内存在单个节点dump时用到,内存一下多利用了6G! 当然节点开的越多内存的利用率也越高。如果带宽不是问题,节点数建议 = CPU数。

我的应用里为了保证高性能,数据没有做dump,也没有用aof。因为不做dump发生的故障远远低于做dump的时候,即使数据丢失了,自动修复脚本可以马上数据恢复。毕竟对海量数据redis只能做数据分片,那么落到每个节点上的数据量也不会很多。

redis的虚拟内存建议也不要用,用redis本来就是为了达到变态的性能,虚拟内存、aof看起来都有些鸡肋。

现在还离不开redis,因为它的mget是现在所有db里性能最好的,以前也考虑过用tokyocabinet hash方式做mget,性能不给力。直接用redis,基本上单个redis节点mget可以达到10W/s

纠错

之前说过redis做数据dump的时候内容会扩大一倍,后来我又做了些测试,发现有些地方说的不对。

top 命令并不是反映真实的内存占用情况,在top里尽管fork出来的子进程占了和父进程一样的内存,但是当做dump的时候没有写操作,实际使 用的是同一份内存的数据。当有写操作的时候内存才会真实的扩大(具体是不是真实的扩大一倍不确定,可能数据是按照页分片的),这才是真正的Copy- on-write。

基于这点在做数据持久化会更加灵活。