bigywater.tech
MongoDB · 87

WiredTiger 的读写 ticket 与并发

WiredTiger 的读写 ticket 是什么?9.0 里还是 128 吗?并发超过 ticket 数时排队有多严重?这篇用 serverStatus 的 queues 对着真实排队验证。

10
空闲时 read / write 的 totalTickets(上限参数 128)
29
40 个并发慢读时 queueLength 的最大值
2737 → 1198 ms
16 个并发慢读,ticket 5 与 128 的总耗时

初始 ticket

实测 · 本机执行输出
  read.normalPriority  = {'out': 0, 'available': 10, 'totalTickets': 10, 'queueLength': 0, 'addedToQueue': 0, 'removedFromQueue': 0, 'totalTimeQueuedMicros': 0, 'finishedProcessing': 2}
  write.normalPriority = {'out': 0, 'available': 10, 'totalTickets': 10, 'queueLength': 0, 'addedToQueue': 0, 'removedFromQueue': 0, 'totalTimeQueuedMicros': 0, 'finishedProcessing': 5}
  参数: {'executionControlConcurrencyAdjustmentAlgorithm': 'throughputProbing', 'executionControlConcurrentReadTransactions': 128, 'executionControlConcurrentWriteTransactions': 128}

9.0.2 的 queues.execution.read/write.normalPriority 初始 totalTickets 是 10,executionControlConcurrentReadTransactions 等参数是上限 128。

排队

实测 · 本机执行输出
  单个慢读的耗时:1003 ms, 712 ms, 683 ms
  40 个并发:最快 822 ms,中位数 1808 ms,最慢 3052 ms;全部做完共 3057 ms;期间采样到 out 最大 = 11,queueLength 最大 = 29
  耗时分布(按 300 ms 一档的计数): {600: 3, 900: 7, 1200: 1, 1500: 9, 1800: 2, 2100: 8, 2400: 3, 2700: 6, 3000: 1}
  之后 read.normalPriority = {'out': 0, 'available': 9, 'totalTickets': 9, 'queueLength': 0, 'addedToQueue': 32, 'removedFromQueue': 32, 'totalTimeQueuedMicros': 45486758, 'finishedProcessing': 89}

40 个并发慢读时只有约 10 个同时在跑,queueLength 最大 29,最慢的操作比最快的多等了两秒多。

throughputProbing

实测 · 本机执行输出
  (秒, totalTickets, out, queueLength): [(3, 11, 11, 53), (6, 11, 11, 53), (9, 11, 11, 53), (12, 12, 12, 52), (15, 12, 12, 52), (18, 12, 12, 52), (21, 13, 13, 51), (24, 13, 13, 51), (27, 13, 13, 51), (30, 13, 13, 51)]

持续高负载下 ticket 数自己调整,但很慢:30 秒内只从 11 涨到 13。

固定并发

实测 · 本机执行输出
  setParameter: {'was': 'throughputProbing', 'ok': 1.0}
  setParameter executionControlConcurrentReadTransactions=4 → code 2 BadValue: Concurrent read transactions limit must be greater than or equal to 5.
  setParameter executionControlConcurrentReadTransactions=5: {'was': 128, 'ok': 1.0}
  参数: {'executionControlConcurrencyAdjustmentAlgorithm': 'fixedConcurrentTransactions', 'executionControlConcurrentReadTransactions': 5, 'executionControlConcurrentWriteTransactions': 128};read.normalPriority = {'out': 0, 'available': 5, 'totalTickets': 5, 'queueLength': 0, 'addedToQueue': 647, 'removedFromQueue': 647, 'totalTimeQueuedMicros': 1673589774, 'finishedProcessing': 1230}
  16 个并发:最快 686 ms,中位数 2044 ms,最慢 2736 ms;全部做完共 2737 ms;out 最大 = 5,queueLength 最大 = 11
  耗时分布(按 300 ms 一档的计数): {600: 4, 1200: 4, 1800: 5, 2100: 2, 2700: 1}

改成 fixedConcurrentTransactions 后读 ticket 不能低于 5;设成 5 时 16 个并发慢读排队、总耗时 2737 ms。

放大 ticket

实测 · 本机执行输出
  setParameter: {'was': 5, 'ok': 1.0}
  read.normalPriority = {'out': 0, 'available': 128, 'totalTickets': 128, 'queueLength': 0, 'addedToQueue': 660, 'removedFromQueue': 660, 'totalTimeQueuedMicros': 1688069294, 'finishedProcessing': 1262}
  16 个并发:最快 679 ms,中位数 982 ms,最慢 1197 ms;全部做完共 1198 ms;out 最大 = 16,queueLength 最大 = 0
  耗时分布(按 300 ms 一档的计数): {600: 6, 900: 10}

ticket 放大到 128,同样的负载没有排队,总耗时 1198 ms。

排队统计

实测 · 本机执行输出
  ticket = 5(16 个并发):addedToQueue +13,removedFromQueue +13,totalTimeQueuedMicros +14,479,520,finishedProcessing +32 → 排队的操作平均排队 1114 ms
  ticket = 128(16 个并发):addedToQueue +0,removedFromQueue +0,totalTimeQueuedMicros +0,finishedProcessing +32 → 没有排队

ticket = 5 时 addedToQueue +13,平均排队约 1114 ms;ticket = 128 时全是 0。

分步演示

WiredTiger ticket
本步的对象

    本文用到的工具与边界

    • 本机 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、时间戳等每次运行都不同。单机 mongod;用 $where 里 sleep(300) 的 find 占住 ticket,不是真实的磁盘/CPU 负载
    • 没有验证:真实 CPU 或磁盘密集负载下 throughputProbing 如何调整(sleep 的操作吞吐随并发线性增长,可能与真实负载不同);写 ticket 的行为;低优先级 ticket(lowPriority)与 deprioritization;同一个 find 为什么单独跑要 700 ms 以上(sleep(300) 的 JS 谓词被多次评估,没有进一步确认);旧版本 wiredTigerConcurrentReadTransactions 参数与 128 的默认值;在 Linux 多核机器上的结果
    京ICP备2026067502号-1京公网安备11010502063487号