知识卡片
大偏移量分页慢的根源:白白扫描并丢弃了前面的行
内容
用LIMIT offset, count实现分页,翻到很靠后的页时会明显变慢,原因不是
“数据变多了”,而是MySQL必须先扫描到offset + count条记录,再把前面
offset条整个丢弃、只保留最后count条——LIMIT 1000,20表面上只要
20条结果,实际上服务器要真实地找出并检查1020条记录。当各页被访问的
概率大致均匀时,这类分页查询平均要扫描半张表,代价随页码线性增长。
针对这个根源,书中给出两种不改变分页语义、只改变”怎么拿”的优化:一是
“延迟关联”——先只用索引覆盖扫描找出这一页需要的主键/排序列(不取其他
列),再用这批少量的键去关联主表取出完整数据,这样真正被扫描丢弃的只
是索引项而不是整行数据,代价小得多;二是把”按偏移量翻页”换成”按已知
位置翻页”(书签分页)——如果排序列是单调的(比如自增主键),记录下
本页最后一条记录的位置值,下一页查询直接用WHERE id < 上次最后位置
ORDER BY id DESC LIMIT N定位,完全避免OFFSET,无论翻到多后面性能都
保持稳定,代价是不能随意跳到任意页码,只能连续向前/向后翻。两种技巧
的共同思路是:分页慢的本质是”OFFSET逼着数据库线性扫过一段不需要的
数据”,只要能用索引或已知位置把这段扫描量绕开,就能从根本上解决,而不
是想办法让扫描本身变快。
参考来源
- 位置:《高性能MySQL:第3版》第6章"查询性能优化"6.7.5节"优化LIMIT
分页"(源文件:_epub-src/OEBPS/Text/part0013.xhtml)
- 结论依据:原文明确"在偏移量非常大的时候,例如可能是LIMIT 1000,20
这样的查询,这时MySQL需要查询10020条记录然后只返回最后20条,前面
10000条记录都将被抛弃……'延迟关联'将大大提升查询效率……如果可以
使用书签记录上次取数据的位置,那么下次就可以直接从该书签记录的位置
开始扫描,这样就可以避免使用OFFSET",直接说明大偏移量分页的代价来源
及延迟关联、书签分页两种规避技巧。
- 原始内容:MySQL需要查询10020条记录然后只返回最后20条,前面10000条
记录都将被抛弃……这里的"延迟关联"将大大提升查询效率,它让MySQL扫描
尽可能少的页面。