0%

分布式系统实例之redis 集群之概述

[TOC]

关系

cluster>node(ip:port)>slot(ip:port:slot)>key

如何搭建redis集群

省略

redis集群slot

Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

如下可以看到,当执行指令 set name percy时,6381服务器返回了 MOVED 错误,并返回正确的机器地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@redis-cluster-0:/data# redis-cli -p 6381
127.0.0.1:6381> set name percy
(error) MOVED 5798 10.244.14.157:6380
127.0.0.1:6381> get name percy
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6381> get name
(error) MOVED 5798 10.244.14.157:6380
127.0.0.1:6381> exit
root@redis-cluster-0:/data# redis-cli -p 6380
127.0.0.1:6380> get name
(nil)
127.0.0.1:6380> exit
root@redis-cluster-0:/data# redis-cli -p 6380
127.0.0.1:6380> set name percy
OK

主从复制

Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。

Redis一致性保证

Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作:一个原因是因为集群是用了异步复制. 写操作过程:

  1. 客户端向主节点B写入一条命令.
  2. 主节点B向客户端回复命令状态.
  3. 主节点将写操作复制给他得从节点 B1, B2 和 B3

可以看到是异步的主从同步。

集群加入新节点

1
2
3
4
5
6
# 集群中加节点, 节点被加入集群后不会自动分配 slot, 需要手动执行指令
bin/redis-trib add-node 127.0.0.1:7006 127.0.0.1:7000

# 分配slot
/bin/redis-trib reshard 127.0.0.1:7000
# 分配 slot 时,可以选择该节点分配多少slot, 从哪里分配(all 表示全部)

MIGRATE

因为 Redis 集群目前在进行重分片的时候, 会使用 MIGRATE 命令, 将被迁移的槽包含的每个键从原节点移动到新节点, 就像这样:

1
2
for key in all_keys_in_target_slot:
使用 MIGRATE 将键 key 从当前节点(原节点)移动到新节点

并且在每个 MIGRATE 命令执行的过程中, 原节点和新节点都会被阻塞(因为原子操作), 直到命令执行完毕为止。 因此如果你直接对生产环境中的集群执行重分片操作, 而涉及该操作的两个节点正好又是被频繁访问的节点的话, 那么访问这两个节点的其他客户端就很可能会出现大量的超时错误。

客户端路由

所以,综合上述情况,客户端命令执行流程如下所示:

客户端根据本地 slot 缓存发送命令到源节点,如果存在键对应则直接执行并返回结果给客户端。
如果节点返回 MOVED 错误,更新本地的 slot 到 Redis 节点的映射关系,然后重新发起请求。
如果数据正在迁移中,节点会回复 ASK 重定向异常。格式如下: ( error ) ASK { slot } { targetIP } : { targetPort }

客户端从 ASK 重定向异常提取出目标节点信息,发送 asking 命令到目标节点打开客户端连接标识,再执行键命令。
ASK 和 MOVED 虽然都是对客户端的重定向控制,但是有着本质区别。ASK 重定向说明集群正在进行 slot 数据迁移,客户端无法知道什么时候迁移完成,因此只能是临时性的重定向,客户端不会更新 slot 到 Redis 节点的映射缓存。但是 MOVED 重定向说明键对应的槽已经明确指定到新的节点,因此需要更新 slot 到 Redis 节点的映射缓存。

P2P路由

现在我们考虑如何实现去中心化的访问,也就是说无论访问集群中的哪个节点,你都能够拿到想要的数据。其实这有点类似于路由器的路由表,具体说来就是:

  • 每个节点都保存有完整的HashSlot - 节点映射表,也就是说,每个节点都知道自己拥有哪些Slot,以及某个确定的Slot究竟对应着哪个节点。

  • 无论向哪个节点发出寻找Key的请求,该节点都会通过CRC(Key) % 16384计算该Key究竟存在于哪个Slot,并将请求转发至该Slot所在的节点(实际上不是转发,是返回error Moved)。

参考

最佳的, redis命令参考:http://redisdoc.com/index.html

整体还是可以的:https://juejin.im/entry/596343056fb9a06bc340ac15

为什么是16384:https://www.cnblogs.com/rjzheng/p/11430592.html

https://github.com/antirez/redis/issues/2576

挺好的:https://juejin.im/post/5cf7c811f265da1b7a4b6368#heading-4

重分片对 Redis 集群的性能影响分析:很不错的一篇文章:https://blog.huangz.me/2017/redis-reshard-performance-issue.html

单节点数据扩容:https://my.oschina.net/u/4399312/blog/4127334

高级开发不得不懂的Redis Cluster数据分片机制:https://www.cnblogs.com/lowmanisbusy/p/10993748.html