我试图在 2D 天文坐标系中实施一个科学研究系统。我有一个过程可以生成大量地理空间数据,我们可以将这些数据组织在两个坐标和一个字符串值的文档中。另一方面,我有一小部分数据,其中只有一个坐标必须至少与生成的两个日期中的一个匹配。
简单来说,我将数据组织成两个集合:
-
collA
平均。大小:~2GB(时间恒定) -
collB
超过 50GB(持续增加)
地点:
collA
中文档的架构是:
{
terrainType: 'myType00001',lat: '000000123',lon: '987000000'
},{
terrainType: 'myType00002',lat: '000000124',{
terrainType: 'myType00003',lon: '997000000'
}
请注意,首先我们放置索引是为了避免 COLLSCAN。 collA
中有两个索引:__lat_idx
(唯一)和 __long_idx
(唯一)。我可以保证生成过程不会在批次列中的后期生成重复项(如上所示:lat 和 lon 有九位数字,但这只是为了简单......在实际情况下,这些值非常大)。
collB
中文档的架构是:
{
latOrLon: '0045600'
},{
latOrLon: '0045622'
},{
latOrLon: '1145600'
}
我尝试了一些不同的查询策略。
策略 A
let cursor = collB.find() // Should load entire 2GB into memory?
curosor.forEach(c => {
collA.find({lat: c.latOrLon})
collA.find({lon: c.latOrLon})
})
这需要对 collB
中的每个文档进行两次 mongo 调用:非常慢。
策略 B
let cursor = collB.find() // Should load entire 2GB into memory?
curosor.forEach(c => {
collA.find({$expr: {$or: [{$eq: [lat,c.latOrLon]},{$eq: [lon,c.latOrLon]} })
})
这需要对 collB
中的每个文档进行 mongo 调用,比 A 快但仍然很慢。
策略 C
for chunk in chunks:
docs = []
for doc in chunk:
CHUNK_SIZE = 5000
batch_cursor = collB.find({},{'_id': 0},batch_size=CHUNK_SIZE)
chunks = yield_rows(batch_cursor,CHUNK_SIZE) # yield_rows defined below
res = collA.find({
'$or': [
{
'lon': {
'$in': docs
}
},{
'lat': {
'$in': docs
}
}
]
})
这首先从 collA
获取 5000 个文档,然后将它们放入一个数组中并发送 find() 查询。效率高:collA
中每 5000 个文档调用一次 mongo。
此解决方案非常快,但我注意到随着 collA
大小的增加,速度会变慢,为什么?由于我使用的是索引,因此在计算时间方面搜索索引值应该花费 O(1)...例如,当 collA
的大小约为 25GB 时,执行一次大约需要 30 分钟完整查找()。现在这个集合是 50GB 大小,需要 2 个多小时。
我们计划下个月数据库至少达到 5TB,这将是一个问题。
我向大学要求使用 MongoDB 分片并行化这项工作的可能性,但这不会立即实现。我正在寻找一个临时解决方案,直到我们可以并行化这项工作。
请注意,我们尝试了不止这三种策略,我们混合了 Python3 和 NodeJS。
yield_rows 的定义:
def yield_rows(batch_cursor,chunk_size):
"""
Generator to yield chunks from cursor
:param cursor:
:param chunk_size:
:return:
"""
chunk = []
for i,row in enumerate(batch_cursor):
if i % chunk_size == 0 and i > 0:
yield chunk
del chunk[:]
chunk.append(row)
yield chunk