mongodb 百万_MongoDB查询性能超过500万条记录

面对MongoDB集合中超过2百万条记录的性能问题,通过添加复合索引和优化查询策略试图提升查询速度。然而,部分查询仍运行缓慢。建议包括检查内存使用、启用DB profiler、分析慢查询、优化索引选择性以及限制查询类型,以解决低选择性字段导致的大量文档扫描。在某些情况下,可能需要考虑使用搜索数据库来支持更复杂的查询需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们最近为我们的一个主要系列收录了> 2百万条记录,现在我们开始对该系列的主要性能问题感到不满。

它们在集合中的文档有大约8个字段,您可以使用UI过滤,结果应该按记录处理的时间戳字段排序。

我已经添加了几个复合索引与过滤的字段和时间戳

例如:

db.events.ensureIndex({somefield: 1, timestamp:-1})

我也添加了几个索引,同时使用几个过滤器,希望实现更好的性能。但一些过滤器仍然需要很长的时间来执行。

我确保使用解释查询确实使用我创建的索引,但性能仍然不够好。

我想知道如果分片是现在的方式去,但我们很快就会开始有每天约100万新的记录在该集合..所以我不知道如果它会扩展好..

编辑:查询的示例:

> db.audit.find({'userAgent.deviceType': 'MOBILE', 'user.userName': {$in: [[email protected]']}}).sort({timestamp: -1}).limit(25).explain()

{

"cursor" : "BtreeCursor user.userName_1_timestamp_-1",

"isMultiKey" : false,

"n" : 0,

"nscannedObjects" : 30060,

"nscanned" : 30060,

"nscannedObjectsAllPlans" : 120241,

"nscannedAllPlans" : 120241,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 1,

"nChunkSkips" : 0,

"millis" : 26495,

"indexBounds" : {

"user.userName" : [

[

"[email protected]",

"[email protected]"

]

],

"timestamp" : [

[

{

"$maxElement" : 1

},

{

"$minElement" : 1

}

]

]

},

"server" : "yarin:27017"

}

请注意deviceType在我的集合中只有2个值。

这是在大海捞针。对于那些性能不佳的查询,我们需要一些explain()输出。不幸的是,即使这将解决问题只有那个特定的查询,所以这里是一个如何处理这个策略:

>确保它不是因为RAM不足和过度分页

>启用DB profiler(使用db.setProfilingLevel(1,timeout),其中timeout是查询或命令需要的毫秒数的阈值,将记录任何更慢的阈值)

>检查db.system.profile中的慢查询,并使用explain()手动运行查询

>尝试识别explain()输出中的慢操作,例如scanAndOrder或大型nscanned等。

>关于查询的选择性的原因以及是否可以使用索引来完善查询。如果没有,请考虑不允许最终用户的过滤器设置,或给他一个警告对话框,操作可能很慢。

一个关键的问题是,你显然允许用户随意组合过滤器。没有指数交叉,这将大大地炸毁所需的指数的数量。

另外,盲目地在每个可能的查询上抛出索引是一个非常糟糕的策略。重要的是构造查询并确保索引字段具有足够的选择性。

假设您对状态为“有效”的所有用户以及其他一些条件进行了查询。但是在500万用户中,300万是活跃的,200万是不,所以超过500万条目只有两个不同的价值观。这样的索引通常不会帮助。最好先搜索其他条件,然后扫描结果。平均而言,当返回100个文档时,您必须扫描167个文档,这不会对性能造成太大的影响。但这不是那么简单。如果主要标准是用户的joined_at日期,并且用户停止使用时间的可能性很高,那么在找到100个匹配项之前,您可能需要扫描数千个文档。

因此,优化很大程度上取决于数据(不仅是其结构,而且数据本身),其内部相关性和查询模式。

当数据对于RAM太大时,事情变得更糟,因为那时索引很大,但是扫描(或者直接返回)结果可能需要从磁盘随机取得大量数据,这需要很多时间。

控制这种情况的最佳方法是限制不同查询类型的数量,禁止对低选择性信息的查询,并尝试阻止对旧数据的随机访问。

如果一切都失败了,如果你真的需要过滤器的灵活性,可能值得考虑一个单独的搜索数据库,支持索引交集,从那里获取mongo id,然后使用$ in得到mongo的结果。但这是充满了自己的危险。

– 编辑 –

您发布的解释是扫描低选择性字段的问题的一个漂亮的例子。显然,[email protected],查找这些文档并按时间戳降序排序是相当快的,因为它是由高选择性索引支持的。不幸的是,由于只有两种设备类型,mongo需要扫描30060文档,以找到第一个匹配’mobile’。

我假设这是某种类型的网络跟踪,用户的使用模式使查询慢(如果他每天切换移动和Web,查询会很快)。

使得特定查询更快可以使用包含设备类型的复合索引来完成,例如使用

a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})

要么

b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})

不幸的是,这意味着像find({“username”:“foo”})。sort({“timestamp”:-1}); can’t use the same index anymore,因此,如所描述的,索引的数量将增长非常快。

我恐怕没有非常好的解决方案这使用mongodb在这个时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值