首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何同时使用MongoDB和$lte优化$gt查询?

问如何同时使用MongoDB和$lte优化$gt查询?
EN

Stack Overflow用户
提问于 2012-10-24 20:40:42
回答 4查看 10.1K关注 0票数 12

我有以下类似于反向范围查找的查询:

代码语言:javascript
复制
db.ip_ranges.find({ $and: [{ start_ip_num: { $lte: 1204135028 } }, { end_ip_num: { $gt: 1204135028 } }] })

当只使用$lte标识符运行时,查询将立即返回。但是,当我在同一个查询中同时使用$gt和$lte运行时,它非常慢(以秒为单位)。

start_ip_num和end_ip_num字段都有索引。

如何优化这个查询?

编辑

在查询中使用explain()函数时会得到以下信息:

代码语言:javascript
复制
{
    "cursor" : "BtreeCursor start_ip_num_1",
    "nscanned" : 452336,
    "nscannedObjects" : 452336,
    "n" : 1,
    "millis" : 2218,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {
        "start_ip_num" : [
            [
                -1.7976931348623157e+308,
                1204135028
            ]
        ]
    }
}

编辑2

添加复合索引后,explain()函数返回以下内容:

代码语言:javascript
复制
{
    "cursor" : "BtreeCursor start_ip_num_1_end_ip_num_1",
    "nscanned" : 431776,
    "nscannedObjects" : 1,
    "n" : 1,
    "millis" : 3433,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {
        "start_ip_num" : [
            [
                -1.7976931348623157e+308,
                1204135028
            ]
        ],
        "end_ip_num" : [
            [
                1204135028,
                1.7976931348623157e+308
            ]
        ]
    }
}

然而,perf仍然很差(以秒计)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-10-24 20:56:42

因此,在Mongo中,双范围查询是不明智的。我假设您有一个包含这两个{start_ip_num: 1, end_ip_num: 1}的索引。

如果这还不够接近(如果第一个字段返回了足够多的数据(因为它需要进行大量的B树扫描),那么使用2D框查询(一次只能在两个范围内工作)有一个技巧可以解决这个问题。

基本上,在一个包含数组中两个点的字段上放置一个2D地理索引,比如start_ip、end_ip,并给它一个足够高的min/max值,这样它就不会达到默认的-180/180的极限。

最后,使用一个边界查询,其范围从min到框的一个角落的$lte值,而gt和最大值在盒子的另一个角落。有关语法,请参见http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-BoundsQueries。

看起来是这样的:

代码语言:javascript
复制
db.ip_ranges.find({ip_range:{$within:{$box:[[0, 1204135028], [1204135028, max]]}}});

其中最大的ip是你能拥有的。

我已经有一段时间没有看过这个了,所以这个框可能是错的,但是这个概念是合理的,它使得双范围查询的性能比普通的两个字段B树索引要好一些。一直不到一秒钟(虽然通常是几百毫秒),而普通索引只有几秒钟--我想当时我有数亿个文档,但是已经有一段时间了,所以用一点盐来看这些记忆中的基准。结果会有很大的差异,取决于您的数据和范围大小,我相信。

更新:您可能希望尝试使用bits设置,尝试一个低数字和高数字,看看它是否有区别。对我来说,这似乎并没有影响平均查询。有关语法,请参见http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-CreatingtheIndex。

票数 9
EN

Stack Overflow用户

发布于 2016-01-07 18:11:48

根据Ip2location网站,可以使用mongodb实现对ip地址的快速查询,而不需要范围查询。仅在mongodb { ip_to: 1 }上创建一个索引,并使用以下方法查询ip:

代码语言:javascript
复制
db.collection_name.find({ ip_to: { $gte : ip_integer } }).sort({ ip_end: 1 }).limit(1)

使用此配置,我获得了6万个文档集合的1ms查询时间。

票数 8
EN

Stack Overflow用户

发布于 2015-04-17 07:06:19

诀窍是使用一个$lte和一个排序。我把查询降到了几毫秒。

我也遇到了同样的问题--找到哪个CIDR块匹配了特定的IP地址。我还尝试使用$gte和$lte,得到了10秒的响应时间。

我以不同的方式解决了这个问题。注意,MaxMind数据库中的CIDR块( IP地址范围)不重叠。每个IP地址最多只能匹配一个结果。所以,您需要做的就是找到CIDR块,其中最大的start_ip_num小于特定的IP地址。然后在应用程序代码中验证end_ip_num是否大于特定的IP地址。

下面是代码(使用节点MongoDB客户机):

代码语言:javascript
复制
// Convert IP address to base 10.
var ipToDecimal = function (ipAddress) {
  var split = ipAddress.split('.');
  return (split[0] * 16777216) + (split[1] * 65536) + (split[2] * 256) + (+split[3]);
};

var ipAddress = '1.2.3.4';
var ipDecimal = ipToDecimal(ipAddress);

db.ip_addresses.find({start_ip_num: {$lte: ipDecimal}}, {_id: 0, country_code: 1, end_ip_num: 1}, {limit: 1, sort: [['start_ip_num', -1]]}).toArray(function (error, ipAddresses) {
  if (ipAddresses[0] && ipAddresses[0]['end_ip_num'] >= ipDecimal) {
    console.log('IP address found: ', ipAddresses[0]['country_code']);
  } else {
    console.log('IP address not found.');
  }
});

确保在start_ip_num上创建索引。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13057387

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档