求求你不要再用offset和limit了

简介: 求求你不要再用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/3JSpBxVgcqL3W2AzfRNCyq/1?ref=hackernoon.com


在左侧面板中,您有一个基本架构,该架构将为我们的测试插入100.000行,而在右侧,则是有问题的查询和我们的解决方案。只需单击顶部的“运行”,然后比较每个执行时间。第一个查询:1秒;(问题查询)至少需要30秒钟的时间才能运行。


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


https://github.com/IvoPereira/Efficient-Pagination-SQL-PoC?ref=hackernoon.com


现在你应该知道这背后都发生了什么:OFFSET 越高,查询时间就越长。

2. 有什么解决方案

这是你应该使用的:

这是基于游标的分页  

你应该存储最后收到的主键(通常是一个ID)和LIMIT,而不是在本地存储当前的OFFSET和LIMIT并随每个请求传递它,因此查询最终可能与此类似。

为什么?因为通过显式传递最新的读取行,你可以根据有效的索引键告诉数据库确切从哪里开始搜索,而不必考虑该范围之外的任何行。

以下面的比较为例:

针对我们的优化版本:

接收到的记录完全相同,但是第一个查询花费了12.80秒,第二个查询花费了0.01秒。你能体会到差异吗?


要使用这种基于游标的分页,需要有一个惟一的序列字段 (或多个),比如惟一的整数 ID 或时间戳,但是在某些特定情况下,这可能不符合我们的需求。


我的建议是始终考虑每种表体系结构的优缺点以及在每种表体系结构中需要执行哪种查询。


如果您需要在查询中处理大量相关数据,Rick James 的文章提供了更深入的指导。

http://mysql.rjweb.org/doc.php/lists

结论

这样做的主要要点是始终检查查询的性能(无论是1k行还是1M行)。可伸缩性至关重要,如果从一开始就正确实施,肯定会避免将来出现许多麻烦。

再优化

类似于查询 SELECT * FROM table_name WHERE id > 8000000 LIMIT 10; ,这样的效率非常快,因为主键上是有索引的,但是这样有个缺点,就是ID必须是连续的,并且查询不能有where语句,因为where语句会造成过滤数据.

SELECT * FROM table_name WHERE id > 8000000 LIMIT 10;

覆盖索引优化

mysql 的查询完全命中索引的时候,称为覆盖索引,是非常快的,因为查询只需要在索引上进行查找,之后可以直接返回,而不用再回数据表拿数据。因此我们可以先查出索引的 ID,然后根据 Id 拿数据。

select * from (select id from table_name limit 1000000,100) a left join table_name b on a.id = b.id;

参考来源:https://hackernoon.com/please-dont-use-offset-and-limit-for-your-pagination-8ux3u4y

pub哥 2020 年终知识清单:

目录
相关文章
|
4月前
|
安全 Serverless API
函数计算操作报错合集之进不去页面,报错乱码 ResourceThrottled, Reserve resource exceeded limit 一般是由于什么导致的
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
|
5月前
|
SQL Java
一直傻傻分不清 count(*) count(id) count(1) 这次终于整明白了
一直傻傻分不清 count(*) count(id) count(1) 这次终于整明白了
60 0
|
C语言
偏移量(Offset)的概念
偏移量(Offset)的概念
1126 0
|
缓存 NoSQL 数据库
原来布隆,他还会.......
原来布隆,他还会.......
56 0
|
SQL Oracle 关系型数据库
【SQL开发实战技巧】系列(十五):查找最值所在行数据信息及快速计算总和百之max/min() keep() over()、fisrt_value、last_value、ratio_to_report
本篇文章讲解的主要内容是:***计算部门中那个工资等级的员工最多、通过返回部门10最大工资所在行的员工名称小案例来讲解max/min() keep() over()、通过查询工资最高的人小案例来介绍fisrt_value、last_value、通过计算各个部门的工资合计以及各个部门合计工资占总工资的比例小案例来介绍如何计算百分比及ratio_to_report分析函数的使用***
【SQL开发实战技巧】系列(十五):查找最值所在行数据信息及快速计算总和百之max/min() keep() over()、fisrt_value、last_value、ratio_to_report
1-50可复制到word文档外带圈的数生成方法
1-50可复制到word文档外带圈的数生成方法
206 0
|
前端开发 Java C#
源码阅读07-p-limit
并发场景在所有的开发过程中都是会遇到的。像面向对象语言 C#、Java 经常都是使用多线程的技术来解决并发的场景。但是 Node 是单线程的,对于解决并发基本是通过异步方式。还有使用 node 提供的 Cluster 模块来完成多进程的。 注意点:Node 中的异步是为了解决不堵塞主线程,而不是为了解决并发问题。
148 0
|
机器学习/深度学习 存储 SQL
别再用 offset 和 limit 分页了,性能太差
别再用 offset 和 limit 分页了,性能太差
1496 0
别再用 offset 和 limit 分页了,性能太差
|
SQL 关系型数据库 MySQL
十一、操作delete或者update语句,加个limit或者循环分批次删除
十一、操作delete或者update语句,加个limit或者循环分批次删除
301 0
【CCCC】L2-021 点赞狂魔 (25分),map数组,间接排序
【CCCC】L2-021 点赞狂魔 (25分),map数组,间接排序
127 0