数据库中结果的分页如何工作?

Posted

技术标签:

【中文标题】数据库中结果的分页如何工作?【英文标题】:How does pagination of results in databases work? 【发布时间】:2018-11-23 14:11:27 【问题描述】:

这是适用于 mysql、Oracle DB 或其他任何可能存在的一般问题。

我知道 MySQL 有 LIMIT offset,size;对于 Oracle,有 'ROW_NUMBER' 或类似的东西。

但是当这样的“分页”查询被背靠背调用时,数据库引擎实际上是否会重新执行整个“选择”,然后每次都检索不同的结果子集?还是它只对结果进行一次整体获取,将结果保存在内存中或其他东西中,然后根据偏移量和大小为后续查询提供结果子集?

如果它每次都进行完整的提取,那么它似乎效率很低。

如果它只完整获取一次,它必须以某种方式将查询“存储”在某个地方,以便下次查询进入时,它知道它已经获取了所有数据,只需要从中提取下一页它。 在那种情况下,数据库引擎将如何处理多个线程?两个线程执行同一个查询?

我很困惑:(

【问题讨论】:

【参考方案1】:

是的,当您使用不同的 OFFSET 运行查询时,该查询将再次执行。

是的,这是低效的。如果您需要对大型结果集进行分页,请不要这样做。

我建议查询一次,使用较大的 LIMIT — 足够 10 或 12 页。然后将结果保存在缓存中。当用户想要浏览多个页面时,您的应用程序可以获取您保存在缓存中的 10-12 个页面并显示用户想要查看的页面。这通常比为每个页面运行 SQL 查询要快得多。

如果您的用户像大多数用户一样只阅读了几页然后更改了他们的查询,则此方法效果很好。


你的评论:

缓存是指 Memcached 或 Redis 之类的东西。高速、内存中的键/值存储。

MySQL 视图不存储任何内容,它们更像是为您运行预定义查询的宏。

Oracle 支持物化视图,因此可能会更好,但查询视图会产生解释 SQL 查询的开销。

更简单的内存缓存应该更快。

【讨论】:

这是有道理的。 “缓存”是指视图或类似的东西?【参考方案2】:

我不同意@Bill Karwin。首先,不要在没有测量的情况下预先假设某事会快还是慢,并提前将代码复杂化以一次下载12页并缓存它们,因为“在我看来它会更快”。 YAGNI principle - 程序员在认为必要之前不应添加功能。 用最简单的方法(一页的普通分页),测量它在生产中的工作情况,如果速度很慢,然后尝试不同的方法,如果速度令人满意,保持原样。


根据我自己的实践——一个从包含大约 80,000 条记录的表中检索数据的应用程序,主表与 4-5 个附加查找表连接,整个查询是分页的,每页大约 25-30 条记录,大约 2500 -3000 页。数据库是Oracle 12c,有几列索引,查询是由Hibernate生成的。 在服务器端对生产系统的测量表明,检索一页的平均时间(中位数 - 50% 百分位数)约为 300 毫秒。 95% 的百分位数小于 800 毫秒——这意味着 95% 的检索单个页面的请求小于 800 毫秒,当我们添加从服务器到用户的传输时间和大约 0.5-1 秒的渲染时间时,总时间不到 2 秒。够了,用户很开心。


还有一些理论 - 请参阅此答案以了解 Pagination pattern 的目的是什么

【讨论】:

我同意在实施解决方法之前应该首先进行测量以确保存在真正的性能问题。我假设性能问题是给定的。 @krokodilko 我的问题实际上不是关于性能,而是数据库是简单地从“一个选择查询”中获取部分结果还是每次都运行“选择”查询 - 即它是否维护一个会话跨多个分页查询调用。在您的 80K 记录示例中,每页 25-30 行 - 数据库是每次都获取所有 80K 行并返回行的子集(基于页面偏移量、大小)还是一次获取 80K 行然后缓存它们?根据比尔,它没有(这是我自己可以做的事情) @krokodilko NoSQL db 像 mongo 或 Elastic search 怎么样?分页和 RDBMS 一样吗?

以上是关于数据库中结果的分页如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何从数据库中获取 Graphql 中的分页游标?

如何使用 ruby​​ on rails 提供来自 google cloud bigquery 的分页结果

mysql的分页使用子查询?

CodeIgniter 中的分页问题

刷新 UITableView 中的分页数据

php中的分页显示