前言
本文介紹 MyISAM 和 InnoDB 如何執行 count 操作,如果是一個需要使用 count 進行大量計數的場景,應該如何設計實作,以及不同 count 操作的效率,
MyISAM和InnoDB的count
MyISAM
MyISAM 存盤引擎的每個表記錄了總行數,在沒有 where 條件時,直接獲取該記錄值回傳,
InnoDB
InnoDB 獲取 count 值,只能通過掃描索引樹來計數,
為什么 InnoDB 只能臨時掃描來計數,而不能像 MyISAM 一樣存盤一個總行數值?
對于同一時刻的多個查詢請求,因為并發版本控制的原因,InnoDB 表應該回傳多少行是不確定的,需要掃描索引,判斷每行記錄的可見性,
此外,InnoDB 也做了一些優化,主鍵索引樹存盤了行記錄,而普通索引樹只存盤主鍵值,所以普通索引樹比主鍵索引樹小很多,因此,MySQL 會優先選擇最小的索引樹來遍歷,在保證邏輯正確的情況下,盡量減少掃描的資料量,是資料庫系統設計的通用法則之一,
count值如何記錄
1、快取記錄
比如在 Redis 中用 string 型別記錄一個計數,當新增或者洗掉記錄時,相應修改 Redis 的值,
這樣是不行的,沒法保證資料的一致性,
首先,如果業務系統插入或洗掉一行資料后,系統宕機,Redis 沒有寫入,重啟系統后 Redis 會與資料庫不一致,不過,這個問題可以通過系統重啟時從資料庫查詢一次解決,
而且,如果需要同時從 Redis 和資料庫中查詢資料,兩者無法保證資料一致,比如從 Redis 中取出表總行數和從資料庫中取出前 100 行資料,因為并發請求,只要從 Redis 和 MySQL 查詢資料的操作不是原子的,資料就不是一致的,
2、資料庫統計表記錄
資料庫新建一個統計表記錄行數,那不是和前一個方法一樣么,并發請求下得到的結果可能不一致,
可以的,行資料和統計資料同時存在資料庫中,并且資料庫支持事務,所以可以將多個操作封裝成原子的,保證資料一致,
多種count操作的效率
count(主鍵id)
存盤引擎遍歷表拿到行記錄回傳,server 層決議出 id 值,判斷是否為 null,統計行數,
count(1)
存盤引擎遍歷表不取值,server 層對于每一行放進去一個 1,判斷是否為 null,統計行數,
因為 server 層需要決議引擎回傳的結果拿到 id,所以 count(1) 比 count(主鍵id) 高效,
count(欄位)
存盤引擎遍歷表取出欄位回傳,server 層決議,判斷欄位是否為 null,統計行數,
count(*)
MySQL 進行了優化,存盤引擎遍歷表不取值,server 層判斷是否為 null,逐行累加,
效率從高到低:count(*) ≈ count(1) > count(主鍵id) > count(欄位)
提問
在剛剛討論的方案中,我們用了事務來確保計數準確,由于事務可以保證中間結果不被別的事務讀到,因此修改計數值和插入新記錄的順序是不影響邏輯結果的,
但是,從并發系統性能的角度考慮,你覺得在這個事務序列里,應該先插入操作記錄,還是應該先更新計數表呢?
先執行插入操作,再執行更新操作,從并發系統性能的角度考慮,應該盡可能減少鎖等待,而更新操作需要加行鎖,行鎖在事務提交之后才會釋放,所以最后執行更新操作,減少鎖等待,提高并發度,
參考
- [1] count這么慢,我該怎么辦
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/300398.html
標籤:MySQL
