# Redis集群
# 集群介绍
Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令.
Redis 集群的优势:
- 自动分割数据到不同的节点上。
- 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
# 数据分片
Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
- 节点 1 包含 0 到 5500号哈希槽.
- 节点 2 包含5501 到 11000 号哈希槽.
- 节点 3 包含11001 到 16384号哈希槽.
这种结构很容易添加或者删除节点. 比如如果我想新添加一个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
无主模型
客户端请求任意一个节点,hash之后如果是映射到自己就直接返回数据,如果映射到不是自己就将实际存储数据的节点id返回给客户端,让客户端重定向到对应的节点取数据
每个节点都需要存储hash算法和所有节点的映射关系
数据分治很难实现事务
hashtag
{ooo}k1
{ooo}k2
#
# 主从复制模型
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.
# Redis一致性保证
Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作,原因有如下两点
集群是用了异步复制,写操作的过程如下
- 客户端向主节点B写入一条命令.
- 主节点B向客户端回复命令状态.
- 主节点将写操作复制给他得从节点 B1, B2 和 B3
集群出现了网络分区
客户端写入的节点由于网络分区已经被认为是掉线了,重新选择了主节点,从而丢失数据
# 集群的弊端
- 部分命令不可用(keys、watch)
# 集群搭建
# 官方集群支持
# 命令创建
# 创建实例
集群的最小选项配置文件
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
1
2
3
4
5cluster-enabled 选项用于开实例的集群模式
cluster-conf-file 选项则设定了保存节点配置文件的路径, 默认值为 nodes.conf.节点配置文件无须人为修改, 它由 Redis 集群在启动时创建, 并在有需要时自动进行更新
添加配置文件
mkdir cluster-test cd cluster-test mkdir 7000 7001 7002 7003 7004 7005
1
2
3在文件夹 7000 至 7005 中, 各创建一个 redis.conf 文件, 文件的内容可以使用上面示例的最少选项配置, 但记得将配置中的端口号从 7000 改为与文件夹名字相同的号码。
开启实例
实例打印的日志显示, 因为 nodes.conf 文件不存在, 所以每个节点都为它自身指定了一个新的 ID :
59834:M 09 Oct 2021 14:50:17.008 * No cluster configuration found, I'm 87dab917a8fc09c47d2582ac450c8365a40aecee
实例会一直使用同一个 ID , 从而在集群中保持一个独一无二(unique)的名字。
按照同样的方法开启剩余的5个实例
# 创建集群
通过redis-trib.rb创建集群(v5以前)
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 # --replicas 1 表示我们希望为集群中的每个主节点创建一个从节点
1
2redis-trib 位于 Redis 源码的 src 文件夹中, 它是一个 Ruby 程序, 这个程序通过向实例发送特殊命令来完成创建新集群, 检查集群, 或者对集群进行重新分片(reshared)等工作
通过redis-cli创建集群(v5及higher)
redis-cli --cluster create 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 --cluster-replicas 1 # --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点
1
2
执行过程中会展示槽位分配方案,让你选择是否按照这个方案分配,选择yes后 执行结果如下
➜ ~ redis-cli --cluster create 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 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: fd116c19cd332d72c4025f80eb8bbac90868cc9c 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: 87dab917a8fc09c47d2582ac450c8365a40aecee 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 7356a44bf9141527e851b7b447fdfc204c05774a 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 06101da098ae8d52ab31b63de44fb9c4af07e216 127.0.0.1:7003
replicates 7356a44bf9141527e851b7b447fdfc204c05774a
S: e9a610f702cde191f99484dc170fd48c5626a19e 127.0.0.1:7004
replicates fd116c19cd332d72c4025f80eb8bbac90868cc9c
S: c9d23b834961984f22e6e199afadeb0a08691a22 127.0.0.1:7005
replicates 87dab917a8fc09c47d2582ac450c8365a40aecee
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 127.0.0.1:7000)
M: fd116c19cd332d72c4025f80eb8bbac90868cc9c 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: c9d23b834961984f22e6e199afadeb0a08691a22 127.0.0.1:7005
slots: (0 slots) slave
replicates 87dab917a8fc09c47d2582ac450c8365a40aecee
S: e9a610f702cde191f99484dc170fd48c5626a19e 127.0.0.1:7004
slots: (0 slots) slave
replicates fd116c19cd332d72c4025f80eb8bbac90868cc9c
S: 06101da098ae8d52ab31b63de44fb9c4af07e216 127.0.0.1:7003
slots: (0 slots) slave
replicates 7356a44bf9141527e851b7b447fdfc204c05774a
M: 87dab917a8fc09c47d2582ac450c8365a40aecee 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 7356a44bf9141527e851b7b447fdfc204c05774a 127.0.0.1:7002
slots:[10923-16383] (5461 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.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# utils/create-cluster
#
无主模型,多节点的cluster
官方脚本:utils/create-cluster/
配置&搭建
修改配置启动脚本
vi ./create-cluster
启动
./create-cluster start
分槽位
脚本自动分槽位
./create-cluster create
➜ create-cluster ./create-cluster create >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 127.0.0.1:30005 to 127.0.0.1:30001 Adding replica 127.0.0.1:30006 to 127.0.0.1:30002 Adding replica 127.0.0.1:30004 to 127.0.0.1:30003 >>> Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: 2d552911484d6f5954f240457d876a6b5fa1b274 127.0.0.1:30001 slots:[0-5460] (5461 slots) master M: 616223b6cc540e0d25649a22b84d98b04cc0af31 127.0.0.1:30002 slots:[5461-10922] (5462 slots) master M: 1c74e104764f70555293efe4b66020e679b06eb0 127.0.0.1:30003 slots:[10923-16383] (5461 slots) master S: b7ce6fcf77fc6625c3d7d90b39e4c8e346595731 127.0.0.1:30004 replicates 616223b6cc540e0d25649a22b84d98b04cc0af31 S: 7fbcb7abf5308680549519d6c531dfee8e873685 127.0.0.1:30005 replicates 1c74e104764f70555293efe4b66020e679b06eb0 S: 13b1a1acc9ef359b72a4fd9026bb8127d3f20870 127.0.0.1:30006 replicates 2d552911484d6f5954f240457d876a6b5fa1b274 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 127.0.0.1:30001) M: 2d552911484d6f5954f240457d876a6b5fa1b274 127.0.0.1:30001 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: 13b1a1acc9ef359b72a4fd9026bb8127d3f20870 127.0.0.1:30006 slots: (0 slots) slave replicates 2d552911484d6f5954f240457d876a6b5fa1b274 M: 616223b6cc540e0d25649a22b84d98b04cc0af31 127.0.0.1:30002 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: b7ce6fcf77fc6625c3d7d90b39e4c8e346595731 127.0.0.1:30004 slots: (0 slots) slave replicates 616223b6cc540e0d25649a22b84d98b04cc0af31 M: 1c74e104764f70555293efe4b66020e679b06eb0 127.0.0.1:30003 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 7fbcb7abf5308680549519d6c531dfee8e873685 127.0.0.1:30005 slots: (0 slots) slave replicates 1c74e104764f70555293efe4b66020e679b06eb0 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51手动分
redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas
客户端连接
redis-cli -c -p 30001
关闭节点
./create-cluster stop
清理
./create-cluster clean
其他命令
redis-cli --cluster help
测试
直接连接端口redis-cli -p 30001
操作的key不在当前节点的槽位中时会报错
(error) MOVED 449 127.0.0.1:30001
可以正常开启事务,如果事务中操作的key都在同一个槽位可以成功,否则失败
(error) CROSSSLOT Keys in request don't hash to the same slot
在匹配槽位的实例上操作支持hasttag事务,但需要在指定的槽位端口上
集群方式连接redis-cli -c -p 30001
开启事务操作不同的key
操作不同端口槽位的数据时会重定向到对应的端口,再执行exec时会提示没有开启事务
(error) ERR EXEC without MULTI
支持
# 第三方代理
# predixy
# 概述
# 环境搭建
# twemproxy
# 概述
# 环境搭建
通过源码构建twemproxy
cd /opt git clone git@github.com:twitter/twemproxy.git cd twemproxy autoreconf -fvi ./configure --enable-debug=full make src/nutcracker -h
1
2
3
4
5
6
7将twemproxy配置成服务
cd /opt/twemproxy # 将官方准备的脚本拷贝到/etc/init.d中 cp scripts/nutcracker.init /etc/init.d/twemproxy cd /etc/init.d # 增加可执行权限 chmod +x twemproxy cd /opt/twemproxy mkdir /etc/nutcracker cp conf/* /etc/nutcracker
1
2
3
4
5
6
7
8
9修改配置conf/nutcracker.yml
alpha: listen: 127.0.0.1:22121 hash: fnv1a_64 distribution: ketama auto_eject_hosts: true redis: true server_retry_timeout: 2000 server_failure_limit: 1 servers: - 127.0.0.1:6379:1 - 127.0.0.1:6380:1
1
2
3
4
5
6
7
8
9
10
11启动需要代理的Redis实例
参考Redis安装与配置启动需要代理的Redis实例,如:redis_6379,redis_6380
启动twemproxy
service twemproxy start # 也可以不用将twemproxy配置成服务,直接通过可执行程序制定配置文件启动 # ./src/nutcracker -c conf/nutcracker.yml
1
2
3
# 集群使用
# redis-cli
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7002> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
2
3
4
5
6
7
8
9
10
11
12
13
# redis-rb-cluster
github:https://github.com/antirez/redis-rb-cluster (opens new window)
运行example
git clone https://github.com/antirez/redis-rb-cluster cd redis-rb-cluster ruby example.rb
1
2
3执行脚本报错
<internal:/usr/local/Cellar/ruby/3.0.2/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in
require': cannot load such file -- redis (LoadError)`原因:ruby环境未安装Redis
解决方案:gem install redis
# 集群重新分片
redis-trib.rb
redis-cli
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
1redis-