31redis事务和日志

事务

Redis事务保证原子性吗,如何实现Redis原子性 (1)Redis事务保证原子性吗Redis事务不像MySQL那样的关系型数据库事务那样拥有事务回滚操作当Redis事务的所有命令都成功执行后才能保证原子性,若某一命令失败,则下一命令仍会继续执行.(2)Redis如何实现原子性背景引入Redis内核是单线程,按发送顺序执行,天然地命令串行,不会又并发安全问题.Redis能保证同一客户端地命令严格串行,但不同的客户端会命令会交叉执行,无法保证同一客户端的一组命令连续执行,容易出现问题.事务能保证同一客户端的一组命令连续执行,不会被其它客户端打断Lua脚本解决可以将多个操作写到一个lua脚本中,Redis会把整个lua脚本作为一个整体执行既能实现事务的核心需求”不可打断性”,又可以通过逻辑控制(if)实现条件判断来决定后续的命令是否执行,更可以解决事务的频繁网络IO问题且只仅需一次网络IO由于Lua脚本是逐行检查并执行,若中间代码出现语法错误导致不执行,那么之前的命令不回滚,而事务在EXEC时统一校验语法, 有错误则全不执行,但对于类型匹配问题校验时是通过的,事务仍会通过.1)事务没有逻辑控制,要么全不执行(语法错误),要么能执行的全执行,失败的仅自身不生效,注定它无法完成库存校验->库存扣减->订单生成这样的组合性原子操作,只能通过lua脚本2)但Lua脚本也无法实现命令执行失败的回滚操作,因为Redis追求轻量高效,没有Undo Log(MySQL通过该技术记录数据修改前的状态,回滚时可反向执行操作),若为回滚设计命令日志,会大量增加内存开销和执行复杂度.3)可以在业务层设置一个Redis延迟队列来进行兜底,先预估业务执行时间,然后创建临时标记同时将延迟任务入列,接着开启一个守护线程来阻塞等待消费,消费时通过查询数据来判断命令是否正确执行,若未执行则触发兜底操作.

事务不支持 ACID 的完整实现

ACID 属性 Redis 事务支持 为什么?
A (Atomicity) 不支持 事务中命令失败时,已执行的命令不会回滚(例如 SET 成功后 INCR 失败,SET 的修改保留)
C (Consistency) 不支持 事务不保证数据一致性(如 INCR 依赖 SET 的值,但事务中可能被其他客户端修改)
I (Isolation) 支持 事务命令 串行执行(其他客户端无法在事务执行期间插入命令),实现类似串行化隔离
D (Durability) 不支持 事务本身不涉及持久化,持久性由 AOF/RDB 保证

💡 面试官深度问:为什么 Redis 不支持原子性?
“Redis 采用 单线程模型,事务命令在 EXEC顺序执行,但没有回滚机制。设计哲学是 ‘快速执行,不保证事务完整性’ —— 与数据库不同,Redis 优先保证高吞吐量,牺牲了事务的原子性。
例如:MULTI; SET a 1; INCR a; EXEC,如果 INCR 失败,SET a 1 的结果已生效。”(LUA中途出现语法错误)

aof和rdb

Redis有哪2种持久化方式?分别的优缺点是什么?

aof

Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis 重启后,内存中的数据就会丢失,那为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,这样在 Redis 重启就能够从磁盘中恢复原有的数据。Redis 共有三种数据持久化的方式:

  • AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
  • RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
  • 混合模式

AOF 日志是如何实现的?

客户端发送写命令(如 SET name Tom);
Redis 执行命令并将原生命令转换为 文本协议格式(RESP);
命令被追加到 AOF 缓冲区(aof_buf);
根据配置的同步策略(appendfsync),缓冲区内容写入并同步到磁盘文件 appendonly.aof;
(后台或手动触发)AOF 重写(rewrite):压缩历史命令,生成更精简的 AOF 文件;
Redis 重启时加载 AOF 文件,顺序执行命令恢复数据。

aof的3 个写回策略的优缺点总结成了一张表格:

img

RDB 快照是如何实现的呢?

因为 AOF 日志记录的是操作命令,不是实际的数据,所以用 AOF 方法做故障恢复时,需要全量把日志都执行一遍,一旦 AOF 日志非常多,势必会造成 Redis 的恢复操作缓慢。为了解决这个问题,Redis 增加了 RDB 快照。

rdb

所以,RDB 快照就是记录某一个瞬间的内存数据,记录的是实际数据,而 AOF 文件记录的是命令操作的日志,而不是实际的数据。因此在 Redis 恢复数据时, RDB 恢复数据的效率会比 AOF 高些,因为直接将 RDB 文件读入内存就可以,不需要像 AOF 那样还需要额外执行操作命令的步骤才能恢复数据。

Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:

  • 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程
  • 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞

适用场景

image-20251120115350554 image-20251120115422043

(1)AOF优点数据完整性:AOF数据完整性更高,因为它每隔一小段时间就会追加到文件末尾,即使Redis服务宕机,也只会丢失最后一次追加前的数据平衡策略:AOF支持多种同步策略,可以调整数据安全与性能的平衡重写机制:触发阈值或手动重写AOF文件,减小文件体积并加快恢复速度文件修复:AOF还提供了redis-check-aof工具来修复损坏的文件

缺点磁盘空间占用:因为记录每一个写操作,AOF文件比RDB文件更大磁盘IO:频繁进行磁盘IO操作,可能会拖慢Redis写入性能宕机恢复:即使重写,AOF的恢复速度通常也慢于RDB.

(2)RDB优点RDB基于快照形式保存某一时刻的数据状态并进行二进制压缩,文件体积下且恢复速度快RDB是在主进程之外通过fork子进程异步生成快照,对Redis核心业务的性能影响小

缺点数据完整性较低:两次快照期间若发生宕机会造成数据的大量丢失,这在高并发环境下是难以容忍的在快照形成期间,若有写请求会触发写时复制,极端情况内存占用提升一倍

RDB

只要满足配置自动快照策略,就会自动执行 BGSAVE。

主从全量复制(全同步)
当从节点首次连接主节点或主从数据严重不一致时,主节点会执行一次 RDB 快照,并将 RDB 文件发送给从节点用于初始化。此时即使未启用 SAVE 规则,也会触发 RDB 生成。
执行 SHUTDOWN 命令
若启用了 RDB(默认开启),Redis 在正常关闭(非 crash)时会执行一次 RDB 持久化以保存最新状态再退出。

AOF重写

步骤 1:触发重写条件(自动/手动)

触发方式 配置参数 示例 说明
自动触发 auto-aof-rewrite-min-size 64mb auto-aof-rewrite-percentage 100 AOF > 64MB 且 比上次重写后大 100% 例如:上次重写后 AOF=50MB,当前=100MB → 触发
手动触发 BGREWRITEAOF redis-cli BGREWRITEAOF 无延迟,立即执行

💡 为什么这样设计
避免频繁重写(如 AOF 1MB 时重写无意义),只在文件膨胀到合理阈值时触发。

步骤 2:fork 子进程(关键!)

  • 操作:Redis 主进程 fork() 一个子进程(类似 RDB 快照)
  • 为什么用子进程?
    • 不阻塞主线程:子进程遍历内存时,主线程继续处理请求
    • 内存快照:子进程获得当前内存数据的副本(fork 时的快照)
  • 代价fork() 耗时(与内存大小正相关,1GB 内存约 10ms)

💡 面试官深度问:为什么不用主线程重写?
“主线程重写会阻塞所有客户端请求!例如遍历 100 万 key 需 100ms,用户会感知卡顿。
子进程方案保证 Redis 高可用——这是 Redis 为性能牺牲的工程智慧。”

步骤 3:子进程生成新 AOF 文件(核心!)

  • 操作:子进程遍历当前内存中的所有 key,为每个 key 生成最少命令
  • 关键优化:
    • 只保留最新值SET key 1SET key 2只保留 SET key 2
    • 合并命令HSET user:1000 name Alice + HSET user:1000 age 25保留两条命令(无法合并)
    • 跳过过期 key:不生成已过期 key 的命令

💡 为什么能压缩
例如:

  • 原 AOF:SET a 1 SET a 2 SET a 3SET a 1000
  • 重写后:SET a 1000(仅 1 条命令!)
    压缩率 99.9%+(小数据集压缩率 90%+,大数据集 95%+)

步骤 4:主进程处理重写缓冲区(数据安全!)

  • 问题:子进程重写期间,主线程继续处理新命令(如 SET a 1001
  • 解决方案:
    • 主线程将新命令追加到 aof_rewrite_buffer(内存缓冲区)
    • 重写完成后,aof_rewrite_buffer 追加到新 AOF 文件
  • 为什么安全?
    • 重写期间的命令被完整记录,不会丢失
    • 新 AOF 文件 = 重写文件 + 重写期间的新命令

💡 面试官终极问:如果重写过程中 Redis 崩溃,数据会丢失吗?
不会丢失!因为:

  1. 重写文件保存了当前数据
  2. 重写缓冲区保存了重写期间的增量
  3. 恢复时,Redis 先加载新 AOF 文件,再重放缓冲区命令
    (这是 Redis 持久性的核心保障!)”

Q1:为什么 Redis 不像 MySQL 一样先写日志再执行命令?

“因为 Redis 的定位是 ‘内存缓存’,不是 ‘数据库’

  • MySQL 为 ACID 事务设计,必须先写日志(强一致性)
  • Redis 为 高吞吐量 设计,先执行命令再写日志(性能优先)
    (Redis 官方文档: ‘Redis 是内存数据库,速度是第一优先级’)”

Q2:如果我用 everysec 配置,崩溃时丢失 1 秒数据,怎么避免?

有三种方案

  1. 升级配置:用 appendfsync always(但 QPS 会暴跌)
  2. 业务补偿:在应用层记录关键操作(如订单状态)
  3. 混合持久化:Redis 4.0+ 支持 AOF + RDB 混合(默认 RDB 为主,AOF 为辅)
    (最佳实践:99% 场景用默认 everysec,仅关键业务用 always)”

如何进行恢复

Redis 启动时如何选择 RDB 还是 AOF?

如果 同时开启了 RDB 和 AOF:
优先使用 AOF 恢复(因为 AOF 数据更新);
加载 appendonly.aof 并执行所有命令。AOF恢复从磁盘中加载AOF文件到Redis进程的内存缓冲区;逐行解析AOF文件中的命令,将字节流还原成一条一条Redis命令;按顺序在内存中重新执行命令.

如果只启用了 AOF:加载 AOF;
如果只启用了 RDB:加载 dump.rdb;RDB恢复流程检测RDB文件,若未开启AOF且配置开启RDB(默认),指定路径下扫描是否存在RDB文件,自动加载该文件按Redis定义的RDB二进制协议解析文件,先确保文件未损坏再还原数据.
如果都未开启:启动空数据库。

end


31redis事务和日志
http://example.com/2025/11/18/31redis事务和日志/
作者
無鎏雲
发布于
2025年11月18日
许可协议