MongoDB含50万文档集合pymongo写入速度慢的优化咨询
系统信息
- 操作系统:Ubuntu 20.04 LTS
- 硬件配置:80GB内存、1TB SSD、i7-12700k
当前集合中文档平均大小16KB,累计已有50万条文档,随着集合规模扩大,写入耗时呈线性增长趋势。目前写入15万条文档需耗时10小时,与以下代码计算的预测结果一致:
def f(num): return 0.0004*num+0.9594 sum=0 for i in range(500,650): sum+=f(i*1000) >>> sum/3600 >>> 9.61497
潜在优化方向设想
- 使用C++ Mongo引擎进行写入
- 为Mongod分配更多内存
监控日志
iotop显示mongod仅使用不到1%的IO容量,写入速度约为10-20KB/shtop显示mongod仅占用约16GB内存- 磁盘尚有约300GB SSD空间可用
核心写入逻辑(伪代码)
docs=[...] for doc in docs: doc["last_updated"]=str(datetime.now()) doc_from_db = collection.find_one({"key":doc["key"]}) new_dict = minify(doc) if doc_from_db is None: collection.insert_one(new_dict) else: collection.replace_one({"key":doc["key"]},new_dict,upsert=true)
针对性优化方案
消除冗余查询,利用原生upsert特性
现有逻辑先执行find_one再判断插入/替换,完全可以直接通过replace_one的upsert=True参数完成原子操作,省去一次数据库读查询。修改后逻辑:docs=[...] for doc in docs: doc["last_updated"]=str(datetime.now()) new_dict = minify(doc) collection.replace_one({"key":doc["key"]}, new_dict, upsert=True)批量操作替代单条循环
单条循环会产生大量网络往返和数据库事务开销,改用bulk_write批量执行更新/插入:from pymongo import UpdateOne docs=[...] operations = [] for doc in docs: doc["last_updated"]=str(datetime.now()) new_dict = minify(doc) operations.append( UpdateOne( {"key": doc["key"]}, {"$replaceRoot": {"newRoot": new_dict}}, upsert=True ) ) # 每1000条执行一次批量提交 if len(operations) == 1000: collection.bulk_write(operations) operations = [] # 处理剩余未提交的操作 if operations: collection.bulk_write(operations)添加关键索引
检查key字段是否存在索引,若无则立即创建单字段索引:db.collection.createIndex({"key": 1})无索引时,
find_one和replace_one会触发全表扫描,这是写入耗时随数据量线性增长的核心原因之一。添加索引后查询效率会呈数量级提升,索引维护的开销远小于全表扫描的耗时。调整MongoDB内存缓存配置
当前mongod仅占用16GB内存,可将WiredTiger缓存大小调整为物理内存的50%-60%(比如40GB),让更多数据和索引驻留内存,减少磁盘IO。修改mongod.conf:storage: wiredTiger: engineConfig: cacheSizeGB: 40重启mongod生效。
优化时间字段存储
避免将datetime转换为字符串存储,直接存入MongoDB原生日期类型,既节省空间又消除转换开销:from datetime import datetime doc["last_updated"] = datetime.now()关于C++引擎的优先级
改用C++客户端确实能提升性能,但优先级低于上述优化项。Python的pymongo性能足够支撑需求,优先解决逻辑、索引和配置层面的瓶颈后,再考虑更换客户端。
内容的提问来源于stack exchange,提问作者user20289089
相关产品推荐
相关产品推荐

