mongo???java???shell???????
Posted
技术标签:
【中文标题】mongo查询与java代码和shell花费不同的时间【英文标题】:mongo query which costs different time from java code and shell 【发布时间】:2015-04-10 06:25:06 【问题描述】:我有一个 mongo 查询,它花费的时间与 java 代码和 shell 不同。 Java代码如下,
mongo服务器的版本是v2.6.5,v2.4.8版本没有问题。
DBObject obj = new BasicDBObject();
obj.put("accountId",accountId);
List<DBObject> listOr = new ArrayList<DBObject>();
listOr.add(new BasicDBObject("status", 11));
listOr.add(new BasicDBObject("status", 12));
obj.put("$or", listOr);
BasicDBObject andDB = new BasicDBObject();
andDB.append("$gt", 0);
andDB.append("$lt",4514185);
obj.put("currBoardId",andDB);
DBCursor cur = null;
cur = coll.find(obj,new BasicDBObject("currBoardId",1)).sort(new BasicDBObject("commentId",-1)).limit(10);
当我运行代码时,我可以从配置文件集合中获取慢查询记录。它显示“nscanned”:1566031 和“millis”:4724。
> db.system.profile.find().sort($natural:-1).limit(10);
"op" : "query", "ns" : "l_comment.comment", "query" : "$query" : "accountId" : NumberLong(4), "$or" : [ "状态“:11 ,“状态”:12 ],“currBoardId”:“$gt”:0,“$lt”:NumberLong(4514185),“$orderby”:“commentId”:- 1 ,“cursorid”:220355902849,“ntoreturn”:10,“ntoskip”:0,“nscanned”:1566031,“nscannedObjects”:1566031,“keyUpdates”:0,“numYield”:5,“lockStats”: “timeLockedMicros”:“r”:NumberLong(8921271),“w”:NumberLong(0),“timeAcquiringMicros”:“r”:NumberLong(14),“w”:NumberLong(2), “nreturned”:10,“responseLength”:410,“millis”:4724,“execStats”:“type”:“PROJECTION”,“works”:1566031,“yields”:12234,“unyields”:12234,“无效”:0,“高级”:10,“needTime”:0,“needFetch”:0,“isEOF”:0,“children”:[“type”:“FETCH”,“works”:1566031,“产量”:12234,“unyields”:12234,“无效”:0,“高级”:10,“needTime”:1566021,“needFetch”:0,“isEOF”: 0,“alreadyHasObj”:0,“forcedFetches”:0,“matchTested”:10,“children”:[“type”:“IXSCAN”,“works”:1566031,“yields”:12234,“unyields”: 12234,“无效”:0,“高级”:1566031,“needTime”:0,“needFetch”:0,“isEOF”:0,“keyPattern”:“ commentId:-1.0 ”,“isMultiKey”:0 ,“boundsVerbose”:“字段#0 ['commentId']:[MaxKey,MinKey]”,“yieldMovedCursor”:0,“dupsTested”:0,“dupsDropped”:0,“seenInvalidated”:0,“matchTested”: 0,“keysExamined”:1566031,“孩子”:[] ] ],“ts”:ISODate(“2015-02-10T08:51:16.195Z”),“客户”:“192.168.66.103”, “所有用户”:[],“用户”:“”
但是当我从 shell 运行查询时,它返回很快。
db.comment.find( "accountId" : NumberLong(4), "$or" : [ "status" : 11 , "status" : 12 ], "currBoardId" : "$gt" : 0, "$lt" : NumberLong(4514185) ).sort(commentId:-1).limit(10)
下面是它的解释输出。它显示查询扫描 10517 条记录并使用 109 毫秒。 为什么会这样,我该如何改进代码?
感谢任何提示和帮助。
"clauses" : [
"cursor" : "BtreeCursor idx_atst",
"isMultiKey" : false,
"n" : 10,
"nscannedObjects" : 10517,
"nscanned" : 10517,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" :
"accountId" : [
[
NumberLong(4),
NumberLong(4)
]
],
"rootId" : [
[
"$maxElement" : 1
,
"$minElement" : 1
]
],
"status" : [
[
"$minElement" : 1
,
"$maxElement" : 1
]
],
"type" : [
[
"$minElement" : 1
,
"$maxElement" : 1
]
]
,
"cursor" : "BtreeCursor ",
"isMultiKey" : false,
"n" : 0,
"nscannedObjects" : 0,
"nscanned" : 0,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" :
"accountId" : [
[
NumberLong(4),
NumberLong(4)
]
],
"rootId" : [
[
"$maxElement" : 1
,
"$minElement" : 1
]
],
"status" : [
[
"$minElement" : 1
,
"$maxElement" : 1
]
],
"type" : [
[
"$minElement" : 1
,
"$maxElement" : 1
]
]
],
"cursor" : "QueryOptimizerCursor",
"n" : 10,
"nscannedObjects" : 10517,
"nscanned" : 10517,
"nscannedObjectsAllPlans" : 31574,
"nscannedAllPlans" : 31574,
"scanAndOrder" : false,
"nYields" : 246,
"nChunkSkips" : 0,
"millis" : 109,
"server" : "app-sz-2-3.sz.chosk.net:27017",
"filterSet" : false,
"stats" :
"type" : "KEEP_MUTATIONS",
"works" : 10529,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 10,
"needTime" : 10519,
"needFetch" : 0,
"isEOF" : 0,
"children" : [
"type" : "OR",
"works" : 10529,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 10,
"needTime" : 10519,
"needFetch" : 0,
"isEOF" : 0,
"dupsTested" : 10,
"dupsDropped" : 0,
"locsForgotten" : 0,
"matchTested_0" : 0,
"matchTested_1" : 0,
"children" : [
"type" : "SORT",
"works" : 10529,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 10,
"needTime" : 10518,
"needFetch" : 0,
"isEOF" : 1,
"forcedFetches" : 0,
"memUsage" : 4675,
"memLimit" : 33554432,
"children" : [
"type" : "FETCH",
"works" : 10518,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 1909,
"needTime" : 8608,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 1909,
"children" : [
"type" : "IXSCAN",
"works" : 10518,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 10517,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : " accountId: 1, rootId: -1.0, status: 1, type: 1 ",
"isMultiKey" : 0,
"boundsVerbose" : "field #0['accountId']: [4, 4], field #1['rootId']: [MaxKey, MinKey], field #2['status']: [MinKey, MaxKey], field #3['type']: [MinKey, MaxKey]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 10517,
"children" : [ ]
]
]
,
"type" : "SORT",
"works" : 0,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 0,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 0,
"forcedFetches" : 0,
"memUsage" : 0,
"memLimit" : 33554432,
"children" : [
"type" : "FETCH",
"works" : 0,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 0,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 0,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
"type" : "IXSCAN",
"works" : 0,
"yields" : 246,
"unyields" : 246,
"invalidates" : 0,
"advanced" : 0,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 0,
"keyPattern" : "",
"isMultiKey" : 0,
"boundsVerbose" : "field #0['accountId']: [4, 4], field #1['rootId']: [MaxKey, MinKey], field #2['status']: [MinKey, MaxKey], field #3['type']: [MinKey, MaxKey]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 0,
"children" : [ ]
]
]
]
]
【问题讨论】:
查询的不同之处在于,您在 Java 代码中使用new BasicDBObject("currBoardId",1)
限制返回的键,剥离结果应该需要一些时间,但不应更改扫描的对象。很有趣。
这让我很困惑
你能在java中显示变量accountId
的定义吗?
所有参数都在慢查询记录中,我在shell中运行从那里复制的查询。
从 Java 运行的查询和从 shell 运行的查询使用了不同的索引。从 Java 运行的查询使用索引来处理排序 "commentId" : -1
,而从 shell 运行的查询使用索引来处理实际的查询谓词。这解释了为什么 Java 查询的 nscanned
比 shell 查询高得多。这种索引选择是否每次都会发生?还是 Java 查询仅在某些时候选择排序索引?你需要其他的排序索引吗?能否将排序字段添加到 shell 查询的索引中?
【参考方案1】:
在您的 Shell 查询中,您在 $lt
的 $or
语句中使用 NumberLong(4514185)
。但是在您的 Java 查询中,您使用的是 andDB.append("$lt", 4514185);
这是一个 Integer
,因此您的 Java 查询需要一个从 Integer 到 Long 的类型转换。尝试在您的号码后使用 andDB.append("$lt", 4514185L);
和 L
以创建带有 Long 的查询。 accountId
也应该是 Long
。
【讨论】:
在我的代码中,参数类型是 Long ,我只是直接写了它们以显示示例。【参考方案2】:在 wdberkeley 的提示下,我注意到了索引。我不明白mongodb是如何选择索引的,但是在我添加另一个索引之后,来自java代码的查询会选择正确的索引。即使我删除后来添加的索引,它仍然使用正确的索引。所以如果查询没有使用我们想要的索引,我们应该使用hint来分配我们想要的索引。
我添加的索引:
ensureIndex("accountId" : -1,"type" : 1,"status" : 1,"name" : "idx_ats");
【讨论】:
以上是关于mongo???java???shell???????的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB - The mongo Shell, Configure the mongo Shell
MongoDB - The mongo Shell, Write Scripts for the mongo Shell
MongoDB - The mongo Shell, Access the mongo Shell Help
MongoDB - The mongo Shell, Data Types in the mongo Shell
Errr 'mongo.js:L112 错误:无法在 src/mongo/shell/mongo.js:L112 连接到服务器 127.0.0.1:27017'