不须要放心数据库性能优化问题的日子曾经一去不复返了。
随着时代的提高,随着狼子野心的企业想要变成下一个 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