- 在项目中缓存是如何使用的?缓存如果使用不当会造成什么后果?
- Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?
- Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
- Redis 的过期策略都有哪些?手写一下 LRU 代码实现?
- 如何保证 Redis 高并发、高可用?Redis 的主从复制原理能介绍一下么?Redis 的哨兵原理能介绍一下么?
- Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?
- Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?
- 了解什么是 redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?
- 如何保证缓存与数据库的双写一致性?
- Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?
- 生产环境中的 Redis 是怎么部署的?
目录
Redis 主从架构
redis replication 的核心机制
redis 主从复制的核心原理
主从复制的断点续传
无磁盘化复制
过期 key 处理
复制的完整流程
数据同步相关核心机制
全量复制
增量复制
heartbeat
异步复制
redis 如何才能做到高可用
单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。
redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发
注意,如果采用了主从架构,那么建议必须开启 master node 的持久化,不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了。
另外,master 的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能确保启动的时候,是有数据的,即使采用了后续讲解的高可用机制,slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。
当启动一个 slave node 的时候,它会发送一个 PSYNC
命令给 master node。
如果这是 slave node 初次连接到 master node,那么会触发一次 full resynchronization
全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB
快照文件,同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB
文件生成完毕后, master 会将这个 RDB
发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中,接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。
从 redis2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。
master node 会在内存中维护一个 backlog,master 和 slave 都会保存一个 replica offset 还有一个 master run id,offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了,slave 会让 master 从上次 replica offset 开始继续复制,如果没有找到对应的 offset,那么就会执行一次 resynchronization
。
如果根据 host+ip 定位 master node,是不靠谱的,如果 master node 重启或者数据出现了变化,那么 slave node 应该根据不同的 run id 区分。
master 在内存中直接创建 RDB
,然后发送给 slave,不会在自己本地落地磁盘了。只需要在配置文件中开启 repl-diskless-sync yes
即可。
repl-diskless-sync yes
# 等待 5s 后再开始复制,因为要等更多 slave 重新连接过来
repl-diskless-sync-delay 5
slave 不会过期 key,只会等待 master 过期 key。如果 master 过期了一个 key,或者通过 LRU 淘汰了一个 key,那么会模拟一条 del 命令发送给 slave。
slave node 启动时,会在自己本地保存 master node 的信息,包括 master node 的host
和ip
,但是复制流程没开始。
slave node 内部有个定时任务,每秒检查是否有新的 master node 要连接和复制,如果发现,就跟 master node 建立 socket 网络连接。然后 slave node 发送 ping
命令给 master node。如果 master 设置了 requirepass,那么 slave node 必须发送 masterauth 的口令过去进行认证。master node 第一次执行全量复制,将所有数据发给 slave node。而在后续,master node 持续将写命令,异步复制给 slave node。
第一次slave连接master的时候,执行全量复制,这个过程中存在一些细节机制。
(1)master和slave都会维护一个offset
master会在自身不断累加offset,slave会在自身不断累加offset。
slave每秒会上报自己的offset给master,同时master会保存每个slave的offset。
(2)backlog
master node有一个backlog,默认1MB大小
master node给slave node复制数据时,也会将数据在backlog中同步写一份。
backlog主要是用来做全量复制中断后的增量复制。
(3)master run id
info server,可以看到master run id
如果根据host+ip定位master node,是不靠谱的,如果master node重启或者数据出现变化,那么slave node应该根据不同的run id区分,run id 不同就做全量复制。
如果需要不更改run id重启redis,可以使用redis-cli debug reload命令。
(4)psync
slave发送该psync runid offset给master进行复制,master检查后响应,根据runid和offset判断触发full resynchronization或者continue触发增量复制。
client-output-buffer-limit slave 256MB 64MB 60
主从节点互相都会发送 heartbeat 信息。
master 默认每隔 10秒 发送一次 heartbeat,slave node 每隔 1秒 发送一个 heartbeat。
master 每次接收到写命令之后,先在内部写入数据,然后异步发送给 slave node。
failover
故障转移,也可以叫做主备切换。master node 在故障时,自动检测,并且将某个 slave node 自动切换为 master node 的过程,叫做主备切换。这个过程,实现了 redis 的主从架构下的高可用。后面会详细说明 redis 基于哨兵的高可用性。