我正在尝试将当前存在于MS SQL 2005中的解决方案移植到MongoDB - 当前架构是结构化的,但是我们希望添加一个新级别的数据,而不是无模式的更多结构变体所以noSQL似乎还有很长的路要走。
数据的主要用途是分析性的,我们对数据进行了大量聚合。但是我在MongoDB中遇到聚合查询的困难而且我不确定问题出在哪里 - 这可能是我的期望,我对MongDB的知识有限(我只是在学习)或者可能是MongoDB不正确这项工作的工具。
我已阅读有关集合,索引和聚合的MongoDb手册。有一些简单的aggregation examples,作为我学习的一部分,我决定使用这些。
但是,在第一次尝试时(使用第一个示例),我发现性能比我预期的要慢,并且肯定比我用MS SQL实现的要慢。我的猜测是我做错了什么,但我真的不知道什么,我的直觉让我失望了 - 这就是我问这个问题。
测试 我想计算在一个字段上有多少实体匹配简单的'起始'查询。
这与SQL中的基本select count() from where
模式匹配,并使用MongoDB站点中的比较示例。
数据
原始数据有21 ^ 6行。在MongoDB中我有一个没有空白的单个集合,所以21 ^ 6个文档大约有17到40个字段(平均28)。
它有单一索引 - 我之所以选择它,因为它是我们分析中的常见字段。我让Mongo给它命名。
{
"n4" : 1
}
索引的大小约为2Gb,所以 - 如果我理解正确的话 - 应该有足够的空间来存储它。
在MSSQL中,这是一个包含40列的单个表。在MS SQL中,表具有以下索引
USE [v3_newDev]
GO
/****** Object: Index [n4] Script Date: 05/30/2014 13:34:19 ******/
CREATE NONCLUSTERED INDEX [n4] ON [dbo].[beta_3_dev_POST_coded]
(
[n4] ASC,
[pn4] ASC,
[ypCat Code] ASC
)
INCLUDE ( [SIC div],
[SIC 2]) WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
稍微复杂一点,但它是为一些查询设计的覆盖索引,最终我想在MongoDB上重现。
机器 双Xeon 5050四核,24Gb内存,慢速磁盘,但很多并且分区很好。在MS-SQL和Mongo中,各个部分都有自己的主轴。我是这个开发环境的唯一用户。
我做了什么
mongoDB中的第一个测试是简单计数/查询的复制。
db.main.find({n4:/^UKN/}).count()
在25.714秒内返回n = 2306913。
使用PerfMon进行检查表明没有发生任何磁盘活动,因此我猜测这一切都发生在内存中,但后来看起来很慢。
所以,我看一下解释只是为了确保......
db.main.find({n4:/^UKN/}).explain()
结果:
{
"cursor" : "BtreeCursor n4_1",
"isMultiKey" : false,
"n" : 2306913,
"nscannedObjects" : 2306913,
"nscanned" : 2306914,
"nscannedObjectsAllPlans" : 2306913,
"nscannedAllPlans" : 2306914,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 18022,
"nChunkSkips" : 0,
"millis" : 22255,
"indexBounds" : {
"n4" : [
[
"UKN",
"UKO"
],
[
/^UKN/,
/^UKN/
]
]
},
"server" : "rack01:27017",
"filterSet" : false,
"stats" : {
"type" : "FETCH",
"works" : 2306914,
"yields" : 18022,
"unyields" : 18022,
"invalidates" : 0,
"advanced" : 2306913,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
{
"type" : "IXSCAN",
"works" : 2306913,
"yields" : 18022,
"unyields" : 18022,
"invalidates" : 0,
"advanced" : 2306913,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : "{ n4: 1.0 }",
"boundsVerbose" : "field #0['n4']: [\"UKN\", \"UKO\"), [/^UKN/, /^UKN/]",
"isMultiKey" : 0,
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 2306914,
"children" : []
}
]
}
}
在那里一切似乎都没关系,它正在使用BTreeCursor进行索引扫描。但我正试图让它更快。
根据手册检查我注意到indexOnly
(设置为false
)在此方案中是不必要的,因此请尝试使用方法
._addSpecial( "$returnKey", true )
通过阅读手册和网络上的其他内容,此方法告诉查询计划程序仅使用索引中可用的n4字段
这确实更快......
db.main.find({n4:/^UKN/})._addSpecial( "$returnKey", true ).explain().n
在9.896sec中返回n = 2306913。
一项改进,但仍然比我想要的慢一个数量级,以及我能用MS SQL实现的目标。
我的下一步是使用.count()
方法返回_addSpecial()
方法,虽然这是徒劳的,因为这会产生所有
在129.575sec中返回n = 2306913。 (虽然在随后的测试中大约是23/25秒。)
如何改善MongoDB的查询时间?我可以通过索引或更好的查询模式做些什么吗?
在MS SQL 2005中,使用以下简单查询
SELECT COUNT(*)as n FROM v3_newDev..beta_3_dev_POST_coded 在哪里n4喜欢'UKN%'`
查询在<中返回n = 2306913 1秒。
根据我在这里写的内容,我可以用MongoDB做些什么来提高这个基本查询的性能?