Mongodb 在pymongo中加速$or查询
我在mongodb中存储了18亿条记录,每条记录如下:Mongodb 在pymongo中加速$or查询,mongodb,mongodb-query,pymongo,nosql,Mongodb,Mongodb Query,Pymongo,Nosql,我在mongodb中存储了18亿条记录,每条记录如下: { "_id" : ObjectId("54c1a013715faf2cc0047c77"), "service_type" : "JE", "receiver_id" : NumberLong("865438083645"), "time" : ISODate("2012-12-05T23:07:36Z"), "duration" : 24, "service_description" :
{
"_id" : ObjectId("54c1a013715faf2cc0047c77"),
"service_type" : "JE",
"receiver_id" : NumberLong("865438083645"),
"time" : ISODate("2012-12-05T23:07:36Z"),
"duration" : 24,
"service_description" : "NQ",
"receiver_cell_id" : null,
"location_id" : "658_55525",
"caller_id" : NumberLong("475035504705")
}
我需要获得200万特定用户的所有记录(我在文本文件中有感兴趣的用户id),并在将结果写入数据库之前对其进行处理。我在接收方id和调用方id上有索引(每个都是单个索引的一部分)
我目前的程序如下:
for user in list_of_2million_users:
user_records = collection.find({ "$or" : [ { "caller_id": user }, { "receiver_id" : user } ] })
for record in user_records:
process(record)
但是,使用用户_记录游标平均需要15秒(进程函数非常简单,运行时间很短)。这对于处理200万用户是不可行的。有没有加快$or查询速度的建议?因为这似乎是最耗时的一步
db.call_records.find({ "$or" : [ { "caller_id": 125091840205 }, { "receiver_id" : 125091840205 } ] }).explain()
{
"clauses" : [
{
"cursor" : "BtreeCursor caller_id_1",
"isMultiKey" : false,
"n" : 401,
"nscannedObjects" : 401,
"nscanned" : 401,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"caller_id" : [
[
125091840205,
125091840205
]
]
}
},
{
"cursor" : "BtreeCursor receiver_id_1",
"isMultiKey" : false,
"n" : 383,
"nscannedObjects" : 383,
"nscanned" : 383,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"receiver_id" : [
[
125091840205,
125091840205
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
"n" : 784,
"nscannedObjects" : 784,
"nscanned" : 784,
"nscannedObjectsAllPlans" : 784,
"nscannedAllPlans" : 784,
"scanAndOrder" : false,
"nYields" : 753,
"nChunkSkips" : 0,
"millis" : 31057,
"server" : "some_server:27017",
"filterSet" : false
}
这是收集的统计数据:
db.call_records.stats()
{
"ns" : "stc_cdrs.call_records",
"count" : 1825338618,
"size" : 438081268320,
"avgObjSize" : 240,
"storageSize" : 468641284752,
"numExtents" : 239,
"nindexes" : 3,
"lastExtentSize" : 2146426864,
"paddingFactor" : 1,
"systemFlags" : 0,
"userFlags" : 1,
"totalIndexSize" : 165290709024,
"indexSizes" : {
"_id_" : 73450862016,
"caller_id_1" : 45919923504,
"receiver_id_1" : 45919923504
},
"ok" : 1
}
我正在运行带有125GB内存的Ubuntu服务器
请注意,我将只运行此分析一次(而不是定期执行) 如果
caller\u id
和receiver\u id
上的索引是单个复合索引,则此查询将执行集合扫描而不是索引扫描。确保它们都是单独索引的一部分,即:
db.user_records.ensureIndex({caller_id:1})
db.user_records.ensureIndex({receiver_id:1})
您可以确认您的查询正在mongo shell中进行索引扫描:
db.user_records.find({'$or':[{caller_id:'example'},{receiver_id:'example'}]}).explain()
如果解释计划将其游标类型返回为BTreeCursor,则使用的是索引扫描。如果上面写的是BasicCursor,那么你正在做一个不好的收集扫描
了解每个索引的大小也很有趣。为了获得最佳查询性能,两个索引都应该完全加载到RAM中。如果索引太大,以至于只有一个(或者两个都没有!)适合RAM,那么您必须从磁盘将它们分页以查找结果。如果它们太大而无法放入RAM,那么您的选择就不是太好,基本上要么以某种方式拆分集合并重新编制索引,要么获得更多RAM。仅出于此分析的目的,您始终可以获得一个AWS RAM重实例,因为这是一件一次性的事情。我不知道为什么您的方法如此缓慢 但您可能希望尝试以下替代方法:
我不是MongoDB方面的专家,尽管我也遇到过类似的问题&以下解决方案帮助我解决了这个问题。希望它也能帮助你 Query使用索引并扫描精确的文档,因此索引没有问题,不过我建议您: 首先,尝试查看命令的状态:
mongostat--discover
有关页面错误
和索引未命中
等参数,请参阅
您是否尝试过预热(第一次执行查询后的查询性能)?热身后的表现如何?如果与前一个相同,则可能存在页面错误
如果要将其作为分析运行,我认为预热数据库可能会对您有所帮助。两个键都是单独索引的一部分。我得到了一个光标类型。索引大小如下:“'indexSizes:{“id”:73450862016,“caller_id_1”:45919923504,“receiver_id_1”:45919923504,“caller_id_1_receiver_id_1”:576743216}我使用的服务器有大量的RAM。您是如何获得这些索引大小的?我有点不愿意相信这些是你索引的实际大小,因为每一个都是45G。我会考虑一个拥有32GB RAM的服务器有很多,但它甚至不能适应一个索引。要了解更多信息,我将引导您访问。我从查询的explain()中获取了它们。我使用的服务器有125G RAM可用。我更新了我的问题以包含这些内容。@JedEstep每个索引项大约25字节听起来并不令人难以置信。你能为一个
用户记录
查询发布解释吗?您的代码需要15秒才能使用的问题?编辑了“我的问题”以包含.explain()的输出结果集很小,查询已正确索引。您如何知道MongoDB/pymongo是这里的性能问题?你在mongod日志中看到缓慢的操作了吗?@wdberkeley是的。。从mongod日志中可以看出,获得结果几乎需要15秒:1)整个数据库有400GB,比RAM大得多。因此,虽然查询索引本身很快,但它需要从磁盘加载记录本身。2)考虑在数据库中使用短字段名称,并且只在反序列化期间将它们映射到有意义的名称。这将使集合的大小减少1/3左右。