jjzjj

performance - 带排序的 MongoDB 地理空间查询 - 性能问题

coder 2023-11-01 原文

我有查询(非常慢 ~2,5s):

db.markers.find({ latlng: { '$within': { '$box': [ [ -16, -140 ], [ 75, 140 ] ] } } }).sort({_id: -1}).limit(1000)

当我为这个查询运行 explain 时,我得到了

{
   "cursor" : "GeoBrowse-box",
   "isMultiKey" : false,
   "n" : 1000,
   "nscannedObjects" : 242331,
   "nscanned" : 242331,
   "nscannedObjectsAllPlans" : 242331,
   "nscannedAllPlans" : 242331,
   "scanAndOrder" : true,
   "indexOnly" : false,
   "nYields" : 1383,
    "nChunkSkips" : 0,
    "millis" : 2351,
    "indexBounds" : {
        "latlng" : [ ]
    },
    "lookedAt" : NumberLong(262221),
    "matchesPerfd" : NumberLong(242331),
    "objectsLoaded" : NumberLong(242331),
    "pointsLoaded" : NumberLong(0),
    "pointsSavedForYield" : NumberLong(0),
    "pointsChangedOnYield" : NumberLong(0),
    "pointsRemovedOnYield" : NumberLong(0),
    "server" : "xx:27017"
}

当我删除 sort({_id: -1}) explain 给我(快速查询 5 milis):

{
    "cursor" : "GeoBrowse-box",
    "isMultiKey" : false,
    "n" : 1000,
    "nscannedObjects" : 1000,
    "nscanned" : 1000,
    "nscannedObjectsAllPlans" : 1000,
    "nscannedAllPlans" : 1000,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 5,
    "indexBounds" : {
        "latlng" : [ ]
    },
    "lookedAt" : NumberLong(1000),
    "matchesPerfd" : NumberLong(1000),
    "objectsLoaded" : NumberLong(1000),
    "pointsLoaded" : NumberLong(0),
    "pointsSavedForYield" : NumberLong(0),
    "pointsChangedOnYield" : NumberLong(0),
    "pointsRemovedOnYield" : NumberLong(0),
        "server" : "xx:27017"
}

我在 latlng 上有 2d 索引,在 _id 和 compound 索引上有 desc 索引。

db.markers.ensureIndex({latlng: '2d', _id:-1})
db.markers.ensureIndex({ latlng: '2d' })
db.markers.ensureIndex({ _id: -1 })

我想要实现的是从最新排序的特定区域获取标记。

有什么想法或建议可以让时间少于 2.5 秒吗??

如果有人想自己做测试

var i = 0,
  lat = 0,
  lng = 0;

for (i; i < 260000; i++) {
  lat = parseFloat(Math.min(-90 + (Math.random() * 180), 90).toFixed(6));
  lng = parseFloat(Math.min(-180 + (Math.random() * 360), 180).toFixed(6));
  collection.insert({latlng: [lat, lng]}, function () {});
}

collection.find({ latlng: { '$within': { '$box': [ [ -90, -180 ], [ 90, 180 ] ] } } }, {latlng: 1, _id: 1 }).sort({_id: -1}).limit(1000).explain()

在我的本地机器上,我收到(~ 2,6s):

{
    "cursor" : "GeoBrowse-box",
    "isMultiKey" : false,
    "n" : 1000,
    "nscannedObjects" : 260000,
    "nscanned" : 260000,
    "nscannedObjectsAllPlans" : 260000,
    "nscannedAllPlans" : 260000,
    "scanAndOrder" : true,
    "indexOnly" : false,
    "nYields" : 1612,
    "nChunkSkips" : 0,
    "millis" : 2613,
    "indexBounds" : {
            "latlng" : [ ]
    },
    "lookedAt" : NumberLong(260000),
    "matchesPerfd" : NumberLong(260000),
    "objectsLoaded" : NumberLong(260000),
    "pointsLoaded" : NumberLong(0),
    "pointsSavedForYield" : NumberLong(0),
    "pointsChangedOnYield" : NumberLong(0),
    "pointsRemovedOnYield" : NumberLong(0),
    "server" : "xx:27017"
}

谢谢

最佳答案

您的集合中是否真的定义了以下三个索引?

db.markers.ensureIndex({ latlng: '2d', _id:-1 })
db.markers.ensureIndex({ latlng: '2d' })
db.markers.ensureIndex({ _id: -1 })

geospatial indexing文档建议不要在同一集合上创建多个地理索引。尽管 MongoDB 允许这样做,但这种行为可能并不理想。我对您的情况的猜测是,可能已选择使用非复合 {latlng: '2d'} 而不是复合索引。 explain() 输出在这里并没有真正帮助我们,因为它只是报告 GeoBrowse-box 而不是索引名称;但是,我会建议手动 hinting游标使用复合索引并查看结果是否有所改善。或者,简单地摆脱非复合索引,所以 {latlng: '2d', _id:-1} 因为查询优化器显而易见且唯一的选择。

最后,{_id: -1} 索引是多余的,可以删除。根据 compound index文档,方向仅在处理由多个字段组成的索引时才相关。对于单键索引,我们可以很容易地向后或向前遍历索引。由于 MongoDB 已经默认为我们创建了一个 {_id: 1} 索引,因此简单地依赖它会更有效。

现在,索引不受影响:查询的一个警告是,在按非地理条件(在您的情况下为 _id)排序之前,限制应用于地理空间查询组件。我相信这意味着,虽然您的结果确实会按 _id 排序,但该排序可能不会考虑匹配范围内的所有文档。 compound index 中提到了这一点文档的一部分,其中引用了 SERVER-4247作为悬而未决的解决方案。


编辑:跟进您的基准

我填充了示例数据,它们是 ±90 到 ±180 之间的 260k 个随机点。然后我运行了您的查询:

db.markers.find(
  { latlng: { $within: { $box: [[-90, -180], [90, 180]] }}},
  { latlng: 1, _id: 1 }
).sort({_id: -1}).limit(1000).explain()

这花费了 1713 毫秒(我将用它作为比较基准,而不是你的 2351 毫秒)。我还会注意到查询匹配了所有 260k 文档,并扫描了相同数量的索引条目。似乎限制在 _id 排序之前没有考虑,这不是我根据注释 here 所期望的。 .然后我稍微调整了查询​​以检查其他一些情况:

  • 没有 _id 排序和限制的原始查询:nscanned 为 260k,时间为 1470ms。
  • 没有 _id 排序的原始查询:nscanned 为 1000,时间为 9ms。
  • 无限制的原始查询:nscanned为260k,时间为2567ms。

我还想单独测试未索引字段的排序,以模拟 _id 排序在地理匹配后可能发生的情况;但是,我无法使用 _id,因为默认索引将始终存在。为此,我删除了复合地理索引,然后按 latlng 对象排序。这导致 260k 的 nscanned 和 1039ms 的时间。如果我加上 1000 的限制,则时间为 461 毫秒。

如果我们将其添加到上面的 1470 毫秒(没有排序和限制的地理查询),它非常接近没有限制的原始查询,即 2567 毫秒。同样,如果我们将 461 毫秒 ( limited sort ) 添加到 1470 毫秒,它接近原始基准测试结果 1713 毫秒。基于这种相关性,我敢打赌您的基准测试中的 _id 排序根本没有利用复合索引。

无论如何,基准测试速度慢的另一个原因是地理匹配范围非常广。更严格的边界肯定会导致更少的数据排序,即使该排序未被索引。也就是说,我确实认为SERVER-4247会对您有所帮助,因为它可能会在执行地理匹配之前先处理非地理排序。

关于performance - 带排序的 MongoDB 地理空间查询 - 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12908871/

有关performance - 带排序的 MongoDB 地理空间查询 - 性能问题的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  3. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  4. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  5. ruby - Fast-stemmer 安装问题 - 2

    由于fast-stemmer的问题,我很难安装我想要的任何ruby​​gem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=

  6. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  7. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  8. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  9. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  10. 【高数】用拉格朗日中值定理解决极限问题 - 2

    首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

随机推荐