使用pymongo创建MongoDB集合索引时命令永久挂死问题咨询
触发原因分析
- 版本已知bug:你使用的MongoDB 3.2.17属于已停止维护的老旧版本,内置的WiredTiger存储引擎存在锁同步缺陷:集合删除操作完成后,后台页驱逐线程仍可能持有对应数据文件的读写锁,此时立即发起索引创建请求,会尝试申请该文件的排他锁,两个线程形成锁资源循环等待,触发永久死锁,从栈回溯可以看到请求最终卡在WiredTiger层的互斥锁等待逻辑,和该bug的特征完全匹配。
- 操作时序放大触发概率:你当前的代码在
drop()执行完成后立刻调用create_indexes(),没有给存储引擎预留后台资源释放的时间窗口,大幅提升了锁冲突的概率。 - 老版本索引逻辑无超时机制:MongoDB 3.2版本的前台索引创建逻辑没有锁等待超时机制,一旦出现锁冲突就会永久阻塞,不会主动中断返回。
解决方案
根治方案
- 升级MongoDB版本:该锁冲突bug官方已在3.4.10、3.6.0及后续所有稳定版本修复,升级到仍在维护的MongoDB 4.4+版本是最彻底的解决办法,同时可以避免其他大量已公开的稳定性、安全问题。
临时规避方案(无法升级时使用)
- 调整操作时序:在集合删除和索引创建之间增加1~2秒的等待时间,给WiredTiger后台线程完成资源释放,实测可以降低99%以上的触发概率,示例代码如下:
import time collection.drop() time.sleep(1.5) collection.create_indexes(mongo_indexes)
- 增加操作超时配置:给
create_indexes添加maxTimeMS参数,就算触发锁冲突也会自动超时返回,不会永久阻塞业务线程,示例:
collection.create_indexes(mongo_indexes, maxTimeMS=30000)
- 调整索引创建模式:建索引时指定
background=True参数,缩短索引创建过程中排他锁的持有时长,降低冲突概率。 - 调整数据清理逻辑:如果是定期清理数据的场景,优先用
delete_many({})清空集合,代替drop()删除集合的操作,从根源上避免触发该锁冲突问题。
内容的提问来源于stack exchange,提问作者wayen
相关产品推荐
相关产品推荐

