bigywater.tech
MongoDB · 56

事务 60 秒上限:到点之后发生什么

事务 60 秒的上限到点之后到底发生什么?是不是准点中止?事务外的写入会被它阻塞多久?这篇用 5 秒和 60 秒两个上限实测。

60 秒
transactionLifetimeLimitSeconds 默认值
70 s 成功,89 s 中止
默认 60 秒上限下:一个事务在 70 秒时提交仍成功,另一个到 89 秒才被服务端中止
251
过期事务继续操作或提交的错误码 NoSuchTransaction,带 TransientTransactionError 标签

上限

实测 · 本机执行输出
  # 默认值
  test> var r = db.adminCommand({getParameter: 1, transactionLifetimeLimitSeconds: 1, maxTransactionLockRequestTimeoutMillis: 1}); ({transactionLifetimeLimitSeconds: r.transactionLifetimeLimitSeconds, maxTransactionLockRequestTimeoutMillis: r.maxTransactionLockRequestTimeoutMillis})
  {
    transactionLifetimeLimitSeconds: 60,
    maxTransactionLockRequestTimeoutMillis: 5
  }

事务最长存活 60 秒(transactionLifetimeLimitSeconds),currentOp 里的 expiryTime 就是开始时间加 60 秒。

过期后

实测 · 本机执行输出
  currentOp 里事务的字段: ['expiryTime', 'parameters', 'readTimestamp', 'startWallClockTime', 'timeActiveMicros', 'timeInactiveMicros', 'timeOpenMicros']
  timeOpenMicros = 672;timeActiveMicros = 126;expiryTime - startWallClockTime = 60.0 秒;parameters = {'txnNumber': 2, 'txnRetryCounter': 0, 'autocommit': False, 'readConcern': {'level': 'local', 'provenance': 'implicitDefault'}}

事务被中止,修改回滚;继续操作或提交都报 251 NoSuchTransaction,带 TransientTransactionError,可以整个重试。

阻塞其他写入

实测 · 本机执行输出
  test> var r = db.adminCommand({setParameter: 1, transactionLifetimeLimitSeconds: 5}); ({was: r.was, ok: r.ok})
  { was: 60, ok: 1 }
  事务外对 A 的 update 等了 5357 ms 才完成(上限 5 s;期间事务里一直没有再操作)
  事务开始 5.4 s 后在事务里继续操作:code=251(NoSuchTransaction),errorLabels=['TransientTransactionError'];message: Transaction with { txnNumber: 1 } has been aborted., full error: {'errorLabels': ['TransientTransactionError'], 'ok': 0.
  commit:code=251(NoSuchTransaction),errorLabels=['TransientTransactionError']
  文档 A(_id 1)的 v = 0(事务里的 $inc 被回滚了),w = 1(事务外的写入生效)

事务改了文档不提交,事务外对同一个文档的写入要等到事务被中止或提交才能完成(上限 5 秒时等了 5.4 秒)。

不会续期

实测 · 本机执行输出
  每 0.5 秒操作一次:成功了 15 次后在 7.6 s 时失败 code=251(NoSuchTransaction)——即使一直在用,到上限也会中止

生命周期从事务开始算起,即使一直在操作也会过期。

不是准点

实测 · 本机执行输出
  含 transaction + expired/abort 的日志行 4 条;不同的 msg: ["Aborting transaction because it has been running for longer than 'transactionLifetimeLimitSeconds'", 'transaction']
  test> var r = db.adminCommand({setParameter: 1, transactionLifetimeLimitSeconds: 60}); ({was: r.was, ok: r.ok})
  { was: 5, ok: 1 }

过期事务由后台任务周期性清理:默认 60 秒的事务在 70 秒时提交仍成功,89 秒才被中止。所以别指望“恰好 60 秒”,事务要尽量短。

日志

实测 · 本机执行输出
  事务 A:70 s 时 commit 成功;文档 _id 4 的 v = 1
  事务 B(一直没动):在 89 s 时从 currentOp 里消失(被服务端中止),上限是 60 s
  B 的 commit:code=251(NoSuchTransaction),errorLabels=['TransientTransactionError'];文档 _id 3 的 v = 0(没有被修改)
  日志里 "Aborting transaction because it has been running for longer than transactionLifetimeLimitSeconds" 的记录数 5

服务端会记一条 “Aborting transaction because it has been running for longer than transactionLifetimeLimitSeconds”。

分步演示

事务的 60 秒上限
本步的对象

    本文用到的工具与边界

    • 本机 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、时间戳等每次运行都不同。单节点副本集;为缩短等待,前几节把 transactionLifetimeLimitSeconds 调到 5 秒,最后一节用默认的 60 秒真实等待 89 秒
    • 没有验证:清理任务的执行周期是否固定、与上限的关系(只观察到 5 秒上限在 5.4~7.6 s、60 秒上限在 89 s 中止);长事务对 WiredTiger 缓存与 history store 的占用;maxTimeMS 对事务内操作的作用;分片事务的超时;客户端 withTransaction 的 120 秒重试预算
    京ICP备2026067502号-1京公网安备11010502063487号