在项目中有一个数据导出的需求,原来的实现方式也比较简单,根据查询条件分页查所有的数据,然后转成csv的格式一行一行写进文件存储中。
实际上线之后,发现出现了慢查询,具体的sql如下:
select * from table_name
WHERE create_time > "2025-04-11 00:00:00" and create_time < "2025-04-12 23:59:59.999"
order by create_time limit offset, size;
第一次出现的时候也没多想,发现create_time没加索引,就给create_time加了索引,以为从此万事大吉。
没想到,今天又出现了慢查询。纳尼,这还能忍?
于是乎,常规操作,执行了一下explain,竟然意外的发现,当请求的页码深度超过一定程度以后,create_time索引就被放弃了,改为全表扫描了。
因此二级索引在使用的时候,一般是需要再次进行回表进行查询的,所以当分页深度超过一定程度,优化器会认为成本太高直接改为全表扫描。
关于MySQL可能造成索引失效的一些情况,可以参考下面这篇文章。
https://juejin.cn/post/7300460850011734070?spm=a2c6h.12873639.article-detail.4.45c5438eRjVDEU
在知道了原因之后,头脑一热,心想这还不简单吗,force index了解一下?
使用force index虽然可以解决索引失效的问题,但是因为页码深度的问题造成的回本成本过高的问题也是实际存在的,那么有没有更好的解决方案呢?
还真有,这里之所以使用传统limit offset,size方式进行分页查询,实际上是掉进了一个思维陷阱里,因为这是使用最多的分页查询方式。但是考虑到此处的场景并不需要真正的分页,只需要能达到分批获取数据的逻辑就可以了。
所以最终的解决方案是使用id > xxx limit 100这样的方式来实现。
select * from table_name
WHERE create_time > "2025-04-11 00:00:00" and create_time < "2025-04-12 23:59:59.999" and id > xxx limit 100;
该写完的sql,再来执行一下explain,可以看到现在已经改为根据主键的range查询了。