数据分页方式

予早 2024-05-27 22:50:23
Categories: Tags:

业务场景:基于 MySQL 数据库,从有约 600 万条记录的表 t_content 中先按照 date 从小到大排序,然后获取 name、date、detail 字段

偏移分页

SELECT id, name, date, detail FROM t_content ORDER BY date LIMIT 5000, 1000;
SELECT id, name, date, detail FROM t_content ORDER BY date LIMIT 5000000, 1000;

深分页会严重影响查询性能

偏移分页无索引:若 date 列无索引,则 SQL 会对约 600 万条数据进行排序,然后丢弃前 5000000 条数据,返回随后的 1000 条数据

偏移分页有有索引:若 date 列有索引,则 SQL 会根据遍历索引丢弃前 5000000 条数据, 返回随后的 1000 条数据

整体上,偏移分页有索引比偏移分页无索引节省排序资源消耗,但仍需要从头遍历,丢弃前面大部分数据,保留后面小部分数据

游标分页

第一次请求记录最大id为cursor_id

SELECT name, date, detail FROM t_content ORDER BY date LIMIT 1000;

第二次以及之后请求时使用cursor作为条件查询,并记录本次查询的最大date为cursor

SELECT name, date, detail FROM t_content WHERE date > cursor ORDER BY date LIMIT 1000;

游标分页无索引:若 date 列无索引,先根据 date > cursor 条件过滤数据,然后对剩余数据进行排序,最后直接返回前 1000 条数据即可。

游标分页有索引:若 date 列有索引,先根据 date > cursor 条件过滤数据,由于排序命中索引,随后可以快速返回前 1000 条数据。

通常会有排序条件,然后排序条件全部或者部分必须构成唯一索引来作为 cursor,因为过滤必须是精确的,无排序条件时可使用自增 id(满足有序和唯一),上述例子中,要实现游标分页效果,必须保证 date 唯一。

游标分页无法直接获取指定页码数据,且必须从前往后获取数据。

# 元组排序
where (product_id, open_date) > (9345345,  “2020-02-02”)
# 等价于
where product_id >= 9345345 and open_date > “2020-02-02”
# 就像 56 > 54 和 5 >= 5 and 6 > 4 一样

分页漏数据问题

数据库排序不稳定导致返回数据泄漏

即使数据不变,仅仅使用 limit 并不能保证每一次同样参数获取到的数据相同,limit 并不保证数据相同

SELECT id, name, date, detail FROM t_content limit 5000, 1000;

所以一定要使用 order by 进行排序,且如果 order by 后面的键也不能唯一确认一行数据的话,也不能保证顺序,故 order by 后面一定跟一个唯一键,例如主键

数据被删除导致漏数据

SELECT * FROM order WHERE ... ORDER BY id ASC LIMIT 3,3;

就需要游标式分页,见上文。

通常情况,列表展示页面使用分页,漏数据用户也感知不到,若是分页后台处理数据,数据就不能重复也不能遗漏,例如广告中的报表API必须做到不重不漏。

数据新增导致漏数据

如果主键不自增,那么无法处理,主键自增就不会出现该情况

其他

Elasticsearch 中的 scroll 也是游标分页。