MySQL分頁查詢優化
1、現象
將表中資料全部查出來快取到redis中,表中資料量約百萬級,表的id是自增的。
使用select * from table order by id limit m,n 分頁查詢出資料快取到redis,當翻到比較靠後的頁時,查詢效率會越來越慢
2、原因
這種方式分頁是從磁碟拿整一個數據頁到儲存引擎緩衝區,比如 limit 100000, 10 那就要按順序先取出前100000條資料,跳過了offset後再拿出第[100000,100010]這10條資料,如果這條資料欄位很多且體積比較大,那麼資料頁就會比較大,我們知道儲存引擎記憶體頁大小是有限制的,如果資料頁較大,那可能一次記憶體頁只能存一兩條資料,那麼每次磁碟IO只能取到一兩條,所以要翻掉前100000條,那就可能需要非常多次的IO,這就是為什麼慢的原因。
3、優化思路
思路一:針對“按順序取前100000條”這個問題 , 使用id限定優化
通過id限定可以不用取完前面100000條就能拿到第[100000,100010]條,比如改造limit 100000,10這個sql,用where id > 100000 limit 10,這樣的話可以按 id 主鍵索引先定位到哪個磁碟資料頁,然後按順序取10條資料就好了
例: select * from table where id > 100000 limit 10;
思路二:針對“取大資料頁到記憶體進行過濾”的問題
把資料頁做小,使得一個記憶體頁能容納更多條資料,從而減少磁碟IO次數,又或者直接通過索引頁來過濾,這樣就不需要用原資料頁來進行記憶體計算。就是通過select id from table limit 100000,10先分頁查出id,再回表查詢將這10個id的資料取出來就好了,因為id是主鍵索引,所以拿id來記憶體計算,就比拿一整頁資料計算,IO次數要少的多了(甚至,儲存引擎可能把id索引頁都快取到cache中了的話,壓根都不需要硬碟IO了)。此外,order by的欄位儘量要是索引欄位,比如order by id,所以建表的時候考慮到要分頁查詢的話,儘量保證id的自增序就是分頁的順序/逆序,這樣分頁排序就能直接order by id了。
例: select * from ctr_manage JOIN (select id from ctr_manage order by id limit 100000, 10) b using (id) ;
4、按思路一優化後方案
將表中資料全部查出來快取到redis中,表中資料量約百萬級,表的id是自增的
步驟1:查出最小的id
步驟2:每次查詢出5000條(條數可以自定義,在一個合理的範圍就行)資料,查詢語句
SELECT * FROM TABLE WHERE ID > (最小id) order by id limit 5000
如果查出來的集合不為空,將查出來的資料快取到redis中,並將查出來的資料的最後一條記錄的id記為最小id
如果查出來的集合為空,說明遍歷處理完成,結束