复杂(慢)数据集的分页策略

时间:2009-11-09 16:02:04

标签: php mysql pagination

有哪些策略用于涉及复杂查询的数据集的分页? count(*)需要大约1.5秒,所以我们不想为每个页面视图命中DB。目前,此查询返回了大约45k行。

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

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

5 个答案:

答案 0 :(得分:4)

我的建议是在每个查询中要求MySQL比你需要的行多一行,并根据结果集中的行数决定是否显示next page - 链接。

答案 1 :(得分:4)

我不得不使用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,并定期从最旧的更改记录重新生成最新记录到Web服务器文件系统的所有页面。有点mod_rewrite,我可以查看该页面是否存在于磁盘上并提供服务。通过让Apache检测If-Modified-Since标头,并使用304响应代码进行响应,这也使我能够有效利用反向代理。 (显然,我删除了允许用户选择每页结果数量的任何选项,这是一项不重要的功能。)

更新: RE count(*)当使用MyISAM表时,COUNT在我能够减少表上的读写争用量时没有产生问题。如果我正在做InnoDB,我会创建一个触发器,用行计数更新相邻的表。该触发器只有+1或-1,具体取决于INSERT或DELETE语句。

RE页面选择器(缩略图)当我转向激进的查询缓存时,还会缓存拇指滚轮查询,当批量生成页面时,我使用的是临时表 - 所以计算指轮是没有问题的。大量的指轮计算被简化,因为它成为一个可预测的文件系统模式,实际上只需要最大的页面数字。最小的页码始终是1.

带窗口的拇指上面给出的窗口指轮(<< 4 [5] 6>>>)的示例应该很容易做到,没有任何疑问,只要你知道你的最大页数。

答案 2 :(得分:2)

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

答案 3 :(得分:1)

MySQL非常适合优化LIMIT次查询。

这意味着它选择适当的连接缓冲区,filesort缓冲区等,足以满足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中效率更高。

如果你举一个复杂查询的例子,我可以说一些关于如何改进其分页的更明确的内容。

答案 4 :(得分:0)

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