LIMIT/OFFSET 的性能问题 (O(n²) 复杂度)
使用 LIMIT x OFFSET y
方式分页时,数据库需要:
- 扫描并丢弃前 y 条记录
- 再返回接下来的 x 条记录
为什么是 O(n²) 复杂度:
- 随着页码增加,OFFSET 值变大
- 数据库需要读取更多记录才能返回结果
- 例如:获取第50页(每页10条)时,数据库需要读取500条记录,但只返回10条
- 当用户浏览多个页面时,累计读取的记录数呈二次方增长
书签扫描方式 (O(n) 复杂度)
书签扫描方式使用上一页最后一条记录的唯一标识作为"书签",查询时直接从这个位置开始:
-- 假设id是主键,且按id排序
-- 第一页
SELECT * FROM table ORDER BY id LIMIT 10;
-- 后续页面(假设上一页最后一条记录id为123)
SELECT * FROM table WHERE id > 123 ORDER BY id LIMIT 10;
优势:
- 不再需要跳过大量记录
- 每次查询复杂度为 O(1),总体复杂度为 O(n)
- 查询效率不受页码大小影响
- 能有效利用索引
实际例子
假设要获取用户表中的第100页,每页20条记录:
传统方式:
SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 1980;
数据库需要读取1,980+20条记录,但只返回20条。
书签方式:
-- 假设上一页最后一个用户id是2468
SELECT * FROM users WHERE id > 2468 ORDER BY id LIMIT 20;
数据库直接定位到id>2468的位置,只读取需要的20条记录。
注意事项
书签分页要求:
- 必须有唯一且有序的字段作为书签
- 前端需要记录当前页的最后一个书签值
- 适用于单向浏览的场景(向前翻页)
当需求包括随机访问页码时,可能需要结合两种方法或使用其他策略。