之前已经介绍了一些redis的基本特性。这里介绍下主从复制与集群工作方式。
主从复制
为保证redis的高可用性,一般都会跟其他中间件一样进行主从复制。比如kafka是把消息传递、mysql使用binlog。
既然涉及到分布式,就不得不提及CAP理论。
CAP理论有三点
- C-Consistent 一致性
- A-Availability 可用性
- P-Partition tolerance 分区容忍性
也就是说在一个分布式系统中,不能同时保证这三个特性。即,网络分区发生时,一致性和可用性两难全。
因此大多数中间件都是妥协:保证最终一致性。
简单来说同步有三种方式:
快照同步:即全量同步复制,SYNC
- 主节点进行一次BGSAVE,数据放入磁盘文件。
- 快照文件传递给从节点
- 从节点接收完快照文件后,清空自己,并进行一次全量加载
- 从节点全量加载完成后,通知主节点进行增量同步
增量同步:即PSYNC,利用复制积压缓冲区来完成的
- 主节点将修改指令存入buffer中,异步同步到从节点
- 从节点指向同步指令,并反馈偏移量
无盘复制
不生成文件的复制,其实就是把文件的生成和发送改成直接网络发送的方式。
- 主节点一边遍历内存,一边将序列号内容发送到从节点。
- 从节点收到内容存入磁盘文件,完成后,一次性加载。
无盘复制是2.8版本引入的:主服务器直接通过套接字 将快照内容发送到从节点:主节点会一边遍历内存,一遍将序 列化的内容发送到从节点。
从节点还是跟之前一样,先将接收到的内容存储到磁盘文件中, 再进行一次性加载。
Cluster
Redis将所有数据划分为16394 个 槽位,然后让每个节点负责一部分槽位。这样所有的机器就构成了一个哈希环。
槽位定位也很简单:
hash = crc32(key)
slot_index = hash % 16384
分槽就带来两个问题;请求跳转、迁移
请求跳转
客户端向错误节点发出指令,节点会向客户端返回特殊的跳转指令,并携带目标节点的地址。简单来说客户端client 向 serverA 请求 key,而key实际上再 serviceB上。此时serverA就会返回一个错误命令
-MOVED slot_num IP:port
注意:- 在redis中代表返回错误,slot_num 代表请求的key所在的槽位编号,IP:port 就是目标机器地址。
客户端收到该错误返回就会再次请求 IP:port 。
迁移
因为是集群工作方式,就会涉及扩容,即所谓的迁移。注意,这里迁移的是哈希槽,也就是说迁移的单位就是槽。一个槽一个槽的迁移。
Redis Cluster 提供 redis-trib 手动调整槽位。
迁移的单位是槽。中间过渡状态时,在原节点状态是 migrating ,在目标节点状态是 importing。
- 设置中间状态
- 利用keysinslot 获取所有的key 列表
- 每个key,原节点执行 dump 得到序列号内容,通过客户端向目标节点发送 restore指令及序列化内容
- 目标节点反序列化即可恢复,并回复 OK
- 原节点收到 OK后,再删除 key
迁移过程中,目标节点执行 restore 到 原节点收到 OK 期间,原节点的主线程处于阻塞状态。
迁移中,某客户端请求原节点时,
- 若 请求 key 存在,则直接返回
- 若不存在,则原节点返回 -ASK targetNodeAddr 的重定向指令
- 客户端收到后,先向 targetNodeAddr 发送 asking 指令,再发送执行原先的指令
因为迁移中,该槽位依旧属于原节点。不发送 asking 的话,目标节点会向客户端返回 -MOVED 重定向指令,就会形成重定向循环。
Sentinel哨兵
一般由 3~5 个节点组成,负责监视主从节点的健康。