理论
简介
- 能够自动分割数据到不同节点上,扩展存储空间
- 能保证一定程度的可用性,当某些节点挂掉或网络不可达时,集群能继续对外提供服务。但当主节点和对应所有从节点都挂掉时,集群不可用
2个端口号
注意:redis集群需要开通2个端口,一个是对外暴露的执行redis命令的端口6379,另一个是节点间通信的内部端口,用来交换数据的,在生产环境要注意开通这2个端口的防火墙,不然集群不能正常工作。
hash槽
redis用了hash槽来实现数据分片,一共有16384个hash槽(0~16383),每个master节点分到其中一部分插槽,通过对key求CRC64校验和,然后对16384求模来得到应该存到哪个插槽。hash槽的好处是方便数据迁移,只要把hash槽对应的数据分给其他节点即可,而且迁移过程不需要停机。另外一个要注意的是,批量操作命令的key要在同一节点中,可以在存储时用大括号指定分组,k1{g1},k2{g2}
数据一致性
redis集群不保证数据的强一致性,主节点和从节点数据复制是异步的,有以下2种场景:
- 客户端写入节点A后,还没来得及同步到从节点A1,主节点A挂了,从节点切换成主节点,这时写入的数据丢失
- 发生网络分区,客户端和A在同一分区,写入A的数据会丢失。可以配置一个节点超时时间cluster-node-timeout,当某个节点网络不可达超过这个时间时,停止对外提供数据读写,避免大量数据不一致的情况
集群配置
- cluster-enabled <yes/no>:是否启用集群模式
- cluster-config-file <filename>:集群配置文件,自动生成的
- cluster-node-timeout <milliseconds>: 节点超时时间,某个节点和集群其他节点,网络不可达超过这个时间时,会被认为失败,停止对外提供数据读写
- cluster-slave-validity-factor <factor>:从节点的配置,可以为0或正数,为0 时表示主节点挂掉时,从节点可以取而代之,为正数表示不会取代
- cluster-allow-reads-when-down <yes/no>:节点失败时,是否仍然能读数据,默认no
- cluster-migration-barrier :从节点迁移有关的配置(从节点迁移),
操作流程
redis集群的部署只需要2步:
- 以集群模式启动各个redis实例
- 创建集群
集群模式启动
因为我想创建6个节点,3主3从,依次创建6个目录,如果是生产环境,主节点和从节点不能放在同一机器
drwxr-xr-x. 2 root root 42 Mar 25 14:22 7001
drwxr-xr-x. 2 root root 42 Mar 25 14:22 7002
drwxr-xr-x. 2 root root 42 Mar 25 16:10 7003
drwxr-xr-x. 2 root root 42 Mar 25 16:10 7004
drwxr-xr-x. 2 root root 42 Mar 25 16:10 7005
drwxr-xr-x. 2 root root 42 Mar 25 16:10 7006
每个目录复制配置文件,需要修改的有:端口号、pid文件、日志文件、后台启动、持久化文件路径,另外在REDIS CLUSTER部分增加如下配置:
cluster-enabled yes
cluster-config-file /opt/redis-cluster/7001/nodes.conf
cluster-node-timeout 5000
创建集群
redis 5.0以后可以直接通过redis-cli命令创建集群,redis3、4要通过一个ruby脚本
redis-cli --cluster create 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 127.0.0.1:7006 \
--cluster-replicas 1
之前已经创建过,不需要重新创建
[ERR] Node 127.0.0.1:7001 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
cluster nodes查看集群节点,会显示各个节点的node ID、IP、端口号、ping/pong的时间戳、主还是从、连接状态,插槽范围,slave节点还有对应master的nodeID
[root@localhost redis-cluster]# redis-cli -c -p 7001 cluster nodes
db05a3906426f4f18f3e91095268319aadd9cf54 127.0.0.1:7003@17003 master - 0 1616676667000 3 connected 10923-16383
1a6eb06cda839396668b351b2a21bf35a9252877 127.0.0.1:7004@17004 slave 2783fb20f0c7f4894716af706c1438dc104482cf 0 1616676667545 4 connected
08095926ce60ad74e6a47252ed85f8c63af32c1f 127.0.0.1:7002@17002 master - 0 1616676665505 2 connected 5461-10922
ae6e7e4389de5c119a4e86043652233066a5878f 127.0.0.1:7005@17005 slave 08095926ce60ad74e6a47252ed85f8c63af32c1f 0 1616676666000 5 connected
2783fb20f0c7f4894716af706c1438dc104482cf 127.0.0.1:7001@17001 myself,master - 0 1616676666000 1 connected 0-5460
79e13c5bf81b4c344fa9f5ae654e753ecad2de53 127.0.0.1:7006@17006 slave db05a3906426f4f18f3e91095268319aadd9cf54 0 1616676667034 6 connected
访问集群
-c表示集群方式访问,这样读写key时会自动重定向到对应插槽的节点
[root@localhost redis-cluster]# redis-cli -c -p 7001
127.0.0.1:7001> set k1 v1
-> Redirected to slot [12706] located at 127.0.0.1:7003
OK
127.0.0.1:7003>
集群模式下,跨slot时,批量操作key、同一事务的操作key、lua脚本不支持
127.0.0.1:7003> mset k1 v1 k2 v2 k3 v3
(error) CROSSSLOT Keys in request don't hash to the same slot
如果要批量操作,可以通过大括号指定分组,注意读写都要指定。大括号位置无所谓,比如{group1}.k1、k2{group1}、{group1}.k2,这3个key会分到相同的插槽,可以批量操作
127.0.0.1:7003> mset k1{g1} v1 k2{g1} v2 k3{g1} v3
OK
127.0.0.1:7003> MGET k1 k2 k3
(error) CROSSSLOT Keys in request don't hash to the same slot
127.0.0.1:7003> MGET k1{g1} k2{g1} k3{g1}
1) "v1"
2) "v2"
3) "v3"
集群管理命令
有2类,cluster子命令和–cluster子命令
127.0.0.1:7003> cluster help
1) CLUSTER <subcommand> arg arg ... arg. Subcommands are:
2) ADDSLOTS <slot> [slot ...] -- Assign slots to current node.
3) BUMPEPOCH -- Advance the cluster config epoch.
4) COUNT-failure-reports <node-id> -- Return number of failure reports for <node-id>.
5) COUNTKEYSINSLOT <slot> - Return the number of keys in <slot>.
6) DELSLOTS <slot> [slot ...] -- Delete slots information from current node.
7) FAILOVER [force|takeover] -- Promote current replica node to being a master.
8) FORGET <node-id> -- Remove a node from the cluster.
9) GETKEYSINSLOT <slot> <count> -- Return key names stored by current node in a slot.
10) FLUSHSLOTS -- Delete current node own slots information.
11) INFO - Return onformation about the cluster.
12) KEYSLOT <key> -- Return the hash slot for <key>.
13) MEET <ip> <port> [bus-port] -- Connect nodes into a working cluster.
14) MYID -- Return the node id.
15) NODES -- Return cluster configuration seen by node. Output format:
16) <id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ... <slot>
17) REPLICATE <node-id> -- Configure current node as replica to <node-id>.
18) RESET [hard|soft] -- Reset current node (default: soft).
19) SET-config-epoch <epoch> - Set config epoch of current node.
20) SETSLOT <slot> (importing|migrating|stable|node <node-id>) -- Set slot state.
21) REPLICAS <node-id> -- Return <node-id> replicas.
22) SLOTS -- Return information about slots range mappings. Each range is made of:
23) start, end, master and replicas IP addresses, ports and ids
计算key的插槽
127.0.0.1:7003> CLUSTER KEYSLOT k1
(integer) 12706
统计插槽对应多少个key
127.0.0.1:7003> CLUSTER COUNTKEYSINSLOT 12706
(integer) 1
获取插槽中的key
127.0.0.1:7003> CLUSTER GETKEYSINSLOT 12706 5
1) "k1"
[root@localhost ~]# redis-cli --cluster help
Cluster Manager Commands:
create host1:port1 ... hostN:portN
--cluster-replicas <arg>
check host:port
--cluster-search-multiple-owners
info host:port
fix host:port
--cluster-search-multiple-owners
reshard host:port
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
rebalance host:port
--cluster-weight <node1=w1...nodeN=wN>
--cluster-use-empty-masters
--cluster-timeout <arg>
--cluster-simulate
--cluster-pipeline <arg>
--cluster-threshold <arg>
--cluster-replace
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
del-node host:port node_id
call host:port command arg arg .. arg
set-timeout host:port milliseconds
import host:port
--cluster-from <arg>
--cluster-copy
--cluster-replace
help
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
reshard的意思是重新分片,把其他主节点的插槽重新分一部分到某个节点,其他节点可以是其他所有节点,也可以部分
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
故障转移
当某个主节点挂掉,对应从节点会成为主节点。如下7002是7005的从节点
ae6e7e4389de5c119a4e86043652233066a5878f 127.0.0.1:7005@17005 myself,master - 0 1616744708000 9 connected 0-499 5461-11422
08095926ce60ad74e6a47252ed85f8c63af32c1f 127.0.0.1:7002@17002 slave ae6e7e4389de5c119a4e86043652233066a5878f 0 1616744708783 9 connected
测试7005挂掉,直接kill也可以
127.0.0.1:7005> DEBUG SEGFAULT
Could not connect to Redis at 127.0.0.1:7005: Connection refused
7002成为主节点,插槽是原先对应主节点插槽
08095926ce60ad74e6a47252ed85f8c63af32c1f 127.0.0.1:7002@17002 master - 0 1616744732009 10 connected 0-499 5461-11422
主节点恢复后,成为新主节点的从节点。大哥东山再起,也只能当小弟了
ae6e7e4389de5c119a4e86043652233066a5878f 127.0.0.1:7005@17005 slave 08095926ce60ad74e6a47252ed85f8c63af32c1f 0 1616744870000 10 connected
节点添加和删除
添加和删除注意都要指定集群任意一个节点的IP和端口号,因为要知道是哪个集群
节点添加分为2种情况:
- 添加master,redis-cli --cluster add-node <要加的ip port> <集群任意ip port>
- 添加slave,redis-cli --cluster add-node <要加的ip port> <集群任意ip port> --cluster-slave
从节点自动迁移
当redis集群出现孤立主节点时是很脆弱的,一旦它挂了,整个集群就挂了。好消息是从节点支持自动迁移,意思是当出现孤立主节点时,其他从节点可以自动迁移给孤立节点。具体迁移哪一个可以通过cluster-migration-barrier参数配置,默认是1 ,只有数量大于1的从节点才会迁移。这意味着部署集群时,最好让一些主节点的从节点数大于1,从而使得出现孤立节点时,这些大于1的从节点可以自动迁移过来。
先试试孤立节点挂掉,整个集群是否可用。我把7002,7005干掉,在尝试获取数据:
(error) CLUSTERDOWN The cluster is down
集群添加新slave节点7007,作为7002的从节点:
redis-server 7007/redis.conf
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7003 --cluster-slave --cluster-master-id 08095926ce60ad74e6a47252ed85f8c63af32c1f
kill掉7006,此时7003成为孤立节点,过一会后,发现:
6f46d30854e1a64b8d29d478348687c25b6ef03b 127.0.0.1:7007@17007 slave db05a3906426f4f18f3e91095268319aadd9cf54 0 1616914659659 17 connected
db05a3906426f4f18f3e91095268319aadd9cf54 127.0.0.1:7003@17003 master - 0 1616914659558 17 connected 11423-16383
观察到7007自动迁移过去了