爲啥select count(_) from t,在InnoDB引擎中比MyISAM 慢

感謝參考原文-http://bjbsair.com/2020-04-01/tech-info/18472.html

爲什麼select count(*) from t,在InnoDB引擎中比MyISAM 慢?

統計一張表的總數量,是我們開發中常有的業務需求,通常情況下,我們都是使用 select count(*) from t SQL 語句來完成。隨着業務數據的增加,你會發現這條語句執行的速度越來越慢,爲什麼它會變慢呢?

爲什麼會變慢?想要得到答案就需要知道 MySQL 是如何統計總數量的,先說一個前提吧,count() 的具體實現是由存儲引擎實現的,也就是說不同的存儲引擎實現的方式不一樣。標題:爲什麼select count( ) from t,在 InnoDB 引擎中比 MyISAM 慢?也是高頻面試題。

InnoDB和MyISAM 是我們常用的 MySQL 存儲引擎,所以主要對比一下 count(*) 在 InnoDB 和 MyISAM 中的實現:

  • *在 MyISAM 存儲引擎中,把表的總行數存儲在磁盤上,當執行 select count() from t 時,直接返回總數據**。
  • *在 InnoDB 存儲引擎中,跟 MyISAM 不一樣,沒有將總行數存儲在磁盤上,當執行 select count() from t 時,會先把數據讀出來,一行一行的累加,最後返回總數量**。

知道了 InnoDB 和 MyISAM 引擎 count() 實現之後,爲什麼select count() from t,在 InnoDB 引擎中比 MyISAM 慢?應該有答案了吧,但是這個結論需要有一個前提,就是統計 SQL 不帶過濾條件。如果 統計數量 SQL 語句爲:select count(*) from t where x = 23,那麼在 MyISAM 中就不一定比 InnoDB 快了。

*InnoDB 中 count() 語句是在執行的時候,全表掃描統計總數量,所以當數據越來越大時,語句就越來越耗時了**,爲什麼 InnoDB 引擎不像 MyISAM 引擎一樣,將總行數存儲到磁盤上?這跟 InnoDB 的事務特性有關,由於多版本併發控制(MVCC)的原因,InnoDB 表“應該返回多少行”也是不確定的。

不妨用一個例子來說明一下,假設現在 t 表中有 10000 條數據,現在有三個用戶同時訪問的會話:

  • 會話 A 先啓動事務並查詢一次表的總行數。
  • 會話 B 啓動事務,插入一行後記錄後,查詢表的總行數。
  • 會話 C 先啓動一個單獨的語句,插入一行記錄後,查詢表的總行數。

爲什麼select count(*) from t,在InnoDB引擎中比MyISAM 慢?

假設從上到下是按照時間順序執行的,同一行語句是在同一時刻執行的。可以看出在最後時刻,三個會話返回的總行數不一樣。

出現不一樣的結果跟 InnoDB 存儲引擎有關係,在默認隔離級別可重複讀的情況下,通過多版本併發控制(MVCC)來實現,每一行記錄都需要判斷自己是否對這個會話可見,因此在統計總數量時,InnoDB 只好把數據一行一行的讀取出來判斷,只有當前會話可見的才納入統計中。所以同一時刻不同會話查詢到的數量就不一樣。

InnoDB 引擎在 count()語句上也做了優化,我們知道,在 InnoDB 存儲引擎中是以索引組織表的方式存儲數據,主鍵索引樹上葉子節點存放在所有的數據,而普通索引樹的葉子節點是主鍵值,所以普通索引樹會比主鍵索引樹小很多,但是數量是一樣的,也就是說遍歷主鍵索引樹和普通索引樹得到的結果都是一樣的。MySQL 就利用了這一特性,在 InnoDB 中執行 select count() from t 語句時,MySQL 優化器會找到最小的那棵索引樹來遍歷,這樣可能就可以減少加載次數,在一定程度上提升了 count(*)的執行效率。

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