后台导出、爬虫采集、或者用户把列表翻到几千页——只要LIMIT的偏移量上了十万,查询就会肉眼可见地卡。很多人以为是数据太多撑不住,其实是MySQL的工作方式太老实:LIMIT 100000,20的意思是把前100020行都取出来,扔掉前100000行,只返回20行。偏移量越大,白干的活越多。
先复现确认:同样的查询,LIMIT 0,20毫秒级,LIMIT 500000,20十几秒,基本可以确诊深分页问题。
-- 慢的写法:偏移量越大越慢
SELECT id, title FROM articles
ORDER BY id DESC
LIMIT 500000, 20;
-- 实际扫描了50万零20行,扔掉50万行
解法一:游标分页,记住上一页最后一条的id,下一页从它之后取。这是根治方案,性能跟页码无关,翻到第几页都是毫秒级。
-- 游标分页:记住上一页末尾id=98765
SELECT id, title FROM articles
WHERE id < 98765
ORDER BY id DESC
LIMIT 20;
-- 永远只扫20行,翻到十万页也一样快
游标分页的局限是只能“上一页下一页”,跳页就废了。后台管理需要跳页的场景用解法二:延迟关联。先用覆盖索引把目标id找出来,再回表取整行数据,白干的活从“扫全行”降到“扫索引”。
-- 延迟关联:子查询只走索引
SELECT a.id, a.title, a.content
FROM articles a
JOIN (
SELECT id FROM articles
ORDER BY id DESC
LIMIT 500000, 20
) t ON a.id = t.id;
-- 子查询扫的是主键索引,比扫整行便宜一个量级
验证用EXPLAIN:延迟关联版本的执行计划里子查询应该显示Using index(覆盖索引),对比直接查询的rows估算值,差几个数量级就说明优化生效。
解法三最朴素:限制翻页深度。产品层面只允许翻前100页,再往后让用户用筛选或搜索定位。Google也只给你看前几十页,没人真需要第50000页的第3条数据。
我的排序建议:面向用户的前台直接上游标分页,一劳永逸;后台导出用延迟关联加limit分段;实在没条件改代码,就把max偏移量限制住。别让一条深分页SQL占着连接耗几十秒,它一个人就能把连接池拖垮。
数据来源:MySQL官方手册
A5创业网 版权所有