如何使用 PHP 和 MySQL 有效地对大型数据集进行分页?

Posted

技术标签:

【中文标题】如何使用 PHP 和 MySQL 有效地对大型数据集进行分页?【英文标题】:How to efficiently paginate large datasets with PHP and MySQL? 【发布时间】:2010-11-24 14:54:10 【问题描述】:

你们有些人可能知道,在 mysql 中使用 LIMIT 关键字并不妨碍它读取前面的记录。

例如:

SELECT * FROM my_table LIMIT 10000, 20;

意味着 MySQL 仍会读取前 10,000 条记录并在生成我们之后的 20 条记录之前将它们丢弃。

因此,在对大型数据集进行分页时,高页码意味着较长的加载时间。

是否有人知道任何现有的分页类/技术/方法可以以更有效的方式对大型数据集进行分页,即不依赖 LIMIT MySQL 关键字?

如果可能的话,使用 php,因为这是我公司的首选武器。

干杯。

【问题讨论】:

如果你的数据集不太可能发生变化,你可以在后台计算每个元素的页面索引,然后分页只成为索引字段上的选择。可能发生的情况是,您偶尔会在页面上出现 n+1 个元素。 ***.com/questions/1243952/… 【参考方案1】:

首先,如果你想分页,你绝对必须有一个 ORDER BY 子句。然后,您只需使用该子句深入挖掘您的数据集。例如,考虑一下:

SELECT * FROM my_table ORDER BY id LIMIT 20

您将拥有前 20 条记录,假设它们的 ID 为:5,8,9,...,55,64。您到第 2 页的分页链接看起来像“list.php?page=2&id=64”,您的查询将是

SELECT * FROM my_table WHERE id > 64 ORDER BY id LIMIT 20

无偏移,仅读取 20 条记录。它不允许你任意跳转到任何页面,但大多数时候人们只是浏览下一页/上一页。 “id”上的索引将提高性能,即使使用较大的 OFFSET 值。

【讨论】:

唯一没有考虑的是删除的行(它假定 id 的编号一致)... 这是个好主意,但是我还需要页码,这意味着我必须要么 a) 永远不要删除任何行,要么 b) 计算并存储每条记录的 id页面。 如果您没有获得足够的行,只需请求更多。继续分批请求,直到你得到足够的,或者你得到 0(这意味着没有更多的行)。确保跟踪实际结束的位置,以便下一组行与显示给用户的内容连续,而不是检索到的内容。 该方法与删除行无关,也不假定 id 是连续的,如示例所示。如果您在 URL 中传递页码,则不会丢失页码。【参考方案2】:

一个解决方案可能是不使用限制子句,而是使用连接——连接一个用作某种序列的表。

有关更多信息,关于 SO,我找到了这个 question / answer,它提供了一个示例——这可能会对你有所帮助;-)

【讨论】:

【参考方案3】:

基本上有 3 种方法,每种方法都有自己的取舍:

    将所有10000条记录发送给客户端,并通过javascript等处理分页客户端。明显的好处是所有记录只需要一个查询;明显的缺点是,如果记录大小以任何方式显着,发送到浏览器的页面大小将是成比例的 - 用户可能实际上并不关心完整的记录集。

    执行您当前正在执行的操作,即 SQL LIMIT 并仅获取每个请求所需的记录,完全无状态。好处是它只发送当前请求的页面的记录,所以请求很小,缺点是a)它需要对每个页面的服务器请求,b)随着记录/页面数量的增加,后面页面的速度会变慢结果,正如你所提到的。在单调递增的 id 字段上使用 JOIN 或 WHERE 子句有时会有所帮助,特别是当您从静态表而不是动态查询请求结果时。

    在服务器上维护某种状态对象,该对象缓存查询结果,并且可以在未来的请求中在有限的时间内引用。好处是它有最好的查询速度,因为实际查询只需要运行一次;缺点是必须管理/存储/清理这些状态对象(尤其是对高流量网站来说很讨厌)。

【讨论】:

【参考方案4】:
SELECT * FROM my_table LIMIT 10000, 20;

表示在搜索中显示从记录#10000开始的20条记录,如果你在where子句中使用主键,我的sql不会很重

任何其他分页方法都会像使用连接方法一样承担巨大的负载

【讨论】:

【参考方案5】:

我不知道您提到的性能下降,并且我不知道任何其他分页解决方案,但是 ORDER BY 子句可能会帮助您减少加载时间。

【讨论】:

【参考方案6】:

最好的方法是在 my_table 中定义索引字段,并为每个新插入的行增加该字段。毕竟你需要使用 WHERE YOUR_INDEX_FIELD BETWEEN 10000 AND 10020 它会快得多。

【讨论】:

这要求您永远不要删除任何行,并且您不会显示聚合。【参考方案7】:

其他一些选项,

按每页对表进行分区,因此忽略限制 将结果存储到会话中(一个好主意是使用 md5 创建该数据的哈希,然后为多个用户使用该会话缓存)

【讨论】:

不太喜欢将这么大的数据集存储到会话中

以上是关于如何使用 PHP 和 MySQL 有效地对大型数据集进行分页?的主要内容,如果未能解决你的问题,请参考以下文章

R:根据一天中的时间有效地对数据框进行子集化

如何使用 PHP 将大型 Excel 文件导入 MySql 数据库

如何优化我的 mysql 请求以使用大型数据库

如何有效地对 SQL 数据库中的记录进行版本控制

如何在 javascript 中最有效地对规范化数据进行非规范化

如何使用 AES_ENCRYPT 和 PDO 准备语句改进大型加密数据库的 PHP 解决方法?