關於 MySQL 百萬數據量的 count(*) 查詢如何優化?

明確需求

對這個問題有興趣是源於一次開發中遇到要統計人數的需求。相似於「獲得」專欄的訂閱數。mysql

clipboard.png

可是個人數據量比這個大不少,而對數據的準確性要求就不那麼高。因此首先要明確需求。其餘答案有的說了用緩存,有的答案對比了count(*)、count(1)的區別,都很好,可是我認爲仍是要看一下題主的場景。我根據我實際開發的經驗總結以下幾個方面,FYI。redis

clipboard.png

數據量大/準確性要求低/請求量大

  1. 這種場景通常是C端產品,好比上面說的獲得APP的訂閱數目,若是對一致性要求不高,能夠直接在內存中使用緩存,用guava在內存中作一個緩存定時刷新便可,百萬量級count(*)有緩存的頻率還不至於有啥性能問題;
  2. 可是內存內緩存有一個問題就是不一樣服務器之間的緩存數量是不一致的,能夠考慮用redis做爲計數,通常這種場景是大多數同窗遇到的,簡單粗暴搞定便可;
  3. 用show table status。這個建議仍是不要用了,翻了下mysql 的doc,40%的偏差機率,碰上就有點大了呀。
TABLE_ROWS
The number of rows. Some storage engines, such as MyISAM, store the exact count. For other storage engines, such as InnoDB, this value is an approximation, and may vary from the actual value by as much as 40% to 50%. In such cases, use SELECT COUNT(*) to obtain an accurate count.

數據量大/準確性要求高/請求量通常

這種場景通常出如今帳務上,好比有多少人打款。並且估計DAU在億級別的公司可能纔會遇到。這裏最關鍵的問題仍是一致性的要求。在併發系統中,看看咱們用redis,咱們看看會出現什麼樣的一致性問題:sql

時間       A processor         B processor
T1         插入數據
T2                             1.redis#get計數器;2. 查詢最新的N條數據
T3         redis#incr

在T2的時間點的時候會出現數據不一致,B看到的是數據已經更新,可是數據庫還沒更新。咱們就在想,若是放到一個事務裏面,就能夠完美解決這個問題了呀。因爲事務,innoDB不支持像MyISAM準確計數,解鈴還須繫鈴人,因此咱們建一個計數表(count_table)+事務,解這個問題了。數據庫

時間         會話A                            會話B
T1         begin;
           在計數表中插入一條數據;
T2                                          begin;
                                            1. 讀count_table;
                                            2. 查詢最新的N條數據
                                            commit;
T3         更新conut_table;
           commit;

在T1的時候,若是採用Mysql默認的事務隔離級別:讀提交。由於T1事務尚未提交,因此插入的數據,B是讀不到的,因此從邏輯上來講是一致的。緩存

數據量大/準確性要求高/請求量特別高

抱歉,沒遇到過。若是你以爲你遇到了,你的架構須要你從新design and review,相信我。服務器

帶條件count(*)

不少時候咱們的業務場景不是數據量多,而是條件複雜。這其實就是一個查詢優化的問題了,和是否是count(*)沒有關係,那麼有如下兩招經常使用,這個得具體問題具體分析了。好比時間維度能夠加一個索引來優化;架構

select * from table_name where a = x and b = x;
  • 加索引
  • 業務拆分

count性能比較

  • count(primary key)。遍歷整個表,把主鍵值拿出來,累加;
  • count(1)。遍歷整個表,可是不取值,累加;
  • count(非空字段)。遍歷整個表,讀出這個字段,累加;
  • count(能夠爲空的字段)。遍歷整個表,讀出這個字段,判斷不爲null累加;
  • count(*)。遍歷整個表,作了優化,不取值,累加。

結合mysql的一些索引查詢知識,咱們能夠大體得出以下結論。併發

clipboard.png

建議直接使用count(*)。app

Leetcode名企之路

相關文章
相關標籤/搜索