存储持久化专题 · MySQL / Redis / Elasticsearch

面试冲刺复习手册:把三种存储放在同一个"日志先行"心智模型下对比,一次性理清 WAL / AOF / translog、快照与崩溃恢复。目标不是背参数,而是能讲清楚"每种日志为什么长这样"

MySQL · InnoDB redo/undo/binlog Redis · RDB / AOF / 混合持久化 Elasticsearch · translog / refresh / flush 对比 · 心智模型 · 高频追问

统一心智模型:所有持久化都是同一套骨架

面试先说这句话,再往下展开 —— 能立住"我在讲原理而不是背八股"的人设。

核心论断

磁盘随机写慢、顺序写快;数据量大的随机落盘无法每次同步完成。所以所有存储引擎的共同解法是:把"这次修改"先变成一小段顺序追加的日志落盘(快、可确认),数据本体异步慢慢刷;崩溃后用日志重放,找回没来得及刷盘的那部分。

这套骨架 = 内存缓冲 + 顺序日志(先写) + 异步落盘(快照/合并) + 崩溃重放。三者名字不同,本质同构:

🏛 MySQL / InnoDB WAL

数据页本体在磁盘,Buffer Pool 只是缓存。redo log 先于脏页刷盘,是真正的 WAL;另配 binlog(逻辑)、undo(回滚)。

⚡ Redis RDB + AOF

数据本体在内存,落盘只是"备份"而非缓存管理。RDB 快照 + AOF 命令日志,纯追加、定期重写压缩。

🔎 Elasticsearch translog

段文件在磁盘但写入先走内存 buffer;translog 就是它的 WAL,refresh 让数据可搜、flush 才真正落盘。

一句话记忆

MySQL:改磁盘数据页之前先写 redo(WAL),防止刷盘途中崩溃丢页

Redis:内存是本体,怕重启丢数据,所以记命令日志(AOF)+定期拍快照(RDB)

ES:segment 要攒批才落盘,空窗期靠 translog 兜底,refresh 只管"能不能搜到"

区分两个正交问题:① 数据要不要立刻可恢复(持久性)→ 日志负责;② 数据要不要立刻可见(对 MySQL 即事务可见性,对 ES 即可搜索性)→ 由各自的提交/refresh 机制负责。

为什么"先写日志"就快?

顺序 vs 随机

redo/AOF/translog 都是尾部追加、顺序 IO,可预读可批量;而把 16KB 数据页(或若干 segment 数据)随机刷到磁盘各位置,机械盘磁头寻道、SSD 也有写放大。量级差可达 1~2 个数量级。

小 vs 大

一次事务的日志往往几百字节到几 KB;被修改的数据页可能散布在多处、共几 MB。日志是"变更的最小充分描述"。

可合并(组提交 / 攒批)

多个并发事务的 fsync 可以合并成一次(MySQL 组提交、ES translog 批量刷、Redis everysec)。确认一批事务只需一次落盘。

可放弃(纯内存)

Redis 不落盘也能跑(纯缓存),此时"快照/日志"只是可选项;MySQL/ES 的数据本体在磁盘,WAL 是正确性的必需品 —— 这就是三者的本质差别来源

本篇剩余部分:按系统拆解"日志记什么、何时落盘、崩溃怎么恢复",最后做横向对比和高频追问。

MySQL / InnoDB:redo + binlog + undo + doublewrite

面试主战场。先分清 Server 层(binlog)InnoDB 引擎层(redo/undo),再讲两阶段提交。

2.1 一句话框架

InnoDB 把数据按 16KB 数据页存放在磁盘,Buffer Pool 缓存热页。若每次事务提交都把这些页同步刷回磁盘(随机 IO、页可能被改多次),性能不可接受。因此采用 WAL:

事务执行修改 Buffer Pool 中的页内存
先写 redo log记录"页 × × 偏移 × 改成 ×"顺序追加·可配置 fsync
提交成功确认日志已落盘 → 返回客户端无需刷数据页
后台慢慢刷checkpoint 把脏页异步写回磁盘随机 IO·批量
redo log 到底是什么

物理逻辑日志(physiological):记录"哪个表空间、哪个页号、页内偏移、改成了什么",围绕单个页内操作,不是整条 SQL、也不是整页镜像。循环写入一组文件(ib_logfile*,8.0.30+ 由 innodb_redo_log_capacity 管理)。它只管崩溃恢复(crash recovery),不管复制。

2.2 三份日志各有分工(这是必考点)

日志层级内容用途关键点
redo logInnoDB 引擎层 物理逻辑:页号 + 页内修改 崩溃恢复:保证已提交事务的数据不丢(持久性),把 Buffer Pool 里没刷盘的页改回来 循环写、会覆盖;LSN 单调递增;checkpoint 推进后可覆盖;组提交合并 fsync
binlogServer 层 逻辑日志:完整 SQL 或行变更(statement / row / mixed,8.0 默认 row) 主从复制 + 时间点恢复(PITR),也供 canal 等消费 追加写、不覆盖、可归档;与存储引擎无关;MyISAM 没有 redo 但有 binlog
undo logInnoDB 引擎层 修改前的旧版本数据(逻辑反操作) 事务回滚 + MVCC 快照读:RR/RC 下读 undo 链构建历史版本 也落盘但属于"数据"而非 WAL 语义;purge 线程清理不再被任何事务看到的旧版本
高频混淆点:redo 与 binlog 的区别(回答模板)

① 层级不同:redo 在引擎层,只对 InnoDB;binlog 在 Server 层,对所有引擎、面向复制/恢复。② 内容不同:redo 是物理逻辑(页级),binlog 是逻辑(语句/行级)。③ 生命周期不同:redo 循环覆盖,binlog 追加保留。④ 时机不同:redo 在事务执行中就写(配合组提交),binlog 在事务提交时写。⑤ 语义不同:redo 保证"我告诉客户端提交了就不会丢",binlog 保证"别的主库/时间点能看到同样的数据"。

2.3 两阶段提交(redo ↔ binlog 一致性)

redo 与 binlog 是两个独立文件系统里的两份日志,写它们不是原子的。若先写 redo 后写 binlog,崩在中间:主库重放 redo 提交了事务,但从库没拿到 binlog → 主从不一致;反之则从库多执行。解法是经典的 prepare → commit 两阶段

① prepare写 redo,标记 prepare + XIDredo 落盘
② 写 binlog事务内容写入 binlog 并落盘binlog 落盘
③ commitredo 标记 commit,事务真正提交redo 落盘(可合并)

崩溃恢复时检查 redo 里的 prepare 事务:binlog 里若存在对应 XID 的事务 → 补做 commit(两库都执行了才一致);binlog 里没有 → 回滚。这样无论崩在哪个点,主库与从库(binlog 消费方)的提交集合都一致。注意这里 fsync 次数与组提交:binlog_group_commit 把多个事务的 binlog 写盘合并,innodb_flush_log_at_trx_commit 决定 redo 何时刷。

2.4 刷盘参数与丢数据窗口

参数取值行为崩溃丢多少
innodb_flush_log_at_trx_commit 1(默认)每次事务提交都把 redo log buffer fsync 到磁盘不丢(配合双写/页完整)
2每次提交只写到 OS page cache,由 OS 定期刷盘OS 不宕机不丢;主机断电最多丢约 1s
0提交时不主动刷,由后台线程每秒刷一次MySQL 进程崩溃最多丢 1s 内已提交事务

同为 1(每次落盘)时,redo log 因为顺序写且可组提交,性能远好于直接刷随机数据页 —— 这就是 WAL 的意义。

2.5 崩溃恢复全流程(顺着讲一遍)

定位起点从最近 checkpoint 的 LSN 开始扫描 redo
redo 前滚重放日志,把脏页恢复到提交态(可能包含未提交事务的写入)
处理 prepare 事务对照 binlog XID 决定补提交或回滚
undo 回滚对未提交事务,用 undo log 把页改回原状
服务可用回滚段/页校验完成,接受新请求

checkpoint 是"这些 LSN 之前的脏页已刷盘"的标记,推进它 = 缩短恢复要重放的日志量 + 释放 redo 空间可覆盖。

加分细节:doublewrite buffer 解决"页半写"

redo 能重放的前提是目标页本身完整(它是"在这个页的修改上再改")。如果 16KB 页刷盘时断电只写了一半(torn page),redo 也救不了。所以 InnoDB 先把整页拷贝到连续的 doublewrite buffer(2MB 区)落盘,再写实际位置;恢复时若检测到半写页,从 doublewrite 副本还原。SSD 上可关(innodb_doublewrite=0)换吞吐,但默认开着。此点是区分"背过八股"和"真懂"的试金石。

为什么不能每次提交直接刷脏页,非要 redo?必考

刷脏页是随机 IO 且页大(16KB、涉及多个页);redo 是顺序追加、量小、可组提交合并 fsync。直接刷页 = 一次提交等多次随机写;写 redo = 一次提交等一次顺序写。前者吞吐低 1~2 个数量级。而且页可能被多个事务反复修改,日志只需记最终落盘前的增量,脏页刷盘频率远低于提交频率(checkpoint 周期化)。

redo 会不会无限膨胀?写满了怎么办?常考

循环文件。checkpoint 持续推进:脏页刷回磁盘后,对应 LSN 之前的 redo 空间即可复用。若刷脏页太慢、redo 写满,InnoDB 会强制推进 checkpoint(先刷脏页再允许覆盖),表现为写入被阻塞 —— 这就是 redo log busy / checkpoint age 类告警的来源。设计上让刷盘能力匹配写入量。

binlog 用 row 还是 statement?为什么?追问

8.0 默认 row:记录每行变更前后值,主从结果确定、支持 DDL 安全;statement 只记 SQL,量小但依赖上下文(如 NOW()LIMIT 更新)可能主从不一致。row 的代价是批量操作日志量大(可配 binlog_row_image)。canal/数据同步类中间件基本都要求 row。

Redis:RDB 快照 + AOF 命令日志 + 混合持久化

先立观点:Redis 的持久化是"给内存数据上保险",不是数据正确性必需品 —— 这正是它和 MySQL/ES 最大的不同。

3.1 两种机制先分清

RDB —— 二进制快照

SAVE(阻塞)/BGSAVE(后台):fork 子进程,利用 COW(写时复制)把当前全量数据写成紧凑二进制文件 dump.rdb。优点:文件小、恢复快、适合备份。缺点:两次快照之间崩溃,期间写入全部丢失

自动触发:save 3600 1 / 300 100 / 60 10000(N 秒内 ≥M 次写)等配置;主从全量同步也传 RDB。

AOF —— 追加命令日志

把每个写命令按 RESP 协议追加到文件末尾(appendonly.aof),重启时逐条重放重建数据。丢失窗口由 fsync 策略决定,可做到接近零丢失。缺点:文件大、恢复慢(重放全部命令)。

这就是"Redis 的 AOF 像 WAL"说法的来源 —— 但见 3.4 的本质区别。

3.2 AOF 的 fsync 三档(决定丢多少)

appendfsync行为崩溃丢数据窗口适用
always每个写命令都 fsync几乎不丢(最多一条命令)强一致场景,吞吐最低
everysec(默认)每秒批量 fsync 一次最多丢 1 秒写入绝大多数生产场景
no交给 OS 决定何时刷不定,可能丢较多吞吐优先、可容忍丢失

注意"写 AOF buffer"与"fsync 到磁盘"是两件事:always 是每条命令都等落盘确认,最稳也最慢;everysec 用后台线程刷,兼顾吞吐与 ≤1s 丢失。

3.3 AOF 无限增长怎么办 → rewrite(重写)

AOF rewrite 原理(常考)

AOF 只增不改,跑久了会巨大且充满可抵消的命令(如对同一 key 的多次 set)。重写不是"整理旧文件",而是基于当前内存里的最新数据,生成一份能达到同样状态的最小命令集,写进新文件后原子替换。触发条件:文件超过上次 rewrite 后的一倍且 ≥64MB(auto-aof-rewrite-percentage / min-size)或手动 BGREWRITEAOF

fork子进程持当前内存快照视角(COW)
子进程写新 AOF遍历内存生成最小命令集,写入临时文件
父进程继续服务新命令照常追加旧 AOF,同时缓存到 rewrite buffer
合并收尾子进程写完,父进程把 rewrite buffer 追加进新文件,fsync 后原子改名替换

重写期间的阻塞点:fork 瞬间(大实例可能毫秒~百毫秒级,与内存页数相关);always 模式下父进程每次写命令还要 fsync 旧 AOF,会加剧卡顿 —— 生产多用 everysec。

3.4 Redis AOF 与 MySQL WAL 的本质区别(深度题)

MySQL redo(必需)

数据本体在磁盘。redo 是正确性部件:没有它,崩溃后磁盘数据可能不完整。日志记录的是"对磁盘数据的修改",重放后数据页回到正确状态,日志可以循环覆盖。

Redis AOF(保险)

数据本体在内存。不开持久化 Redis 照常运行,只是重启归零。AOF 记录的是"重建内存的命令流"(是数据本身的一种序列化),不是对某个落盘结构的增量修改。所以它必须全量保留(直到 rewrite 压缩),语义更接近 MySQL 的 binlog 而非 redo。

3.5 混合持久化(4.0+)与版本演进

恢复优先级与故障处理

启动时若 AOF 开启,优先用 AOF 恢复(比 RDB 更新);aof-load-truncated 允许在 AOF 尾部残缺(如断电半条命令)时自动截断并继续启动 —— 面试可提"生产上应先备份再启动,避免截断毁掉可修复数据"。

RDB 和 AOF 都开,重启用哪个?常考

用 AOF:因为它记录到更近的时间点,数据更全(尤其 everysec 只丢 ≤1s,而 RDB 可能丢数分钟)。4.0+ 混合格式下 AOF 头本身是 RDB,加载也快。Redis 官方默认配置两者都生成:AOF 保数据新鲜度,RDB 保快速恢复与备份/主从同步。

bgsave / rewrite 的 fork + COW 会不会阻塞主线程?必考

fork 由主线程执行,成本与进程内存页数相关,大实例 fork 期间会有毫秒到百毫秒级停顿(fork 耗时 指标可见);fork 后子进程独立跑,不阻塞。COW 期间父进程每改一个内存页就要复制该页,写密集时内存会短暂上涨(约等于写集大小),需给实例留内存余量,别让 maxmemory 顶满导致 fork 失败或触发淘汰。这是"Redis 单线程 + 持久化"最常见的生产坑。

主从架构下持久化该开在谁身上?从库全量同步用的什么?追问

全量同步 = 主库生成 RDB(磁盘版或 diskless 直传)给从库,之后主库把增量写命令通过 replication stream 推给从库继续追赶。生产建议:主库至少开 AOF(everysec),从库可按需开;若主库完全关持久化且重启,可能以空数据状态服务,触发从库也清空重同步(有保护机制但风险仍在)——所以"主库持久化不能省"。从库可开 RDB 做备份,降低对主库的 bgsave 压力。

Redis 只当缓存用,还开持久化吗?辨析

纯缓存(可接受全量重建)可以关掉 AOF 甚至全关,省掉 fork/刷盘开销;但不能接受的场景是"缓存里存了可重建代价高的数据或短暂当存储用"。面试加分表述:持久化开关取决于数据重建成本 × 丢失容忍度,Redis 本身不保证持久性语义,需要时自己开、自己测恢复演练。

Elasticsearch:translog + refresh / flush + 不可变段

ES 面试最爱问的两个词:refreshflush 的区别;以及为什么 ES 是"近实时"的。底层是 Lucene。

4.1 一条写入请求的完整旅程

① 写入主分片先写 translog(默认随请求 fsync),再进内存 buffertranslog = WAL
② refresh(默认 1s)buffer 生成新 segment,打开新 searcher → 可被搜索数据在 page cache,未落盘
③ 继续写入translog 继续增长,segment 留在 OS page cache
④ flush(Lucene commit)fsync 所有 segment 到磁盘 + 写 commit point + 清空 translog默认 30min 或 512MB 触发

refresh 管"可见性",flush 管"持久性" —— 这是全篇第二个核心口诀。refresh 前数据在内存 buffer,此时 搜不到,但已被 translog 保护;refresh 后能搜到,但若节点宕机且未 flush,磁盘上还没有它 —— 仍然靠 translog 恢复。

segment(段)

Lucene 的存储单元,不可变(写后不修改)。一次 refresh 把 buffer 固化成 1~N 个新段;查询扫全部分段再合并结果。不可变带来:无需加锁、可被 OS page cache 缓存、利于并发与压缩。

translog

ES 的 WAL:记录每个写操作的完整数据,默认 request 模式每次写请求后 fsync(可改 async,每 5s 刷)。作用是保证 buffer/新段里"还没真正落盘"的数据在崩溃后可重放。

commit point

flush 时写下的"检查点"文件,列出磁盘上所有已确认的 segment。崩溃恢复:从最近 commit point 载入段 → 重放其后 translog → 数据完整。

4.2 为什么能"近实时"而不是实时/同步落盘

实时可见 = 每条写入都建段并 fsync,但建大量小段 + 频繁 fsync 会让写入吞吐崩掉。于是 ES 用 index.refresh_interval(默认 1s)攒批建段 —— 写入确认很快(只要 translog 落盘),但最多延迟 1s 才可搜。想要更快可见可调小 interval 或调 refresh=true,代价是段更多、合并压力更大(大促/日志场景常临时调大或关掉 refresh 换吞吐)。

4.3 删除与更新:不可变段怎么改数据?

崩溃恢复与副本的关系

单副本下:节点宕机,未 flush 但已 fsync 的 translog 在磁盘上 → 重启后 commit point + translog 重放,默认 request 模式理论上不丢已确认的写入。多副本下由主分片负责 translog 同步/重放,副本从主同步数据。async 模式则最多丢约 5s(取决于 sync_interval 与数据量)。

refresh 和 flush 的区别?(标准回答模板)必考

refresh:把内存 buffer 生成新 segment 并打开搜索视图,让新数据可被搜到,但不 fsync(数据在 OS page cache,断电会没),默认 1s 一次,可手动 POST /_refresh。flush:执行 Lucene commit —— fsync 所有段到磁盘 + 写 commit point + 清空 translog,是真正的持久化动作,默认 30min 或 translog 达 512MB 自动触发(index.translog.flush_threshold_size)。一句话:refresh 管可见、flush 管持久;refresh 频繁则段碎,flush 由 translog 阈值兜底。

ES 会丢数据吗?已经返回写入成功的请求呢?常考

默认 index.translog.durability=request:主分片确认写入前 translog 已 fsync,节点进程崩溃/断电后靠 commit point + translog 重放,已确认的写入不丢(前提是磁盘本身没坏、没做 forcemerge 之类删 translog 的操作)。改成 async(每 5s 刷)会提高吞吐,但节点宕机可能丢最近几秒已确认的写入。真正会"丢"的常见场景:副本数为 0 + 分片所在的机器磁盘损坏、或集群脑裂后主分片数据被旧副本覆盖(需要 quorum/避免 split brain)。

segment 不可变带来哪些收益和问题?深度追问

收益:无锁并发读、可整段压缩、可整体进 page cache、崩溃恢复只需按段粒度校验。问题:更新/删除只能标记后靠 merge 清理(写入放大)、小段多了查询要合并很多结果集(段数量与查询延迟直接相关),所以 merge 策略(tiered:按大小分层合并)与 refresh 频率要一起调优。

对比一下:translog 和 MySQL redo log 是不是一回事?跨系统题

定位相同:都是"先于主数据落盘、用于崩溃后重放丢失部分"的 WAL。差异:① redo 记录页级物理修改、循环覆盖、只服务 InnoDB 恢复;translog 记录文档级操作数据(近似逻辑日志)、随 flush 清空、主要覆盖 refresh 产生的"可搜但未落盘"窗口。② redo 的提交确认和 fsync 是事务语义的一部分;ES 的写入确认绑定 translog fsync(request 模式),与 refresh 解耦。③ MySQL 没有"内存 buffer 中可见性延迟"概念(提交即可见),ES 有 refresh 这一层 —— 因为 ES 的"读"是搜索索引结构,需要先固化到段才能高效查。

横向对比:一张表讲完三种存储

维度MySQL / InnoDBRedisElasticsearch
数据本体在哪磁盘(16KB 数据页),Buffer Pool 只做缓存内存,磁盘只是备份/恢复源磁盘(segment),但写入先经内存 buffer + page cache
持久化的性质正确性必需(WAL)可选保险(纯缓存可全关)正确性必需(translog 兜底 refresh 空窗)
"先写日志"是哪个redo log(物理逻辑、循环写、组提交)AOF(命令流、纯追加、rewrite 压缩)translog(操作数据、随 flush 清空)
第二份日志binlog(逻辑,复制/PITR,两阶段提交对齐)RDB 快照(含混合持久化 AOF 头)Lucene commit point(落盘检查点)
日志内容粒度页号 + 页内偏移的修改完整写命令(RESP)完整文档操作
默认丢失窗口≈0(参数=1 每次提交 fsync)≤1s(everysec)或 ≈0(always)≈0(request 模式)或 ≤5s(async)
数据何时对读可见事务提交即对其它事务可见(隔离级别内)写命令执行后立即可见refresh 后(默认 ≤1s 延迟,近实时)
快照/合并机制checkpoint(推进 LSN、回收 redo)bgsave/RDB + AOF rewriteflush(fsync+commit point)+ segment merge
崩溃恢复checkpoint 起重放 redo → 对照 binlog 补/回滚 → undo 回滚未提交事务AOF 优先重放(4.0+ 混合格式先载 RDB 头)commit point 载入段 → 重放 translog
最大性能杀手随机刷脏页、redo 写满强制 checkpointfork 大内存停顿、always 模式每条 fsyncrefresh 过频段碎裂、merge 追赶不上
把三者串成一句话(面试收尾用)

凡是把"写"的确认建立在顺序日志上、把"数据本体"的落盘异步化的系统,都遵循 WAL 骨架;差别只在于 ① 数据本体在哪(决定日志是不是必需品)、② 日志记什么(决定重放/复制能力)、③ 读的可见性由哪一层控制(提交 / 直接生效 / refresh)。MySQL 三份日志分层最全,Redis 用快照+命令流给自己上保险,ES 用 translog 给"近实时可见"的缓冲窗口兜底 —— 各为各的数据模型服务。

追问自测:先口述,再点开对答案

模拟面试节奏:每题先自己讲 30~60 秒,讲完再展开答案,看漏了哪个点。

MySQL 已提交事务崩溃后为什么不会丢?redo log 是怎么和 Buffer Pool 配合的?热身

提交前 redo 已落盘(组提交合并 fsync)。崩溃后 Buffer Pool 里的脏页没了,但磁盘数据页还是旧值 → 启动时从 checkpoint LSN 开始重放 redo,把"已提交事务写过的页"重新改到最新。整个过程对客户端透明:确认提交的那一刻,数据就已经被日志"钉"在磁盘上了。

为什么 MySQL 需要两份日志(redo + binlog)?为什么两阶段提交?核心必考

职责不同:redo 保本机崩溃恢复(引擎层、物理、循环),binlog 保复制与时间点恢复(Server 层、逻辑、追加归档)。因为两份日志独立落盘,不原子,可能崩在中间导致主库与从库对"这个事务提交没有"判断不一致 → prepare 先让 redo 记住事务(含 XID),binlog 落盘成功才 commit;恢复时见 prepare 不见 binlog 的补提交(binlog 有就都执行)、只见 prepare 不见 binlog 的回滚。本质是分布式事务思想用在两个本地组件之间

从"一条 UPDATE 语句"讲到磁盘,说说 InnoDB 都动了哪些结构?综合题

① 定位页:按聚簇索引 B+ 树找到数据页,不在 Buffer Pool 则读盘。② 写 undo:保存旧值(回滚 + MVCC 链)。③ 改内存页并记 redo(物理逻辑变更)。④ 事务提交:redo 落盘(1 则 fsync,可组提交)→ 写 binlog 落盘 → redo 打 commit 标记(两阶段)。⑤ 异步:checkpoint 把脏页刷回磁盘;若页被部分写坏,doublewrite 提供整页副本。⑥ 老版本 undo 由 purge 清理。这一条线能串完 InnoDB 持久化全部考点。

Redis 重启恢复时,AOF 和 RDB 的加载顺序/优先级,以及 4.0 混合格式怎么加载?常考

AOF 开启则只加载 AOF(数据更新);没开 AOF 才读 RDB。混合格式下 AOF 文件 = RDB 二进制头(直接载入内存,秒级)+ 尾部增量命令(重放),加载远快于纯命令流,又不丢 rewrite 之后的细节。7.0 multi-part 则是按 manifest 依次加载 base + incr。

写入 QPS 很高时,三种系统各自的"第一瓶颈"分别是什么?生产题

MySQL:redo 落盘频率(靠组提交与参数 0/1/2 权衡)与磁盘 IOPS;脏页刷盘跟不上会写满 redo 强制 checkpoint。Redis:单线程 CPU 与 fork(持久化瞬间停顿、COW 内存放大),always 时 fsync 直接卡主线程。ES:translog fsync 频率(request 模式每请求一次)→ 用 bulk 批量写摊薄;refresh 每 1s 固化 buffer,段数增长 → merge 线程成为后台瓶颈。三者共同解法都是:批量、攒批、把 fsync 从热路径上摘掉或合并。

如果面试官问"Redis 的 AOF 算 WAL 吗?"怎么答?送命题→送分题

先给判断:形式上像 WAL(先落命令日志、崩溃重放),语义上更接近 binlog。WAL 的经典定义是"先于数据页修改写日志、日志服务于本机崩溃恢复、数据本体在磁盘";Redis 数据本体在内存,AOF 是数据的完整序列化(备份/重建用),不是对磁盘结构的增量。所以可以说"Redis 用 AOF 实现了 WAL 式的先写日志思想,但它的角色是持久化备份,不像 InnoDB redo 那样是正确性必需"。能分清楚这一层,说明真懂。

ES 写入返回成功 = 数据安全落盘了吗?为什么?必考

取决于 translog 模式:request(默认)下写入成功 = translog 已 fsync,即使 node 立刻宕机,重启后也能从 commit point + translog 找回 —— 数据是"安全的",但不在磁盘的 segment 上(可能还在内存 buffer 或 page cache 段里)。async 模式则成功 ≠ 落盘,最多丢几秒。另外注意"落盘安全"≠"可搜索":要等下次 refresh。三层解耦:translog 管不丢、refresh 管可见、flush 管最终落盘。

复习路线建议

第一轮只记三句话:MySQL = 改页前先写 redo(binlog 是复制用的另一份);Redis = 内存是本体,RDB 快照 + AOF 命令流二选一或混合;ES = refresh 管可见、flush 管持久、translog 兜底。第二轮按第五节表格逐格问自己"为什么"。第三轮用第七节问答做口述演练。祝面试顺利。