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

18

这是一个普遍适用于MySQL、Oracle DB或其他可能存在的数据库的问题。

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

但是,当这种“分页”查询连续调用时,数据库引擎是否实际上每次都执行整个“选择”,然后每次检索不同的结果子集?还是它仅执行一次总体的结果获取,将结果保存在内存中或其他地方,然后根据偏移和大小为后续查询提供来自中的结果子集呢?

如果它每次都执行完整的获取操作,那么它似乎非常低效。

如果它只执行一次完整的获取操作,那么它必须以某种方式“存储”查询,以便下次查询到达时,它知道已经获取了所有数据,只需从中提取下一页即可。在这种情况下,数据库引擎如何处理多个线程?两个执行相同查询的线程怎么办?

我很困惑 :(

2个回答

12

是的,当您使用不同的OFFSET运行查询时,查询会重新执行。

是的,这是低效的。如果您需要遍历大量结果集,请不要这样做。

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

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


关于您的评论:

通过缓存,我指的是类似Memcached或Redis的东西。高速的内存键/值存储。

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

Oracle支持材料化视图,因此可能更好,但查询视图会具有解释SQL查询的开销。

一个更简单的内存缓存应该会更快。


这很有道理。你所说的“cache”是指视图或类似的东西吗? - shikhanshu
如果数据非常动态,那该怎么办? - raj240
在这种情况下,动态是什么意思?经常更新? - Bill Karwin

12

我不同意@Bill Karwin的观点。首先,在没有测量的情况下,不要提前做出快或慢的假设,并提前复杂化代码以一次下载12页并缓存它们,因为“我觉得这样会更快”。

YAGNI原则 - 程序员应该在必要时才添加功能。
用最简单的方式完成它(普通的单页分页),在生产环境中测量它的运行情况,如果速度慢,则尝试另一种方法;如果速度令人满意,则保留原样。


从我的实践经验来看,一个应用程序从包含约80,000条记录的表中检索数据,主表与4-5个附加查找表连接,整个查询分页,每页约25-30条记录,总共约2500-3000页。数据库是Oracle 12c,在几列上有索引,查询由Hibernate生成。在服务器端的生产系统上进行的测量显示,检索一个页面的平均时间(中位数-50%百分位数)约为300毫秒。第95个百分位数小于800毫秒-这意味着检索单个页面的95%请求小于800毫秒,当我们添加从服务器到用户的传输时间和约0.5-1秒的呈现时间时,总时间少于2秒。这就足够了,用户很满意。

还有一些理论 - 查看此答案以了解Pagination pattern的目的。


2
我同意在实施解决方案之前先进行测量,以确保存在真正的性能问题。我一开始就假设性能问题是确定存在的。 - Bill Karwin
@krokodilko 我的问题实际上不是关于性能,而是数据库是否仅从“一个选择查询”中获取结果的部分,还是每次运行“选择”查询 - 即它是否在多个分页查询调用之间维护会话。在您的80K记录,每页25-30行的示例中,数据库是否每次都获取所有80K行并返回一组行(基于页面偏移量,大小),还是获取80K行一次并将其缓存?根据Bill的说法,它不会这样做(这是我自己可以做的事情)。 - shikhanshu
@krokodilko 你对像Mongo或Elasticsearch这样的NoSQL数据库有什么看法?分页操作是否与关系型数据库相同? - DirtyMind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接