Redis 是非常常用的 KV 数据库, 使用内存以及 HashMap 进行存储的特点带来了高效的查询. 本文将围绕 Redis 的常见开发使用场景, 阐述在 Redis 集群中各个节点是如何进行数据同步, 每个节点如何进行持久化以及在长期使用中如何对数据进行更新和淘汰.
Redis logo 下面是 Redis 在开发过程中常用的几种使用场景.
Redis 对于集群的部署支持三种模式: 主从 , 哨兵 和 分片 . 在正式的生产环境中一般会采用 "一主一从" + "哨兵" 的模式来解决高可用和高并发读的问题, 但是这种模式无法解决高并发写的问题. 高并发写可以借助于分片集群.
由于单个服务器的并发数量存在上限, 我们可以使用主从模式实现读写分离, 以此提高读写的效率. 通常主节点进行写操作, 从节点进行读操作.
在主从模式中主节点和从节点之间存在数据同步的问题, 目前 Redis 支持两种不同的同步方式: 全量同步 和 增量同步 .
在了解全量同步的流程之前, 我们先熟悉几个 Redis 内部的定义:
ReplicationId: 数据集标记, id 一致说明是同一个数据集. repl-baklog: 命令的日志文件. 主要用于记录同步过程中 master 生成快照到 slave 拷贝快照这段时间 master 接受的命令. offset: repl-baklog 的偏移量. slave 的 offset 如果小于 master 则说明版本落后于 master, 需要更新. 下面是全量同步的流程时序图:
提示
步骤 9 实际上记录了步骤 6 到步骤 10 之间 master 收到的所有命令.
在了解过全量同步后, 再来对比看一下增量同步的流程. 增量同步主要用于 slave 节点重启后或者后期出现数据变化的场景.
哨兵模式主要用来实现主从集群的自动恢复. 主要的功能有三个:
监控: 检查 master 和 slave 是否正常工作.自动恢复: 如果 master 出现故障, 提升 slave 为 master.通知: 集群发生故障转移时, 将最新的信息推送给 Redis 客户端.用心跳机制, 每隔 15 s 发一次 ping
确保节点的状态保持活跃. 节点下线分为两种情况:
主观下线: 某个节点超时未响应.客观下线: 超过阈值数量的哨兵认为某节点下线. 阈值变量为 quorum
.当 Redis 中的 master 节点宕机后, 会使用类似 Raft 的投票机制来进行选主. 选主的标准大致基于以下几个:
slave-priority 越小. offset 越小. 运行 id 越小. 分片集群主要用于应对海量的数据, 有以下几个特点:
多个 master. 每个 master 都会存储不同的数据. 每个 master 有多个 slave. master 之间会用 ping
检测健康. client 可访问任意的节点进行读写, 内部会将请求自动转发到正确的节点上. 那么如果不同的 master 存储的是不同的数据, Redis 又是如何决定每一个数据应当存储到哪个节点上呢? Redis 使用的是插槽, 一共有 16384 个插槽. 大致过程为: 首先根据 key 的有效部分计算 Hash 值, 再与 16384 取余决定其插槽位置, 再去取数据.
至于为什么插槽的数量定为 16384, 其作者有做过正式答复 :
why redis-cluster use 16384 slots?
Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots. At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs. So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set. Redis 一共提供两种持久化的策略: RDB 和 AOF, 现在我们分别来看一下这两种策略的优缺点.
RDB 是基于快照的全量备份, 周期性地把全量的数据写入快照文件.
优点
恢复快. 因为是全量数据直接恢复就好. 主进程不进行 I/O, 对节点影响小. 缺点
丢失间隔. RDB 是快照文件, 两个快照之间节点宕机会导致快照之间的修改会丢失. 老版本无法兼容. RDB 文件格式与 Redis 的版本息息相关, 不同版本可能无法使用相同的RDB进行恢复. 设置的备份的间隔时间过长, RDB 太大的时候可能导致服务暂停. AOF 策略是对内存修改进行指令记录.
优点
不会出现数据丢失. 最多丢失 1 s 的数据. 没有磁盘寻址开销. 因为日志是追加写入(append-only). 日志可读. 该场景可以用于紧急恢复, 例如误删了重要数据后, 可以趁着文件还未更新前将 AOF 保存下来. 缺点
AOF 日志比 RDB 更大. 因为 AOF 是指令文件, RDB 是二进制文件. 性能低. 因为备份间隔更小. 恢复慢. 需要执行一遍 AOF 中的指令才能恢复. 随着 Redis 集群的使用时间不断增加, 里面保存的 KV 越来越多, 此时就会面临内存不够用的问题. Redis 为此提供了过期和淘汰策略来管理海量的数据.
顾名思义就是每一个数据在 Redis 当中都有一个"寿命", 当过了设定的有效时间该数据就会变为无效的数据, 不再支持读写. Redis 提供两种过期策略:
定期删除: 每隔 100 ms 随机删除部分过期 key. 这里只删除部分的原因是如果进行全量扫描会导致 Redis 性能过低. 如果想要调整扫描间隔可以修改参数 h2
.惰性删除: 不进行定期扫描, 仅仅在查询的时候判断该 key 是否过期, 如果过期则删除还有一些额外的场景下 Redis 会对过期的 key 进行统一的处理:
RDB 生成和载入. AOF 的写入和重写. 主从同步. 以上几种场景中都会过滤过期的 key, 即过期的 key 不会进入快照文件或者被同步到 slave 节点. 序号 名称 定义 1 volatile_LRU 设置了过期时间的 key 中执行 LRU 算法 2 allkeys-LRU 在所有 key 中执行 LRU 算法 3 volatile_LFU 设置了过期时间的 key 中执行 LFU 算法 4 allkeys-LFU 在所有 key 中执行 LFU 算法(删除最不常用的 key) 5 volatile_random 设置了过期时间的 key 中执行随机删除 6 allkeys-random 在所有 key 中执行随机删除 7 volatile_TTL 删除过期时间最早的 key 8 noeviction 不进行 key 的删除(默认配置, 但是正常开发中不会使用该模式 )
本文主要讲解了 Redis 集群的主从同步的原理, 以及 RDB 和 AOF 快照. 最后对 Redis 当中的过期淘汰机制进行了简单的介绍.