使用复合索引查询的倍数大于查询

时间:2013-06-12 12:08:36

标签: node.js mongodb mongoose

我有一系列CIDR范围,其中包含以下架构:

{ bottom: Number
top: Number
cidrText: String
live: Boolean
hits: Number }

我想要做的是查找ip是否在底部+顶部范围内(然后对该记录的点击执行$ inc更新。

目前我的索引是:

db.tbl.ensureIndex( { bottom: 1, top: 1, live: 1 } );

但是当我运行查询时 - 只有部分查询使用索引,这对我的服务器性能有很大影响:

我的查询是:

db.tbl.find({ live: true, top: { $gte: 3266341261 }, bottom: { $lte: 3266341261 } })

解释输出是:

{
    "cursor" : "BtreeCursor top_1",
    "isMultiKey" : false,
    "n" : 0,
    "nscannedObjects" : 0,
    "nscanned" : 0,
    "nscannedObjectsAllPlans" : 0,
    "nscannedAllPlans" : 7,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "top" : [
            [
                3266341261,
                1.7976931348623157e+308
            ]
        ]
    },
    "server" : "local:27017"
}

有没有办法可以让$ gte和$ lte同时使用相同的索引 - 或者我应该运行2个查询并在两个查询中找到匹配的行?

修改 我已经删除了所有现有的索引并重新添加了复合索引,现在我得到了正确的边界,但是nscanned仍然很高。

{
    "cursor" : "BtreeCursor bottom_1_top_1_live_1",
    "isMultiKey" : false,
    "n" : 0,
    "nscannedObjects" : 0,
    "nscanned" : 4748,
    "nscannedObjectsAllPlans" : 4746,
    "nscannedAllPlans" : 9494,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 15,
    "indexBounds" : {
        "bottom" : [
            [
                -1.7976931348623157e+308,
                3633904421
            ]
        ],
        "top" : [
            [
                3633904421,
                1.7976931348623157e+308
            ]
        ],
        "live" : [
            [
                true,
                true
            ]
        ]
    },
    "server" : "local27017"
}

我也在日志中得到这个:

warning: ClientCursor::yield can't unlock b/c of recursive lock ns: col.tbl top: { opid: 25701683, active: true, secs_running: 0, op: "query", ns: "tbl", query: { findandmodify: "tbl", query: { live: true, top: { $gte: 1584813140 }, bottom: { $lte: 1584813140 } }, sort: {}, new: 1, remove: 0, upsert: 0, update: { $inc: { hits: 1 } } }, client: "127.0.0.1:39407", desc: "conn581", threadId: "0x497ec940", connectionId: 581, locks: { ^: "w", ^tbl: "W" }, waitingForLock: false, numYields: 0, lockStats: { timeLockedMicros: {}, timeAcquiringMicros: { r: 0, w: 3 } } }

修改2

太清除“查找参数的顺序”看起来像搜索词的顺序确实需要匹配索引顺序(至少在2.4.4中)。运行这两个查询 - 一个使用完整索引而另一个不使用。

> db.tbl.find({top: {$lte: 1454442030}, bottom: {$gte: 1454442030}}).explain()
{
    "cursor" : "BtreeCursor top_1",
    "isMultiKey" : false,
    "n" : 2,
    "nscannedObjects" : 2271,
    "nscanned" : 2271,
    "nscannedObjectsAllPlans" : 6816,
    "nscannedAllPlans" : 11396,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 24,
    "indexBounds" : {
        "top" : [
            [
                -1.7976931348623157e+308,
                1454442030
            ]
        ]
    },
    "server" : "local:27017"
}

> db.tbl.find({bottom: {$lte: 1454442030}, top: {$gte: 1454442030}}).explain()
{
    "cursor" : "BtreeCursor bottom_1_top_1_live_1",
    "isMultiKey" : false,
    "n" : 2,
    "nscannedObjects" : 2,
    "nscanned" : 2080,
    "nscannedObjectsAllPlans" : 6240,
    "nscannedAllPlans" : 10400,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 23,
    "indexBounds" : {
        "bottom" : [
            [
                -1.7976931348623157e+308,
                1454442030
            ]
        ],
        "top" : [
            [
                1454442030,
                1.7976931348623157e+308
            ]
        ],
        "live" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ]
    },
    "server" : "local:27017"
}

由于

标记

2 个答案:

答案 0 :(得分:1)

扫描索引条目数量较多的原因是复合索引中键的选择顺序。索引中条目的顺序对于可以使用索引的查询以及查询必须扫描的索引条目的数量都很重要。

如果您的馆藏中live=true的条目百分比非常低,那么您最好以“live”作为第一个键来构建复合索引 - 因为任何有生命的查询= true将具有高度选择性。

在不知道您的数据分布和查询模式的情况下,很难确定什么是正确的方法,但作为一般规则,如果您可以选择字段的顺序,那么您希望将大多数选择性密钥放在第一位且选择性最低最后一次。

答案 1 :(得分:0)

警告与此find()查询无关。

另外,为什么你不认为nscanned不应该高?

基本上{bottom:{$ lte:3266341261}}必须检查与此匹配的所有索引记录,然后必须应用{top:{$ gte:3266341261}}过滤器。检查explaining()中的索引边界,我猜是按预期正确应用。

你可以运行以下。它应该接近{“nscanned”:4748}

db.tbl.find({ bottom: { $lte: 3266341261 } }).explain()