知识卡片

大偏移量分页慢的根源:白白扫描并丢弃了前面的行

普通读书笔记卡

内容

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扫描 尽可能少的页面。