redis-cluster介绍可以从redis中文或者英文官网中详细看到,我这里只选择我认为重点的来说
Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
HASH_SLOT = CRC16(key) mod 16384
计算哈希槽可以实现哈希标签(hash tags),但这有一个例外。哈希标签是确保两个键都在同一个哈希槽里的一种方式。将来也许会使用到哈希标签,例如为了在集群稳定的情况下(没有在做碎片重组操作)允许某些多键操作。
为了实现哈希标签,哈希槽是用另一种不同的方式计算的。基本来说,如果一个键包含一个 “{…}” 这样的模式,只有 { 和 } 之间的字符串会被用来做哈希以获取哈希槽。但是由于可能出现多个 { 或 },
计算的算法如下:
例子:
当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。
GET x
-MOVED 3999 127.0.0.1:6381
MOVED 指令的第一个参数 3999 是 key 对应的槽位编号,后面是目标节点地址。
MOVED 指令前面有一个减号,表示该指令是一个错误消息。
注意:客户端收到 MOVED 指令后,要立即纠正本地的槽位映射表。后续所有 key 将使用新的槽位映射表。
在key迁移过程中,为什么我们不能单纯地使用 MOVED 重定向呢?因为当我们使用 MOVED 的时候,意味着我们认为哈希槽永久地被另一个不同的节点处理,并且希望接下来的所有查询都尝试发到这个指定的节点上去。而 ASK 意味着我们只要下一个查询发送到指定节点上去。
这个命令是必要的,因为下一个关于哈希槽 8 的查询需要的键或许还在节点 A 中,所以我们希望客户端尝试在节点 A 中查找,如果需要的话也在节点 B 中查找。 由于这是发生在 16384 个槽的其中一个槽,所以对于集群的性能影响是在可接受的范围。
然而我们需要强制客户端的行为,以确保客户端会在尝试 A 中查找后去尝试在 B 中查找。如果客户端在发送查询前发送了 ASKING 命令,那么节点 B 只会接受被设为 IMPORTING 的槽的查询。 本质上来说,ASKING 命令在客户端设置了一个一次性标识(one-time flag),强制一个节点可以执行一次关于带有 IMPORTING 状态的槽的查询。
所以从客户端看来,ASK 重定向的完整语义如下:
一旦完成了哈希槽 8 的转移,节点 A 会发送一个 MOVED 消息,客户端也许会永久地把哈希槽 8 映射到新的 ip:端口号 上。 注意,即使客户端出现bug,过早地执行这个映射更新,也是没有问题的,因为它不会在查询前发送 ASKING 命令,节点 B 会用 MOVED 重定向错误把客户端重定向到节点 A 上。
因为 Redis Cluster 是去中心化的,一个节点认为某个节点失联了并不代表所有的节点都认为它失联了。所以集群还得经过一次协商的过程,只有当大多数节点都认定了某个节点失联了,集群才认为该节点需要进行主从切换来容错。
Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。
PFAIL状态条件:
如果以上所有条件都满足了,那么节点 A 会:
FAIL 消息会强制每个接收到这消息的节点把节点 B 标记为 FAIL 状态。
从节点的选举和提升都是由从节点处理的,主节点会投票要提升哪个从节点。一个从节点的选举是在主节点被至少一个具有成为主节点必备条件的从节点标记为 FAIL 的状态的时候发生的。
当以下条件满足时,一个从节点可以发起选举:
主节点接收到来自于从节点、要求以 FAILOVER_AUTH_REQUEST 请求的形式投票的请求。
要授予一个投票,必须要满足以下条件:
Redis 集群实现了一个叫做备份迁移(replica migration)的概念,以提高系统的可用性。在集群中有主节点-从节点的设定,如果主从节点间的映射关系是固定的,那么久而久之,当发生多个单一节点独立故障的时候,系统可用性会变得很有限。
比如:
算法具体可以参考官网。例如,如果有 10 个主节点,它们各有 1 个从节点,另外还有 2 个主节点,它们各有 5 个从节点。会尝试迁移的从节点是在那 2 个拥有 5 个从节点的主节点中的所有从节点里,节点 ID 最小的那个。
例如,假设集群有三个主节点 A,B,C。节点 A 和 B 都各有一个从节点,A1 和 B1。节点 C 有两个从节点:C1 和 C2。
备份迁移是从节点自动重构的过程,为了迁移到一个没有可工作从节点的主节点上。在上面提到的例子中,备份迁移过程如下:
从官网下载redis 6.0.6 版本的源码包,解压并且进入src,然后make,将可执行程序拷贝出来使用即可.
下载地址:redis.tar.gz
shell> cd src
shell> make
//1. 创建redis 实例
shell> mkdir -p 7000/data 7001/data 7002/data 7003/data 7004/data 7005/data
shell> cd ./7000
shell> vim redis.conf
//配置项内容
port 7000(每个节点的端口号)
daemonize yes
bind 127.0.0.1(绑定当前机器 IP)
dir /home/ubuntu/Software/redis/redis-cluster/7000/data(数据文件存放位置)
pidfile /home/ubuntu/Software/redis/redis-cluster/7000/redis_7000.pid(pid 7000和port要对应)
cluster-enabled yes(启动集群模式)
cluster-config-file nodes-7000.conf(7000和port要对应)
cluster-node-timeout 15000
appendonly yes
//2. 复制到其他文件夹,并把7000改为对应的端口号
shell> cp ./7000/redis.conf ./7001/
shell> cp ./7000/redis.conf ./7002/
shell> cp ./7000/redis.conf ./7003/
shell> cp ./7000/redis.conf ./7004/
shell> cp ./7000/redis.conf ./7005/
//vim 更改对应项
...
//3. 启动单个实例
shell> ./redis-server ../7000/redis.conf
shell> ./redis-server ../7001/redis.conf
shell> ./redis-server ../7002/redis.conf
shell> ./redis-server ../7003/redis.conf
shell> ./redis-server ../7004/redis.conf
shell> ./redis-server ../7005/redis.conf
//4. 将单点实例整合成一个redis-cluster集群
shell> ./redis-cli --cluster help
shell> ./redis-cli --cluster create --cluster-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
//如下部分内容:
>>> 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: 8fe7bc33040f230c998410804aad5939effcf37d 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: 771b4eb970a567e027fe5294075580f83a47865e 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: eb3de457df241c38da2286c5d5734600a3a5ced2 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 36b5eae11fca4f42e8c14394f0b3c33609c6309e 127.0.0.1:7003
replicates 8fe7bc33040f230c998410804aad5939effcf37d
S: 852f5790bdad1e06e795b63f5247821adbdb747d 127.0.0.1:7004
replicates 771b4eb970a567e027fe5294075580f83a47865e
S: 2154dab30331e6f5b6e0a8c43b70e98f21b54a19 127.0.0.1:7005
replicates eb3de457df241c38da2286c5d5734600a3a5ced2
...
//redis-cli 使用
shell> ./redis-cli -p 7000
127.0.0.1:7000> get foo
(error) MOVED 12182 127.0.0.1:7002
shell> ./redis-cli -p 7002
127.0.0.1:7002> get foo
(nil)
127.0.0.1:7002> set foo 1
OK
127.0.0.1:7002> get foo
"1"
//python3命令行使用展示 (ubuntu 18.04需要安装:python3-redis python3-rediscluster)
shell> python
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from rediscluster import StrictRedisCluster
>>> startup_nodes = [{"host": "127.0.0.1", "port": "7002"}]
>>> rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)
>>> print(rc.get("foo"))
1
>>> rc.set("foo","2")
True
>>> print(rc.get("foo"))
2