bigywater.tech
MongoDB · 18

journal:kill -9 之后哪些写入还在

MongoDB 说写入成功了,进程被 kill -9 之后还在吗?这篇用一个单线程写入的客户端,在 w:1 和 j:true 下各 kill -9 多次,数丢了多少条,再改 journal 刷盘间隔、看恢复日志。

192~420 条
w:1 下 kill -9 后丢失的已确认写入(5 次试验,每次写入约 17,000 条)
0 条
j:true 下 5 次试验丢失的已确认写入,吞吐降到约 480 条 / 1.5 秒
96 / 198 / 306
journalCommitInterval 取 10、100、500 ms 时 w:1 丢失条数的中位数

方法

实测 · 本机执行输出
  丢失条数 = 客户端确认过的最大序号 - 恢复后的最大序号(正数表示“确认过但丢了”)

单线程不停地 insert_one,记录客户端收到确认的最大序号;1.5 秒后 SIGKILL,重启后看最大序号,差值就是“确认过但丢了”的条数。

w:1

实测 · 本机执行输出
  试验 1: 确认到 17,586  恢复后最大 _id 17,264  文档数 17,265  丢失  322
  试验 2: 确认到 15,432  恢复后最大 _id 15,025  文档数 15,026  丢失  407
  试验 3: 确认到 17,238  恢复后最大 _id 16,818  文档数 16,819  丢失  420
  试验 4: 确认到 16,495  恢复后最大 _id 16,092  文档数 16,093  丢失  403
  试验 5: 确认到 16,989  恢复后最大 _id 16,797  文档数 16,798  丢失  192

默认的 w:1 在写入进入内存与 journal 缓冲后就确认,不等 journal 落盘;kill -9 后每次丢 192~420 条。

j:true

实测 · 本机执行输出
  试验 1: 确认到    484  恢复后最大 _id    484  文档数    485  丢失    0
  试验 2: 确认到    482  恢复后最大 _id    483  文档数    484  丢失    0
  试验 3: 确认到    480  恢复后最大 _id    481  文档数    482  丢失    0
  试验 4: 确认到    472  恢复后最大 _id    472  文档数    473  丢失    0
  试验 5: 确认到    429  恢复后最大 _id    430  文档数    431  丢失    0

j:true 等 journal 落盘才确认,丢失为 0,代价是吞吐大幅下降(这里 1.5 秒约 480 条,对 w:1 的约 17,000 条)。

刷盘间隔

实测 · 本机执行输出
  commitIntervalMs=10   生效的配置 {'commitIntervalMs': 10};丢失条数 [0, 3, 24, 89, 102, 105, 155, 160],中位数 96,最大 160
  commitIntervalMs=100  生效的配置 {'commitIntervalMs': 100};丢失条数 [3, 10, 100, 183, 214, 456, 476, 502],中位数 198,最大 502
  commitIntervalMs=500  生效的配置 {'commitIntervalMs': 500};丢失条数 [12, 87, 136, 281, 331, 486, 499, 665],中位数 306,最大 665

journal 默认每 100 ms 刷一次盘。间隔调到 10 ms,中位数丢失降到 96 条;调到 500 ms 升到 306 条,但单次试验的波动很大。

恢复过程

实测 · 本机执行输出
  重启后日志里含 recover 的行: 47 条;msg 字段:
    Recovering data from the last clean checkpoint.
    Opening WiredTiger
    WiredTiger message
    WiredTiger recoveryTimestamp
    Opening spill WiredTiger
    mongod startup complete
  WiredTiger 的 recovery 日志里不同的消息 18 条,前几条:
    opening the WiredTiger library
    connection configuration string parsing completed
    starting WiredTiger utility threads
    starting WiredTiger recovery
    scanning metadata to remove all incomplete tables
    scanning metadata to find the largest file ID

重启时没有 checkpoint 可用(刚启动不到 60 秒),WiredTiger 的 recovery 日志记录了扫描元数据、重放 journal 的过程。

j:true 的连带效果

实测 · 本机执行输出
  试验 1: 确认到 17,807,恢复后最大 _id 17,807,文档数 17,809(含 _id=-1 的那条 j:true 写入),丢失 0
  试验 2: 确认到 16,382,恢复后最大 _id 16,382,文档数 16,384(含 _id=-1 的那条 j:true 写入),丢失 0
  试验 3: 确认到 16,504,恢复后最大 _id 16,504,文档数 16,506(含 _id=-1 的那条 j:true 写入),丢失 0
  试验 4: 确认到 18,576,恢复后最大 _id 18,576,文档数 18,578(含 _id=-1 的那条 j:true 写入),丢失 0

一次 j:true 的写入落盘时,journal 里它之前的所有写入也一起落盘,所以末尾补一次就能保住前面的 w:1 写入。

分步演示

kill -9 后的数据
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同。写线程每次 insert_one 约 130 字节,kill 用 SIGKILL(Python 的 Popen.kill);每次试验都是全新的 mongod
    • 没有验证:副本集 w:majority 的持久性(见后面的文章);断电与操作系统崩溃(只测了进程被杀,操作系统缓存里的数据不会丢);磁盘写缓存;不同硬盘的 fsync 耗时;journalCommitInterval 的效果在多次运行间波动很大,另一次运行里 500 ms 的中位数并没有高于 100 ms,原因没有查明;批量写入下的丢失量
    京ICP备2026067502号-1京公网安备11010502063487号