在項目中有一個數據導出的需求,原來的實現方式也比較簡單,根據查詢條件分頁查所有的數據,然后轉成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查詢了。