先确认write.available是否为0且write.out接近totaltickets,再查currentop中长时间运行事务;若cpu与iowait正常、慢查询已优化、cache压力小,才可能是真票据耗尽。

怎么判断是不是真被WiredTiger票据卡住了?
看到写入变慢、mongostat里qw持续升高,别急着调wiredTigerConcurrentWriteTransactions。先确认瓶颈确实在WT层:
- 运行
db.serverStatus().wiredTiger.concurrentTransactions,看write.available是否真为0,且write.out接近write.totalTickets - 查活跃操作:
db.currentOp({ "secs_running": { "$gt": 10 }, "transactionState": "inProgress" }),如果返回大量>30秒的事务,说明票据耗尽是表象,长事务才是根因 - 检查
mongostat输出中iowait和CPU利用率:若CPU
为什么调高writeTickets反而让系统更卡?
WiredTiger的写票证(write ticket)不是“并发数上限”,而是受内存、CPU和eviction能力约束的动态资源。硬调高会触发连锁反应:
- 每个写事务都要维护快照,
writeTickets设得过大 → 更多事务并行 →wiredTiger.cache.bytes_dirty飙升 → eviction线程跟不上 → 触发WT_CACHE_FULL错误 - MongoDB 7.0+默认启用动态算法,
totalTickets可能远低于你设的值;盲目设到256(32核机器)会导致上下文切换(vmstat 1中cs突增2×以上) - 写票超过读票(如写192/读128)易引发读请求排队,因为后台eviction依赖读票完成page回收
不调参也能释放票据的实操手段
真正有效的解法是让事务“快进快出”,把票据还给队列:
- 所有事务代码必须包在
try/catch/finally里,finally中强制调用session.abortTransaction()——哪怕逻辑已成功,也要防网络中断导致会话悬空 - 服务端设
transactionLifetimeLimitSeconds: 30(低于默认60),配合客户端maxIdleTimeMS: 25000,双重保障长事务自动终结 - 监控
secs_running > 10的事务,用db.killOp(opId)主动终止;别等它自己超时,那会锁住cache页面直到TCP连接断开 - 禁止在事务内调用外部API、
sleep()或find().toArray()——这些操作不产生数据库锁,但无限延长快照生命周期,让票据“假性耗尽”
哪些事务模式会悄悄吃掉大量票据?
这些看似合法的操作,实际在WiredTiger层制造票据黑洞:
- 分片集群中跨分片读写:即使只读一个集合,若该集合chunk分散在多个shard,事务会升级为两阶段提交,每个shard独占一张写票,且无法释放直到全局提交
- 事务内执行
count()或distinct():它们触发全集合扫描,加载大量page进cache,且因快照机制无法evict,间接挤占后续写票空间 - 同一
ClientSession反复startTransaction():MongoDB不支持嵌套事务,但会延续最外层快照,导致cache锁定时间翻倍,票据被长期占用 - 批量写入混用索引字段:比如对
{ts: 1, status: 1}索引做bulkWrite,每条文档都更新两个索引页,dirty page生成速度远超eviction能力
票据本身不稀缺,稀缺的是事务结束后及时归还它的能力。多数“票据耗尽”报警背后,是未关闭的会话、未终止的慢查询,或应用层把事务当成了通用执行容器——这比参数调错更难排查,也更常被忽略。











