天天看點

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

為什麼 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 先啟動一個單獨的語句,插入一行記錄後,查詢表的總行數。

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

出現不一樣的結果跟 InnoDB 存儲引擎有關系,在預設隔離級别可重複讀的情況下,通過多版本并發控制(MVCC)來實作,每一行記錄都需要判斷自己是否對這個會話可見,是以在統計總數量時,InnoDB 隻好把資料一行一行的讀取出來判斷,隻有目前會話可見的才納入統計中。是以同一時刻不同會話查詢到的數量就不一樣。

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

最後

目前網際網路上很多大佬都有MySQL相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支援。若文中有所錯誤之處,還望提出,謝謝。

原文位址

https://www.cnblogs.com/jamaler/p/12579416.html