【问题标题】:Performance issues with mongo + PHP with pagination, distinct valuesmongo + PHP 的性能问题,分页,不同的值
【发布时间】:2011-11-06 10:30:28
【问题描述】:

我有一个 mongodb 集合,其中包含许多领域的书籍。与我的问题相关的一些关键领域是:

{
book_id : 1, 
book_title :"Hackers & Painters", 
category_id : "12",
related_topics : [ {topic_id : "8", topic_name : "Computers"},
                   {topic_id : "11", topic_name : "IT"}
                 ]
...
... (at least 20 fields more)
...
}

我们的搜索页面上有一个用于过滤结果的表单(包含许多输入/选择框)。当然还有分页。通过过滤的结果,我们在页面上显示所有类别。对于每个类别,在该类别中找到的结果数量也会显示在页面上。

我们尝试使用 MongoDB 而不是 PostgreSQL。因为性能和速度是我们这个过程的主要关注点。

现在的问题是:

我可以通过向“查找”函数提供所有过滤器参数来轻松过滤结果。这很酷。我可以使用跳过和限制功能对结果进行分页:

$data = $lib_collection->find($filter_params, array())->skip(20)->limit(20);

但我必须在分页发生之前收集为每个 category_id 和 topic_id 找到的结果数。而且我不想用 PHP “foreach”所有结果、收集类别和管理分页,因为过滤后的数据通常包含近 200.000 个结果。

问题 1: 我在 PHP 手册中找到了带有“不同”示例的 mongodb::command() 函数。我认为我通过这种方法获得了不同的价值。但是命令函数不接受条件参数(用于过滤)。我不知道如何在要求不同的值时应用相同的过滤器参数。

问题 2: 即使有一种方法可以使用 mongodb::command 函数发送过滤器参数,该函数也将是过程中的另一个查询,并且与我认为以前的查询。这将是另一个速度惩罚。

问题3:为了得到不同的topic_ids,结果数量将是另一个查询,另一个速度惩罚:(

我是 MongoDB 的新手。也许我从错误的角度看待问题。您能帮我解决问题并就最快的获取方式发表您的意见吗:

  • 过滤结果
  • 分页
  • 找到的结果数量不同的值

来自大型数据集。

【问题讨论】:

  • 数据集多久更改一次?

标签: php performance mongodb filtering distinct


【解决方案1】:

所以过滤结果和分页的简单方法如下:

$cursor = $lib_collection->find($filter_params, array())
$count = $cursor->count();
$data = $cursor->skip(20)->limit(20);

但是,这种方法可能效率不高。如果您查询未编入索引的字段,服务器“count()”的唯一方法是加载每个文档并检查。如果您执行skip()limit() 而没有sort(),那么服务器只需要找到前20 个匹配的文档,工作量要少得多。

每个类别的结果数量将变得更加困难。

如果数据不经常更改,您可能需要使用常规 map/reduce 作业预先计算这些值。否则,您必须运行一系列 distinct() 命令或内联 map/reduce。两者通常都不适用于临时查询。

唯一的其他选择基本上是加载所有搜索结果,然后依靠网络服务器(而不是数据库)。显然,这也是低效的。

获得所有这些功能需要一些规划和权衡。

【讨论】:

  • 感谢您的回复。我写了一个 map/reduce 函数来收集 category_id 和相关的结果数量。但查询需要 1.5 秒。而 $lib->collection->find() 只需要 0.1 秒。将所有结果加载到 PHP 并在其中循环需要 0.4 秒。所以用 PHP 循环似乎更有效。但我用查询测试了那些返回 15.000 的结果。但结果可能比这更大。我尝试将响应时间保持在 0.3 秒以下。也许不可能..
  • MongoDB 的“权衡”之一是它不擅长实时聚合查询。 SQL 针对“基于集合”的查询进行了优化,例如聚合,其中 MongoDB 针对键值查找进行了优化。两种风格的 DB 都可以做这两件事,但性能不同。请注意,如果您在索引的内容上使用$cursor->count(),您的响应会更快(在所有数据库中都是如此),您可能需要尝试此优化。
【解决方案2】:

分页

对大型数据集进行分页时要小心。请记住,skip()take()——无论您是否使用索引——都必须执行扫描。因此,跳得很远很慢。

这样想:数据库有一个可以相互比较值的索引(B-Tree):它可以快速告诉你某个东西是大于还是小于给定的x。因此,平衡树中的搜索时间是对数的。对于基于计数的索引不是正确:B 树无法快速告诉您第 15.000 个元素是什么:它必须遍历并枚举整个树。

来自documentation

寻呼成本

不幸的是,跳过可能(非常)昂贵,并且需要 服务器从集合或索引的开头走,以获取 在它可以开始返回页面之前到偏移/跳过位置 数据(限制)。随着页码的增加,跳过将变得更慢,并且 更多的 cpu 密集型,并且可能 IO 绑定,更大的集合。

基于范围的分页可以更好地使用索引,但不允许 您可以轻松跳转到特定页面。

确保您确实需要此功能:通常,没有人关心第 42436 个结果。请注意,大多数大型网站都不会让您分页很远,更不用说显示确切的总数了。关于这个主题有一个很棒的网站,但我手头没有地址,也没有找到它的名称。

不同的主题计数

我相信您可能会使用大锤作为漂浮装置。查看您的数据:related_topics。由于对象关系映射,我个人讨厌 RDBMS,但this 似乎是关系数据库的完美用例。

如果您的文档非常大,性能是一个问题,并且您和我一样讨厌 ORM,您可能需要考虑使用 MongoDB 和您选择的 RDBMS:让 MongoDB 获取结果和 RDBMS 聚合给定类别的最佳匹配。您甚至可以并行运行查询!当然,写入数据库的更改需要在两个数据库上进行。

【讨论】:

    猜你喜欢
    • 2016-09-28
    • 2013-10-07
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多