幾種獲取記錄數的方法
count(*): MySQL 優化過,掃描的行數小於總記錄數。執行效率高。
count(1): 遍歷所有記錄,不取值,對每行嘗試新增一個 “1” 列,如果不為 null,就計入累加(引擎層)。
count(主鍵): 遍歷所有記錄,並把每個記錄的 id 取出返回 Server 層判斷,將不為 null 的計入累加。
count(欄位): 遍歷所有記錄,並把每個記錄的欄位值取出返回 Server 層判斷,將不為 null 的計入累加。
效率排序: count(*) ≈ count(1) > count(主鍵) > count(欄位)
除此之外,還可以通過 " show table status like '表名' " 檢視 'TABLE_ROW' 引數來獲取系統內部通過取樣估算的記錄數,但誤差會達到 40% -- 50%。
優化
使用快取
通過上面的分析可以知道通過 count(*) 來獲取計數已經是效率最高的一種方式了, 但是如果效率還是低呢?首先效率低肯定是執行計數操作的併發量太高所導致的。而應對查詢操作最常見的優化方式就是使用快取,但是使用快取適用於查多寫少的場景,不過因為計數不會因為修改操作而改變,只會被增刪操作所改變,所以在增刪操作少的場景也是可以使用的。但是還有另外一個問題,那就是獲取計數的業務和計數自增自減的操作不能保證原子性。這樣導致查出的結果可能並不準確。
比如:
有一個頁面,要顯示操作記錄的總數,同時還要顯示最近操作的 100 條記錄。那麼,這個頁面的邏輯就需要先到 Redis 裡面取出計數,再到資料表裡面取資料記錄。
一種是,查到的 100 行結果裡面有最新插入記錄,而 Redis 的計數裡還沒加 1;
另一種是,查到的 100 行結果裡沒有最新插入的記錄,而 Redis 的計數裡已經加了 1。
那麼第一種情況就會導致查詢的計數和記錄對不上,得到的結果混亂。
資料庫
如果將計數結果單獨建立一張表來儲存,搭配 MySQL 的可重複讀隔離級別,就可以實現資料讀取的 "原子性" ,並且效率也會很高。
在會話B讀取計數時就會因為會話A還未提交,所以不會讀取到會話A執行過的操作。