MySQL 数据 - 实现分页的最佳方式?

Posted

技术标签:

【中文标题】MySQL 数据 - 实现分页的最佳方式?【英文标题】:MySQL Data - Best way to implement paging? 【发布时间】:2011-04-17 11:38:12 【问题描述】:

我的 iPhone 应用程序连接到我的 php Web 服务以从 mysql 数据库中检索数据。一个请求可以返回 500 个结果。

实现分页和一次检索 20 个项目的最佳方法是什么?

假设我从我的数据库中收到了前 20 个广告。现在如何请求接下来的 20 个广告?

【问题讨论】:

【参考方案1】:

你也可以

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

select语句的行数(没有限制)在同一个select语句中被捕获,这样就不需要再查询表大小了。 您可以使用 SELECT FOUND_ROWS(); 获得行数;

【讨论】:

这特别低效。 * 导致获取的列多于必要的列,SQL_CALC_FOUND_ROWS 导致从表中的 all 行中读取这些列,即使它们未包含在结果中。在不读取所有这些列的单独查询中计算行数会更有效。然后您的主查询可以在读取 20 行后停止。 你确定吗?我对一个大表 SQL_CALC_FOUND_ROWS 和另一个未使用的查询进行了定时查询。我没有看到时差。任何方式它都比做 2 个查询更快。 1 - select * from atable limit 0 20, 然后 select count(*) from atable。 是的,我确定 - here's more info。在使用索引过滤行的所有情况下,SQL_CALC_FOUND_ROWS 都比执行 2 个单独的查询慢得多。在极少数情况下您不使用索引,或者(如这个简化的示例中)您没有 WHERE 子句并且它是一个 MYISAM 表,它几乎没有区别(它的速度大致相同)。 还有一个discussion about it on ***【参考方案2】:

为查询定义 OFFSET。例如

第 1 页 -(记录 01-10):偏移量 = 0,限制 =10;

第 2 页 -(记录 11-20)偏移 = 10,限制 =10;

并使用以下查询:

SELECT column FROM table LIMIT someLimit OFFSET someOffset;

第 2 页示例:

SELECT column FROM table
LIMIT 10 OFFSET 10;

【讨论】:

你不是说第 2 页的 offset = 10 吗? 我确实限制了 10 个偏移量 0 来获得前 10 个结果,然后限制 10 个偏移量 1 来获得第二个......等等。我喜欢这个,但是你怎么知道页面或偏移量的总量?【参考方案3】:

有关于它的文献:

Optimized Pagination using MySQL,计算总行数和分页的区别。

Efficient Pagination Using MySQL,由 Yahoo Inc. 在 2009 年 Percona 性能会议上发表。Percona MySQL 团队也将其作为 Youtube 视频提供:Efficient Pagination Using MySQL (video),

主要问题发生在大OFFSETs 的使用上。他们避免将OFFSET 与各种技术一起使用,从WHERE 子句中的id 范围选择到某种缓存或预计算页面。

Use the INDEX, Luke 有建议的解决方案:

“Paging Through Results”。

“Pagination done the right way”。

【讨论】:

为复杂查询的每个分页查询获取最大 ID 将导致不实用、非生产使用确实排名、行数和分页之间的子句类型有助于提高性能! 在提供的链接中考虑并正确评估了该策略。根本没那么简单。 提供的链接似乎只满足基本枢轴单枢轴、交叉应用、多 CTE 或派生表机制?我再次支持我的案例,再次重写如此规模的查询以获得 maxid 是架构矫枉过正!然后再次排列和组合 n" 列数的排序顺序! 我是否误解了“分页方式正确”链接,或者它在任何涉及过滤的查询中根本不切实际。 @contactmatt 我同意你的担忧。最后,似乎没有办法有效地实现全部要求,而是围绕原始要求放宽了变化。【参考方案4】:

查询一:SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

查询 2:SELECT * FROM tbl LIMIT 0,500;

查询 1 对中小型记录运行得更快,如果记录数等于或大于 5,000,则结果相似。

500 条记录的结果:

Query1 耗时 9.9999904632568 毫秒

Query2 耗时 19.999980926514 毫秒

8,000 条记录的结果:

Query1 耗时 129.99987602234 毫秒

Query2 耗时 160.00008583069 毫秒

【讨论】:

你需要在id上放一个索引。 id > 0 有什么用处? 就像 Maarten 所说,这两个查询看起来基本相同,并且可能分解为相同的机器级命令。您一定有索引问题或 MySQL 的版本非常旧。 谢谢,因为我没有看到你的答案,我只需要看看 where , order 和 limit 的顺序 使用了错误的示例。使用offset(limit 的第一个参数是偏移量),您仍然选择所有数据到限制,然后丢弃该偏移量,然后返回offsetlimit 之间的部分。另一方面,使用where 子句,您正在为查询设置一种起点,并查询ONLY 该特定部分。【参考方案5】:

本教程展示了一种很好的分页方式。 Efficient Pagination Using MySQL

简而言之,避免使用OFFSET或较大的LIMIT

【讨论】:

能不能给个总结? 是的,我希望能在答案中付出更多努力。 这是幻灯片,不是教程。用处有限。 本质是:不要使用OFFSET,而是使用ORDER BY,并在用于排序的列上放置一个索引。现在我们可以使用WHERE indexedColumn > lastSeenValue ORDER BY indexedColumn DESC LIMIT pageSize 进行过滤/分页。然后,对网络服务器的请求必须包含 lastSeen 值。【参考方案6】:

From the MySQL documentation:

LIMIT 子句可用于限制 SELECT 语句返回的行数。 LIMIT 接受一个或两个数字参数,它们都必须是非负整数常量(使用准备好的语句时除外)。

有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量为0(不是1):

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

要检索从某个偏移量到结果集末尾的所有行,您可以为第二个参数使用一些较大的数字。此语句检索从第 96 行到最后一行的所有行:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

使用一个参数,该值指定从结果集开头返回的行数:

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

换句话说,LIMIT row_count 等价于 LIMIT 0, row_count。

【讨论】:

当使用 LIMIT 进行分页时,您还应该指定 ORDER BY。 @shylent:引用文档没有错,但我同意他应该提到他正在复制文档并提供原始来源的链接。此外,令我惊讶的是,文档中包含使用 LIMIT 而不使用 ORDER BY 的示例……这似乎是一种令人鼓舞的坏做法。如果没有 ORDER BY,则无法保证调用之间的顺序相同。 无论如何,在对大结果集进行分页时(这就是分页的目的 - 将大结果集分成更小的块,对吧?),您应该记住,如果您执行 limit X, Y,本质上是什么发生的情况是检索 X+Y 行,然后从头开始删除 X 行,并返回剩下的任何内容。重申一下:limit X, Y 会扫描 X+Y 行。 我不喜欢你的 LIMIT 95, 18446744073709551615 想法.. 看看OFFSET ;-) 这在处理大数据时效率不高。查看codular.com/implementing-pagination 了解适用于特定场景的多种方式。【参考方案7】:

对于 500 条记录,效率可能不是问题,但如果您有数百万条记录,那么使用 WHERE 子句来选择下一页可能是有利的:

SELECT *
FROM yourtable
WHERE id > 234374
ORDER BY id
LIMIT 20

这里的“234374”是您查看的上一页中最后一条记录的ID。

这将使 id 上的索引能够用于查找第一条记录。如果您使用LIMIT offset, 20,您可能会发现它在您向末尾翻页时变得越来越慢。正如我所说,如果您只有 200 条记录,这可能并不重要,但它可以对更大的结果集产生影响。

这种方法的另一个优点是,如果数据在调用之间发生变化,您不会错过记录或获得重复记录。这是因为添加或删除一行意味着它更改后所有行的偏移量。在您的情况下,这可能并不重要-我猜您的广告池不会经常变化,无论如何,如果他们连续两次收到相同的广告,没人会注意到-但如果您正在寻找“最佳方式”那么这是在选择使用哪种方法时要记住的另一件事。

如果您确实希望使用带偏移量的 LIMIT(如果用户直接导航到第 10000 页而不是逐页翻页,这是必要的),那么您可以阅读这篇关于 late row lookups 的文章以提高 LIMIT 的性能偏移量很大。

【讨论】:

这更像是 :P 虽然我绝对不赞成这种暗示,但“新”的 id 总是比“旧”的大,大多数时候这个确实会如此,所以我认为这“足够好”。无论如何,是的,正如您所展示的那样,正确的分页(在大型结果集上没有严重的性能下降)并不是特别简单,写 limit 1000000, 10 并希望它能够工作不会让您有任何收获。 后期查找链接很有用 如果您只使用“DESC”进行 id 排序,则此分页会向后工作。我喜欢! 但在现实世界中,人们希望多久通过一次 ID 或通过暗示或“创建日期”进行订购? 这仅在您想按唯一属性(如主键)排序时才有效。一旦您通过诸如日期之类的命令进行订购,这将根本不起作用。

以上是关于MySQL 数据 - 实现分页的最佳方式?的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis物理分页的代码实现

在 symfony 4 中分页的最佳方式

Elasticsearch 实现分页的 3 种方式,还有谁不会??

Elasticsearch 实现分页的 3 种方式,还有谁不会??

分页sql

Elasticsearch 实现分页的 3 种方式,还有谁不会??