0

我有一组具有以下架构的 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 中)。运行这 2 个查询 - 一个使用完整索引,另一个不使用。

> 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"
}

谢谢

标记

4

2 回答 2

1

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

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

在不了解您的数据分布和查询模式的情况下,很难确定什么是正确的方法,但作为一般规则,如果您可以选择字段的顺序,您希望将最有选择性的键放在最前面,将最不选择性的键放在最后。

于 2013-06-12T17:34:13.067 回答
0

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

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

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

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

db.tbl.find({ bottom: { $lte: 3266341261 } }).explain()
于 2013-06-12T12:56:17.440 回答