
目录1. Redis持久化机制RDB与AOF1.1. RDBRedis DataBase内存快照1.1.1 触发机制1.1.2 bgsave 运作流程1.1.3 优缺点1.1.4 RDB文件的处理1.2. AOFAppend Only File日志追加1.2.1 工作流程append - sync - rewrite - load1.2.2 文件同步策略appendfsync1.2.3 AOF重写机制1.2.4 启动时数据恢复Redis 4.0 的混合持久化2. Redis事务与乐观锁2.1. Redis事务的ACID特性2.2. 核心命令与执行流程2.3. WATCH机制乐观锁CAS的实现2.3.1 WATCH 原理版本号机制2.3.2 实操演示2.3.3 UNWATCH3. 高频面试题 QA 速查表1. Redis持久化机制RDB与AOFRedis之所以快是因为数据都在内存中但为了防止进程崩溃或宕机导致数据丢失必须将数据落盘这就是持久化1.1. RDBRedis DataBase内存快照RDB是将当前进程的数据生成快照保存到硬盘的过程生成的是紧凑压缩的二进制文件1.1.1 触发机制手动触发save阻塞当前Redis服务器直到RDB完成。基本废弃bgsaveRedis进程执行fork创建子进程由子进程负责持久化父进程阻塞仅发生在fork阶段。主流方式自动触发配置 save m nm秒内发生n次修改则触发主从全量复制时主节点自动执行bgsave执行 shutdown 命令关闭Redis时1.1.2 bgsave 运作流程父进程判断当前是否存在正在执行的子进程如RDB/AOF子进程存在则直接返回父进程执行 fork 创建子进程此阶段父进程阻塞可通过 info stats 的 latest_fork_usec 查看耗时微秒数父进程 fork 完成后返回 Background saving started不再阻塞继续响应其他命令子进程根据父进程内存快照Copy-On-Write写时复制机制生成临时RDB文件完成后对原有文件进行原子替换子进程发送信号给父进程父进程更新统计信息rdb_last_save_timefork时父进程在干什么父进程继续响应其他命令。利用Linux的写时复制机制子进程共享父进程的内存页只有当父进程修改数据时才会复制一份新的内存页。这保证了极低的性能损耗1.1.3 优缺点优点文件紧凑适合备份和全量复制恢复速度快远快于AOF缺点无法做到实时/秒级持久化fork是重量级操作频繁执行成本高版本演进兼容性有风险特定二进制格式1.1.4 RDB文件的处理保存RDB 文件默认保存到 dir 配置指定的目录默认 /var/lib/redis/文件名由 dbfilename 指定默认 dump.rdb运行期间可通过 config set dir {newDir} 和 config set dbfilename {newFilename} 动态修改下次 RDB 持久化时会保存到新目录并使用新文件名压缩Redis 默认使用 LZF 算法压缩 RDB 文件默认开启可通过 config set rdbcompression {yes|no} 动态修改压缩虽然会消耗一定 CPU但能大幅减小文件体积便于硬盘保存和主从节点网络传输因此建议保持开启校验Redis 启动时如果加载到损坏的 RDB 文件会拒绝启动此时可使用 redis-check-dump新版本中为 redis-check-rdb检测 RDB 文件并获取错误报告生产环境应定期校验备份文件发现损坏时优先从备份或从节点恢复1.2. AOFAppend Only File日志追加AOF以独立日志的方式记录每次写命令重启时重新执行AOF文件中的命令来恢复数据目前是Redis持久化的主流方式开启 AOF 功能需要设置配置appendonly yes默认不开启。AOF 文件名通过 appendfilename 配置默认是 appendonly.aof设置。保存目录同 RDB 持久化方式一致通过 dir 配置指定1.2.1 工作流程append - sync - rewrite - load命令写入所有写命令追加到 aof_buf缓冲区中文件同步根据策略将缓冲区同步到硬盘文件重写定期压缩AOF文件重启加载启动时加载AOF文件恢复数据为什么要有 aof_buf 缓冲区Redis是单线程响应命令如果每次都直接同步硬盘IO操作性能会急剧下降。先写缓冲区可以减少IO次数同时允许提供多种同步策略来平衡性能与安全1.2.2 文件同步策略appendfsyncRedis 提供了多种 AOF 缓冲区同步文件策略由参数 appendfsync 控制appendfsync 配置值说明fsync 时机建议always命令写入aof_buf后调用fsync同步完成后返回每次写入都执行fsync除非是非常重要的数据否则不建议配置everysec命令写入aof_buf后只执行write不立即fsync每秒由同步线程执行一次fsync每秒同步一次默认配置也是推荐配置no命令写入aof_buf后只执行write由操作系统控制fsync频率由 OS 控制不可控除非数据重要程度很低一般不建议配置always每次写入都fsync。最安全性能最差SATA硬盘仅支持几百TPSeverysec每秒由同步线程fsync一次。默认且推荐配置兼顾性能与安全理论上最多丢失1秒数据no由操作系统控制fsync频率。性能最高但数据丢失风险大对比项write 操作fsync 操作核心机制延迟写delayed write利用 Linux 内核页缓冲区强制硬盘同步操作对象文件描述符 / 文件单个文件返回时机写入系统缓冲区后立即返回阻塞直到数据写入硬盘是否阻塞通常不阻塞阻塞落盘保证不保证立即落盘依赖系统调度强制同步等待落盘完成同步触发缓冲区页空间写满、达到特定时间周期等显式调用触发故障风险同步前系统故障宕机缓冲区内数据可能丢失调用返回后正常情况下数据已写入硬盘性能影响性能高减少磁盘 IO性能开销大增加延迟适用场景常规写入追求吞吐量需要持久化保证的关键数据1.2.3 AOF重写机制随着命令不断写入AOF文件会越来越大Redis引入重写机制来压缩体积为什么能变小 超时数据不再写入无效命令如被覆盖的set被删除多条写操作合并如多次lpush合并为一条触发时机手动执行 bgrewriteaof或根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 自动触发配置项说明默认值auto-aof-rewrite-min-size表示触发重写时 AOF 的最小文件大小64MBauto-aof-rewrite-percentage表示当前 AOF 占用大小相比较上次重写时增加的百分比—AOF重写流程与并发细节执行重写请求如果当前进程正在执行 AOF 重写请求不执行如果当前有bgsave在执行则延迟到其完成后再执行父进程 fork 创建子进程关键并发处理父进程继续响应其他命令。所有修改操作写入 aof_buf并根据策略同步到旧AOF文件保证旧AOF机制正确由于子进程只有 fork 之前的内存信息父进程需要将 fork 之后的修改操作写入 aof_rewrite_bufAOF重写缓冲区子进程根据内存快照将命令合并写入新的AOF文件子进程完成后发送信号给父进程父进程将 aof_rewrite_buf 中的命令追加到新AOF文件并用新AOF文件原子替换老AOF文件1.2.4 启动时数据恢复当 Redis 启动时会根据 RDB 和 AOF 文件的内容进行数据恢复当Redis启动时如果同时存在RDB和AOF文件优先加载AOF因为AOF数据更全。如果AOF不存在则加载RDB。如果加载损坏的文件Redis会拒绝启动可用 redis-check-aof 或 redis-check-dump 修复Redis 4.0 的混合持久化通过 aof-use-rdb-preamble yes 开启。开启后AOF 重写时子进程先将当前内存数据以 RDB 格式写入新 AOF 文件头部重写期间及之后的新写命令以 AOF 格式追加到文件后面。重启加载时先加载 RDB 头部再重放后续 AOF 命令。这样既利用 RDB 加载快、体积小的优点又保留 AOF 的增量记录降低数据丢失风险。但数据丢失窗口仍取决于 appendfsync 策略且混合 AOF 文件对旧版本 Redis 和部分工具兼容性较差2. Redis事务与乐观锁Redis的事务与MySQL的事务概念类似都是将一系列操作绑定成一组批量执行但在ACID特性上有极大的弱化2.1. Redis事务的ACID特性弱化的原子性Redis没有“回滚机制”。只能做到“批量执行”不能做到“一个失败就恢复到初始状态”。如果事务中某条命令执行失败如类型错误其余命令仍会继续执行满足一致性无约束不存在违反约束的非法状态。中间状态非法需要业务自己判断天然隔离性Redis单线程处理请求天然串行执行不存在并发执行事务的问题不保证持久性数据在内存中是否持久化由RDB/AOF机制决定与事务无关2.2. 核心命令与执行流程MULTI开启事务。返回OK命令入队后续的命令不会立即执行而是返回 QUEUED放入客户端/服务器的事务队列EXEC真正执行事务。按顺序执行队列中的命令DISCARD放弃当前事务清空事务队列事务的错误处理入队错误语法错误、命令不存在在Redis 2.6.5之后如果入队阶段发生错误EXEC 会直接返回错误整个事务都会被丢弃运行时错误如对String执行lpush或incr一个非数字命令在入队时不会报错EXEC后正确的命令会执行错误的命令报错且不支持回滚。这是Redis事务与关系型数据库最大的区别2.3. WATCH机制在并发场景下客户端可能先读取数据再基于旧值构造事务进行修改。如果在提交事务前该数据已被其他客户端修改直接提交就可能造成数据不一致如丢失更新。Redis 通过 WATCH 命令实现乐观锁CASWATCH 监视相关 key若在 WATCH 之后、EXEC 之前这些 key 被修改过则 EXEC 放弃执行整个事务并返回 nil从而避免基于过期数据做出错误修改2.3.1 WATCH 原理Redis 通过 WATCH 实现乐观锁。Redis 建立映射key - 监视该 key 的客户端列表客户端也记录自己监视了哪些 key。在 WATCH 之后、EXEC 之前只要任何客户端包括自己修改了被监视的 keyRedis 就会通过 touchWatchedKey 给监视该 key 的客户端打上 CLIENT_DIRTY_CAS 标志。执行 EXEC 时如果客户端带有这个标志就放弃整个事务并返回 nil事务中的命令都不执行否则正常执行。EXEC 后自动取消所有 WATCH2.3.2 实操演示# 客户端1 WATCH k1 # 记录 k1 的版本号假设是 0 MULTI set k1 100 # 入队但不提交 set k2 1000 # 入队 # 客户端2此时执行 set k1 200 # 修改成功k1 版本号变为 1 # 客户端1 再执行 EXEC # 比对版本号客户端是 0服务器是 1。版本不一致 (nil) # 事务失败所有命令都不执行2.3.3 UNWATCH取消对所有key的监控相当于WATCH的逆操作EXEC 和 DISCARD 执行后也会自动取消所有WATCHWATCH 适合什么场景WATCH 本质是CASCompare And Swap思路在很多并发编程和数据库如MySQL的乐观锁中都有应用。它适合读多写少的并发场景如果在高并发写场景下事务很容易因为版本冲突而不断重试失败影响性能3. 高频面试题 QA 速查表Q1RDB和AOF的区别如何选择RDB紧凑二进制恢复快适合冷备和主从复制。但实时性差fork开销大AOF文本协议实时性好默认每秒同步可读性强。但文件体积大恢复慢选择如果对数据安全性要求极高选AOFeverysec如果追求恢复速度容忍少量数据丢失选RDB生产环境推荐混合持久化Redis 4.0Q2AOF重写时主进程在做什么主进程继续响应其他客户端请求将新写入的命令追加到 aof_buf 以同步到旧AOF文件将新写入的命令同时追加到 aof_rewrite_buf等待子进程重写完成后追加到新AOF文件保证数据不丢失Q3Redis事务支持原子性吗不支持严格的原子性。它只保证命令的“批量执行”和“连续执行不被加塞”如果发生运行时错误如类型错误Redis不会回滚其余命令会继续执行Q4WATCH命令的实现原理基于版本号CAS乐观锁。WATCH记录key的当前版本号EXEC时比对如果版本号发生变化被其他客户端修改则拒绝执行事务返回nilQ5为什么Redis执行bgsave时不阻塞主进程利用操作系统的 fork 和 写时复制Copy-On-Write 机制。fork出的子进程共享父进程内存只有当父进程有写操作时才会复制被修改的内存页极大降低了内存开销和阻塞时间