1. 程式人生 > 其它 >count(*)這麼慢,我該怎麼辦?

count(*)這麼慢,我該怎麼辦?

1)計算一個表有多少行數用什麼命令?

  • select count(*) from t

2)count(*)底層是怎樣實現的?

  • 在MYISAM中,是把這個總行數存到磁碟中去的,要的時候直接去讀就行,特別快。

  • 而在InnoDB引擎中,這個總數是需要去一行一行的掃描表,然後累加起來看有多少行。所以當資料量大之後,count(*)就變慢了。

3)為什麼InnoDB引擎不像MYISAM一樣把總數直接存到磁碟中?

  • 總數是不斷變化的,並且InnoDB還有MVCC多版本併發控制,每一行都要判斷對當前這個會話是否可見,每個會話下面讀到的值可能都不一樣。變數比較大,所以不能寫死 。

4)針對InnoDB這種引擎,要想快速讀取count(*),有什麼優化方案嗎?

  • 可以考慮用redis來存總數,插入一行總數+1,刪除一行-1。但是用redis的話 不能保證資料的一致性,有可能有些計數沒有儲存但是redis掛掉了。

  • 針對redis掛掉會造成資料一致性問題我們考慮直接將這個總數放在資料庫某張表中存起來,這樣即使資料庫掛了,重啟之後也是能恢復資料的。

5)MYSQL對count(*)一點優化都沒有嗎 ?

  • 還是有點優化的,我們知道主鍵索引存的是整行記錄,非主鍵索引存的是主鍵值。那非主鍵索引生成的這棵樹是遠遠要小於主鍵索引這棵樹的。我count(*)走主鍵索引查和走非主鍵索引查,得到的結果都是一樣的,那我當然走樹小的非主鍵索引這棵。

6)有一個命令是show table state,他查出來的裡面有個TABLE_ROWS值,那我們可不可以用這個命令來代替count(*)呢?

  • 當然不可以,show table state他算總數的話是估算,誤差是比較大的,一半的誤差左右。

7)MYISAM雖然查總數快,但不支援事務,show table state呢又不精準,count(*)呢又會有效能問題,那現在我有一個頁面要經常顯示總數,那我們該怎麼辦?

  • 自己計數。找一個地方,把總數存起來。就是我們4)中的兩個方案。

8)count(*),count(1),count(欄位),count(主鍵id)有什麼區別?

下面的引擎都指的是InnoDB

  • count(1) :引擎掃描全表,但不取值,直接全部丟給server層去。server 層對於返回的每一行,放一個數字“1”進去,判斷是不可能為空的,加起來的結果就是總數。

  • count(主鍵 id):引擎掃描全表 ,把id值取出來,返回給server層。到了server層之後,server層把id不為空的加起來就是結果。

  • count(欄位):引擎掃描,一行一行取出來,然後拿給server層。

    • 如果這個欄位定位為not null:那server層直接數有多少行

    • 如果這個欄位可以為null:server層看返回的這些行,欄位不為空的那就累加起來返回。

  • count(*):MYSQL有優化,確保行中肯定不為null,所以直接數有多少行,這個速度還是挺快的,所以我們首選還是得這個。