幾種獲取記錄數的方法
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執行過的操作。