mongo 与 java 代码和 shell 花费不同时间的查询

mongo query which costs different time from java code and shell

我有一个 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" : [ { "status" : 11 }, { "status" : 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, "invalidates" : 0, "advanced" : 10, "needTime" : 0, "needFetch" : 0, "isEOF" : 0, "children" : [ { "type" : "FETCH", "works" : 1566031, "yields" : 12234, "unyields" : 12234, "invalidates" : 0, "advanced" : 10, "needTime" : 1566021, "needFetch" : 0, "isEOF" : 0, "alreadyHasObj" : 0, "forcedFetches" : 0, "matchTested" : 10, "children" : [ { "type" : "IXSCAN", "works" : 1566031, "yields" : 12234, "unyields" : 12234, "invalidates" : 0, "advanced" : 1566031, "needTime" : 0, "needFetch" : 0, "isEOF" : 0, "keyPattern" : "{ commentId: -1.0 }", "isMultiKey" : 0, "boundsVerbose" : "field #0['commentId']: [MaxKey, MinKey]", "yieldMovedCursor" : 0, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0, "matchTested" : 0, "keysExamined" : 1566031, "children" : [ ] } ] } ] }, "ts" : ISODate("2015-02-10T08:51:16.195Z"), "client" : "192.168.66.103", "allUsers" : [ ], "user" : "" }

但是当我 运行 来自 shell 的查询时,它 returns 很快。

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" : [ ]
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }
}

在您的 Shell 查询中,您在 $or 语句的 $lt 中使用了 NumberLong(4514185)。但是在您的 Java 查询中,您使用的是 andDB.append("$lt", 4514185); 这是一个 Integer,因此您的 Java 查询需要从 Integer 到 Long 的类型转换。尝试在您的号码后使用 andDB.append("$lt", 4514185L);L 来创建长查询。 accountId 也应该是 Long.

在 wdberkeley 的提示下,我对索引进行了一些关注。我不明白 mongodb 如何选择索引,但在我添加另一个索引后,来自 java 代码的查询选择了正确的索引。即使我删除了后来添加的索引,它仍然使用正确的索引。所以如果查询没有使用我们想要的索引,我们应该使用提示来分配我们想要的索引。

我添加的索引:

ensureIndex({"accountId" : -1,"type" : 1,"status" : 1},{"name" : "idx_ats"});