问题描述

分页是数据库常见操作,也是很简单的操作:

select * from Goods limit 100, 10;

如上,是查询第 100~110 条数据。

而数据量非常大时,这种查询语句就……

mysql> select * from Goods limit 230000, 3;
+--------+-------+--------------+---------------------+
| id     | title | subtitle     | createdAt           |
+--------+-------+--------------+---------------------+
| 230001 | abc   | abccccabcccc | 2020-07-10 03:37:59 |
| 230002 | abc   | abccccabcccc | 2020-07-10 03:37:59 |
| 230003 | abc   | abccccabcccc | 2020-07-10 03:37:59 |
+--------+-------+--------------+---------------------+
3 rows in set (0.08 sec)

查询时间变长了。而且数据越多,时间越长。

解决方案

sql 语句中,limit 后面的两个数,一个是“起始位置”,一个是“条数”。
起始位置 ≈≈ 起始id(比如,第 10000 条数据第 id 差不多就是 10000)。
所以:

mysql> select * from Goods where id > 230000 limit  3;
+--------+-------+--------------+---------------------+
| id     | title | subtitle     | createdAt           |
+--------+-------+--------------+---------------------+
| 230001 | abc   | abccccabcccc | 2020-07-10 04:02:15 |
| 230002 | abc   | abccccabcccc | 2020-07-10 04:02:15 |
| 230003 | abc   | abccccabcccc | 2020-07-10 04:02:15 |
+--------+-------+--------------+---------------------+
3 rows in set (0.02 sec)

而且,随着数据的增多,这种方案所需的查询时间几乎不变

但是,使用“约等于”、“差不多”这样的词汇,是很不严谨的、很不负责任的行为。
只要删除过数据,起始位置就不可能等于起始 id。

但是,让我们分析一下分页的逻辑流程,现在一般是这样的:

  • 前端传来页数(pageIndex)、每页条数(pageSize)
  • 后端计算出起始位置(pageStart = pageIndex * pageSize)
  • 使用 sql 查(limit pageStart, pageSize)

让我们再倒退一步,用户最开始看到的,一般是第一页吧,那么:

select * from Goods limit pageSize;

查询第二页时,就可以使用当前页最后一条数据的 id

  • 把“最后一条数据的 id(记作 lastId)”、pageSize 传给后端
  • select * from Goods where id > lastId limit pageSize;

而“上一页”就可以使用当前页的第一条数据的 id

这很符合现在的“滚动加载”、“下拉加载”。国外友人把这种方案叫做:cursor based pagination

但是,有些情况下,我们需要“跳页”。比如看着第 100 页数据时,突然想看第 188 页的数据。
于是上面的解决方案就不再可用。但是稍微改一下:

  • 跟之前一样,把“最后一条数据的 id(记作 lastId)”、pageSize 传给后端
  • select * from Goods where id > lastId limit (88*pageSize), pageSize;

这是传统方案与 cursor based pagination 方案的结合。

最后

这篇记录,并没有总结出一个具体的方案。因为具体方案涉及的问题有很多:

  • 使用的哪款数据库(对应不同的 sql 规范)
  • 使用的哪款编程语言
  • 业务的具体要求(很多场景下就不需要跳页)

给出所有场景下的具体方案只是一个时间问题。但是不给出所有的具体方案却不是因为时间,因为为了让你的屏幕在这个网页上多停留一些时间,这些时间是值得付出的。
但是,那却是无意义的,或者说几乎无意义的。本篇的目的在于讲明一个问题,并且给这个问题提供一种解决思路
所谓二八定律,这里就是那个“二”。
太具体的解决方案往往会让人迷失于各种细节。

参考

共勉,相信你我的未来,是可期的。