【问题标题】:Pagination Strategies for Complex (slow) Datasets复杂(慢)数据集的分页策略
【发布时间】:2010-12-14 16:34:00
【问题描述】:

在涉及复杂查询的数据集分页中使用了哪些策略? count(*) 需要大约 1.5 秒,因此我们不想为每个页面视图都点击数据库。目前该查询返回了大约 45k 行。

以下是我考虑过的一些方法:

  • 缓存行数并每 X 分钟更新一次
  • 限制(和偏移)行数为 41(例如)并将页面选择器显示为“1 2 3 4 ...”;然后重新计算是否有人真正进入第 4 页并显示“... 3 4 5 6 7 ...”
  • 获取一次行数并将其存储在用户的会话中
  • 摆脱页面选择器,只需一个“下一页”链接

【问题讨论】:

    标签: php mysql pagination


    【解决方案1】:

    我不得不使用 PHP 和 MySQL 为一个每天浏览量超过一百万的网站设计一些分页策略。我分阶段研究策略:

    多列索引我应该在尝试物化视图之前先完成此操作。

    生成物化视图。我创建了一个 cron 作业,它对我正在使用的文档表进行了常见的非规范化。我会SELECT ... INTO OUTFILE ... 然后创建新表,并将其旋转:

    SELECT ... INTO OUTFILE '/tmp/ondeck.txt' FROM mytable ...;
    CREATE TABLE ondeck_mytable LIKE mytable;
    LOAD DATA INFILE '/tmp/ondeck.txt' INTO TABLE ondeck_mytable...;
    DROP TABLE IF EXISTS dugout_mytable;
    RENAME TABLE atbat_mytable TO dugout_mytable, ondeck_mytable TO atbat_mytable;
    

    这将写竞争mytable 的锁定时间降至最低,并且分页查询可能会在atbat 物化视图上敲打。我已经简化了上述内容,省略了不重要的实际操作。

    Memcache 然后我创建了一个关于我的数据库连接的包装器,以将这些分页结果缓存到 memcache 中。这是一个巨大的性能胜利。但是还是不够好。

    批量生成 我编写了一个 PHP 守护程序并将分页逻辑提取到其中。它将检测mytable 的更改并定期从最旧的更改记录重新生成到最新记录的所有页面到网络服务器的文件系统。使用一点mod_rewrite,我可以检查该页面是否存在于磁盘上,并提供它。这也让我能够通过让 Apache 检测 If-Modified-Since 标头并使用 304 响应代码来有效地利用 反向代理。 (显然,我删除了任何允许用户选择每页结果数量的选项,这是一个不重要的功能。)

    更新: RE count(*) 使用 MyISAM 表时,COUNT 并没有造成问题,因为我能够减少表上的读写争用量。如果我在做 InnoDB,我会创建一个触发器,用行数更新相邻表。根据 INSERT 或 DELETE 语句,该触发器只会 +1 或 -1。

    RE 页面选择器(指轮) 当我转向主动查询缓存时,指轮查询也被缓存,而在批量生成页面时,我使用的是临时表——所以计算指轮没有问题。很多指轮计算简化了,因为它变成了一种可预测的文件系统模式,实际上只需要最大的页码。最小的页码始终为 1。

    Windowed thumbweel 只要你知道你的最大值页数。

    【讨论】:

      【解决方案2】:

      我的建议是在每个查询中要求 MySQL 多出 1 行,并根据结果集中的行数决定是否显示 next page-link。

      【讨论】:

        【解决方案3】:

        MySQL 有一个特定的机制来计算没有LIMIT 子句的结果集的近似计数:FOUND_ROWS()

        【讨论】:

          【解决方案4】:

          MySQL 在优化LIMIT 查询方面相当出色。

          这意味着它会选择适当的连接缓冲区、文件排序缓冲区等来满足LIMIT 子句。

          另请注意,使用 45k 行您可能不需要精确计数。可以使用对索引字段的单独查询来计算近似计数。说,这个查询:

          SELECT  COUNT(*)
          FROM    mytable
          WHERE   col1 = :myvalue
                  AND col2 = :othervalue
          

          可以用这个来近似:

          SELECT  COUNT(*) *
                  (
                  SELECT  COUNT(*)
                  FROM    mytable
                  ) / 1000
          FROM    (
                  SELECT  1
                  FROM    mytable
                  WHERE   col1 = :myvalue
                          AND col2 = :othervalue
                  LIMIT 1000
                  )
          

          ,在MyISAM 中效率更高。

          如果你举一个复杂查询的例子,也许我可以更明确地说明如何改进它的分页。

          【讨论】:

            【解决方案5】:

            我绝不是 MySQL 专家,但也许放弃 COUNT(*) 并继续使用 COUNT(id)?

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-10-29
              • 1970-01-01
              • 2015-12-19
              • 1970-01-01
              • 2012-12-28
              • 2019-11-14
              • 2018-07-18
              • 2016-04-16
              相关资源
              最近更新 更多