MySql分頁limit優化

問題

mysql使用limit分頁,當limit offset,rows的offset數值過大時,會出現效率問題。

準備數據

版本
count
索引
正常查詢:

select * from t_bank where bank_code like '0%' limit 145000,5;

zhengchang
查詢三次,時間分別是0.684s,0.901s,0.708s.
explain
執行計劃,走的range索引。

優化方法

select * from t_bank JOIN (select id from t_bank where bank_code like '0%' limit 145000,5) a on a.id= t_bank.id;

explain
查詢三次,耗時分別爲0.151s,0.401s,0.211s。

分析

爲什麼這樣寫可以提高效率?
select * from t_bank where bank_code like ‘0%’ limit 145000,5; 的查詢過程:

  1. 找到非聚簇索引葉子節點上的主鍵值;
  2. 根據主鍵值去聚簇索引上查詢需要的全部字段值。
  • 葉子節點:指InnoDB索引(數據結構:B+Tree,由二叉查找樹,平衡二叉樹,B樹演化而來)的一個存儲單元。
  • 聚簇索引:又稱聚集索引,InnoDB的表一定有一個聚簇索引,有主鍵就是主鍵,沒有主鍵就會選擇一個唯一非空索引,沒有唯一非空索引就會隱式創建一個聚簇索引。
  • 聚集索引(主鍵索引)的葉子節點存的是索引key(主鍵)和數據行,非聚集索引的葉子節點存的是索引key和主鍵鍵值。

由此可以看出,非聚簇索引的查詢有兩個過程。而limit查詢,偏移的所有數據,都會走着兩個過程。如果只查id,就可以省略第2個步驟。

證實

爲了證實select * from t_bank where bank_code like ‘0%’ limit 145000,5是掃描了145005個索引節點和145005個聚簇索引節點,只能用間接的方法來證明。
InnoDB有個buffer pool,裏面存有最近訪問的數據頁,包括數據頁和索引頁。我們需要運行兩個SQL,比較buffer pool裏數據頁的數量即可得到結果。

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;

遇到的問題

爲了在每次重啓時確保清空buffer pool,我們需要關閉innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,這兩個選項能夠控制數據庫關閉時dump出buffer pool中的數據和在數據庫開啓時載入在磁盤上備份buffer pool的數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章