业务场景
线上订单列表分页查询,老写法:
sql
select * from order
where user_id=? and create_time between ? and ?
order by create_time desc
limit 10000,10
问题
• limit 偏移量太大,数据库要扫描前10000行再丢弃,极慢
• 只有单列索引,排序触发 Using filesort
• 接口响应3~5秒,频繁超时
优化方案
-
建联合索引
idx_user_createtime (user_id, create_time, id)
-
改成游标分页(正确逻辑)
第一页正常:
sql
select id,xxx from order
where user_id=?
order by create_time desc,id desc
limit 10
下一页带上一页最小时间+最小id,用 id < last_id
(因为是倒序:时间从新到旧、id从大到小)
sql
select id,xxx from order
where user_id=?
and create_time < last_create_time
and id < last_id
order by create_time desc,id desc
limit 10
核心原理
倒序分页:desc 用 id < 上页最后一条id
正序分页:asc 用 id > 上页最后一条id
- 去掉 select *,只查必要字段
优化效果
SQL从3秒多降到10ms内,接口超时全部消失。
我给你一句面试口述原话,直接背:
当时线上订单列表用limit大偏移分页,越往后越慢,还触发了文件排序。我先建了user_id+create_time联合索引,然后把大偏移limit改成游标分页,倒序查询用id小于上一页最后一条id做翻页,避免数据库扫描大量无效数据,同时精简查询字段,优化后耗时从3秒多降到十几毫秒。