乐趣区

关于java:别再用-offset-和-limit-分页了性能太差

不须要放心数据库性能优化问题的日子曾经一去不复返了。

随着时代的提高,随着狼子野心的企业想要变成下一个 Facebook,随着为机器学习预测收集尽可能多数据的想法的呈现,作为开发人员,咱们要一直地打磨咱们的 API,让它们提供牢靠和无效的端点,从而毫不费力地浏览海量数据。

如果你做过后盾开发或数据库架构,你可能是这么分页的:

如果你真的是这么分页,那么我不得不道歉地说,你这样做是错的。

你不以为然?没关系。Slack、Shopify 和 Mixmax 这些公司都在用咱们明天将要探讨的形式进行分页。

我想你很难找出一个不应用 OFFSET 和 LIMIT 进行数据库分页的人。对于简略的小型应用程序和数据量不是很大的场景,这种形式还是可能“应酬”的。

如果你想从头开始构建一个牢靠且高效的零碎,在一开始就要把它做好。

明天咱们将探讨曾经被宽泛应用的分页形式存在的问题,以及如何实现高性能分页。

1.OFFSET 和 LIMIT 有什么问题?

正如后面段落所说的那样,OFFSET 和 LIMIT 对于数据量少的我的项目来说是没有问题的。

然而,当数据库里的数据量超过服务器内存可能存储的能力,并且须要对所有数据进行分页,问题就会呈现。

为了实现分页,每次收到分页申请时,数据库都须要进行低效的全表扫描。

什么是全表扫描?全表扫描 (又称程序扫描) 就是在数据库中进行逐行扫描,程序读取表中的每一行记录,而后查看各个列是否合乎查问条件。这种扫描是已知最慢的,因为须要进行大量的磁盘 I/O,而且从磁盘到内存的传输开销也很大。

这意味着,如果你有 1 亿个用户,OFFSET 是 5 千万,那么它须要获取所有这些记录 (包含那么多基本不须要的数据),将它们放入内存,而后获取 LIMIT 指定的 20 条后果。

也就是说,为了获取一页的数据:

10 万行中的第 5 万行到第 5 万零 20 行

须要先获取 5 万行。这么做是如许低效?

如果你不置信,能够看看这个例子:

https://www.db-fiddle.com/f/3…

右边的 Schema SQL 将插入 10 万行数据,左边有一个性能很差的查问和一个较好的解决方案。只需单击顶部的 Run,就能够比拟它们的执行工夫。第一个查问的运行工夫至多是第二个查问的 30 倍。

数据越多,状况就越糟。看看我对 10 万行数据进行的 PoC。

https://github.com/IvoPereira…

当初你应该晓得这背地都产生了什么:OFFSET 越高,查问工夫就越长。

2. 代替计划

你应该这样做:

这是一种基于指针的分页。

你要在本地保留上一次接管到的主键 (通常是一个 ID) 和 LIMIT,而不是 OFFSET 和 LIMIT,那么每一次的查问可能都与此相似。

为什么?因为通过显式告知数据库最新行,数据库就确切地晓得从哪里开始搜寻(基于无效的索引),而不须要思考指标范畴之外的记录。

比拟这个查问:

和优化的版本:

返回同样的后果,第一个查问应用了 12.80 秒,而第二个仅用了 0.01 秒。

要应用这种基于游标的分页,须要有一个惟一的序列字段 (或多个),比方惟一的整数 ID 或工夫戳,但在某些特定状况下可能无奈满足这个条件。

我的倡议是,不管怎样都要思考每种解决方案的优缺点,以及须要执行哪种查问。

如果须要基于大量数据做查问操作,Rick James 的文章提供了更深刻的领导。

http://mysql.rjweb.org/doc.ph…

如果咱们的表没有主键,比方是具备多对多关系的表,那么就应用传统的 OFFSET/LIMIT 形式,只是这样做存在潜在的慢查问问题。我倡议在须要分页的表中应用主动递增的主键,即便只是为了分页。

起源:toutiao.com/i6860655404431442444

退出移动版