mongodb - 旋转MongoDB收集效率
问题描述
我有一个 MongoDB 集合,其中包含大约 20M 个对象。每晚,我都会删除几个月的数据,并用从 CSV 导入的数据替换它们,该导入使用 PyMongo 3.0.something 通过 Django。随着数据库的增长,此导入所需的时间会越来越长,并且服务器和进程偶尔会开始冻结。在我们的 AWS 服务器上,仅删除(600 万行)就开始需要大约 14 分钟。我已经按日期索引了删除,这是适当的字段。
我用来删除的查询是:
db.getCollection("x").remove({date: {$gte: ISODate('2018-04-01')}})
顶层 Pymongo 集成代码,可能有轻微的内存泄漏,是:
while keep_fetch:
raw_data = input_adapter.get_bulk() # in practice pulling 100,000 lines.
if raw_data is None:
raise Exception("bulk of data didn't return due to error,remove all last inserted docs")
if raw_data == {} or raw_data == [] :
log.debug("no more data to fetch")
keep_fetch = False
break
#check for maintenance before insert first bulk
if first_bulk == True: # first run-through, execute a delete if there is data.
first_bulk = False
#clean data in db to avoid duplicates
if 'maintain_before_insert' in dir(output_adapter):
# Call function that cleans a time period's data from the database, and does other maintenance.
if output_adapter.maintain_before_insert() == False:
raise Exception("collection maintenance failed")
#Run an input adapter that formats the data into a list for insertion into Mongo.
list_docs=input_adapter.format_data( raw_data )
#get last date for next task to start from
last_date = max(list_docs, key=lambda x:x['date'])['date']
output_adapter.store_bulk( list_docs ) # Mongo insert; it uses unmodified insert_many on the collection
gc.collect()
我很好奇如何加快这个过程。我尝试了批量删除,但没有任何改进。我对即将到期的索引很好奇,但我不知道当我们的进口过时时到期索引是否会停止(拖欠整整 3 个月后,它会回落到 2 并且每晚增加一天)
AWS 服务器大小的增加似乎不会影响处理速度。我们使用 Django / Python 服务器来发送删除和插入查询,插入和删除过程似乎花费了大约相同的时间。
服务器没有交换文件;这可能是服务器设置问题,因此更适合 ServerFault,但由于可能存在我在这里询问的代码错误。
我已经进行了大量的研究并尝试了几种可能性。升级我们的 PyMongo 版本可能会有所帮助吗?
过程数据,过程 1
插入单个 (~ 675 MiB) CSV 文件的性能指标:从 S3 存储桶下载文件:0.45 秒删除记录(4 月至 6 月未完成):在 14:18 删除 6,200,000 条记录存储记录:每 11 插入 100,000 条记录-12 秒 [注意:在删除之前将前 100,000 个加载到 Python 内存中,因此数字不准确] 20:15:21 和 20:32:11,总共添加了 16:50 和 6,798,155 条记录
后处理表大小:计数:19,367,660;大小:7.5 GiB,存储大小:3.6 GiB,索引:2,总索引大小:486.3 MiB
过程数据,过程 2
插入单个 (~ 225 MiB) CSV 文件的性能指标:从 S3 存储桶下载文件:0.5 秒删除记录(4 月至 6 月未完成):2:51 删除 2,399,827 条记录存储记录:每 15 插入 100,000 条记录' [注意:删除前将前 100,000 个加载到 Python 内存中,因此数字不准确] 20:37:10 和 20:43:37 之间的秒数,总共添加了 6:27 和 2,417,669 条记录
后处理表大小:计数:6,332,293;大小:2.5 GiB,存储大小:1.1 GiB,索引:3,总索引大小:264.3 MiB
过程数据,过程 3
插入单个 (~ 20 MiB) CSV 文件的性能指标:从 S3 存储桶下载文件:0.03 秒删除记录(4 月至 6 月未完成):22 秒内删除 341,317 条记录存储记录:每 21' 插入 100,000 条记录[注意:在删除之前将前 100,000 条加载到 Python 内存中,因此数字不准确] 20:44:46 和 20:45:17 之间的 ish 秒数,总共添加了 0:31 和 349,100 条记录
后处理表大小:计数:811,200;大小:145.7 MiB,存储大小:75.6 MiB,索引:3,总索引大小:23.4 MiB
过程数据,过程 4
插入单个(~ 20 MiB)CSV 文件的性能指标:从 S3 存储桶下载文件:0.5 秒删除记录(4 月至 6 月未完成):352,010 条记录在 :09 中删除 记录存储:每 8 分钟插入 100,000 条记录[注意:在删除之前将前 100,000 条加载到 Python 内存中,因此数字不准确] 20:46:05 和 20:46:38 之间的 ish 秒,总共添加了 0:33 和 357,040 条记录
后处理表大小:计数:821,924;大小:217.7 MiB,存储大小:95.3 MiB,索引:3,总索引大小:22.9 MiB
笔记
删除时的性能似乎非常非线性。看起来我应该在重新索引之前以某种方式批量删除,而我不是;我可以以某种方式“锁定”该集合以进行删除和添加(除了此过程之外它未被使用)或创建一个单一事务来阻止索引重新计算中间过程以便之后重新计算?
要求的资料:
1) 我们的服务器是 EC2 实例,在 Django / Python 端运行 18.04,在 Mongo 端运行 14.04。Mongo 版本是 3.4.3,Wired Tiger 引擎。
2) 我检查了 explain() 输出,JSON 输出如下所示:
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"date" : -1.0
},
"indexName" : "date_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"date" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"date" : [
"[new Date(9223372036854775807), new Date(1522540800000)]"
]
}
}
我的结论是该指数正在发挥作用。
3) 'date' 字段被索引,'id' 字段也是如此
4)目前Mongo端的硬件是一个t2.small或者t2.medium的Mongo服务器。将实例从 t1 升级到 t2 似乎对删除速度几乎没有影响,尽管中型服务器没有冻结,这很好。
5) 每个驱动器的卷是一个基于云的 gp2 AWS 实例。这有一个理论上 160 MiB/s 的限制,我添加的行总计为 7.5GiB,索引大小为 486.3MB。
解决方案
推荐阅读
- reactjs - 如何在 React 中使用 AG-Grid?我收到构建错误
- mysql - 当其中一张表为空时,SQL 查询不给出任何结果
- r - 按值过滤 R 数据集,但保留来自相同记录号的行
- google-apps-script - 尝试访问 Google 脚本时出现“无法访问此页面”错误
- java - JAVA - 嵌套在jsp中的三元组
- scala - 如何为 appJS/appJVM 交叉构建项目运行 scala sbt-native-packager
- javascript - Angularjs Angular-Material后退按钮执行两次
- c# - 如何在 dotnet pack nuget 包中包含来自构建输出 (dacpac) 的非 dll 文件?
- python - 从给定数据框的一列的所有行中提取粗体字母
- sql - 如何查找只有一个值的列 - Postgresql