Mysql 的 count 太慢了該怎麼辦?

2021-10-10 16:27:24 字數 2193 閱讀 1945

在不同的mysql引擎中,count(*)有不同的實現方式。

如果加了where 條件的話,myisam表也是不能返回得這麼快的。

為什麼innodb不跟myisam一樣,也把數字存起來呢?

因為即使是在同乙個時刻的多個查詢,由於多版本併發控制(mvcc)的原因,innodb表「應該返回多少行」也是不確定的。

假設表t中現在有10000條記錄,設計三個使用者並行的會話。

圖1 會話a、b、c的執行流程

在最後乙個時刻,三個會話a、b、c會同時查詢表t的總行數,拿到的結果不同。

這和innodb的事務設計有關係,預設的隔離級別可重複讀,在**上就是通過mvcc。每一行記錄都要判斷自己是否對這個會話可見,因此對於count(*)請求來說,innodb只好把資料一行一行地讀出依次判斷,可見的行才能夠用於計算基於這個查詢的表的總行數。

mysql在執行count(*)操作的時候也做了優化。

innodb是索引組織表,主鍵索引樹的葉子節點是資料,普通索引樹的葉子節點是主鍵值所以比主鍵索引樹小。對於count(*)這樣的操作,遍歷哪個索引樹得到的結果邏輯上都是一樣的。因此,mysql優化器會找到最小的那棵樹來遍歷。

show table status命令的輸出結果裡面也有乙個table_rows用於顯示這個表當前有多少行,這個命令執行挺快的。

索引統計的值是通過取樣來估算的。實際上,table_rows就是從這個取樣估算得來的,因此它也很不准。官方文件說誤差可能達到40%到50%。所以,show table status命令顯示的行數也不能直接作為count(*)來使用。

如果現在有乙個頁面經常要顯示交易系統的操作記錄總數,我們只能自己計數。基本思路是要自己找乙個地方,把操作記錄表的行數存起來。

可以用乙個redis服務來儲存這個表的總行數。這個表每被插入一行redis計數就加1,每被刪除一行redis計數就減1。這種方式下,讀和更新操作都很快。

but,快取系統可能會丟失更新。

實際上,即使redis正常工作,這個值還是邏輯上不精確的。

例如:有乙個要顯示操作記錄的總數的頁面,同時還要顯示最近操作的100條記錄。那麼,這個頁面的邏輯就需要先到redis裡面取出計數,再到資料表裡面取資料記錄。

不精確體現如下:

圖2 會話a、b執行時序圖

在t3時刻會話b來查詢的時候,會顯示出新插入的r這個記錄,但是redis的計數還沒加1。

把會話a的更新順序換一下,都是先操作redis,然後再運算元據。

圖3 調整順序後,會話a、b的執行時序圖

會話b在t3時刻查詢的時候,redis計數加了1了,但還查不到新插入的r這一行。併發系統無法精確控制不同執行緒的執行時刻,因為存在圖中的這種操作序列,所以,即使redis正常工作,這個計數值還是邏輯上不精確的。

使用redis的話兩個不同的儲存構成的系統,不支援分布式事務,無法拿到精確一致的檢視

如果把這個計數直接放到資料庫裡單獨的一張計數表c中,會怎麼樣?

圖4 會話a、b的執行時序圖

雖然會話b的讀操作仍然是在t3執行的,但是因為這時候更新事務還沒有提交,計數值加1這個操作對會話b還不可見。

會話b看到的結果裡, 查計數值和「最近100條記錄」看到的結果,邏輯上就是一致的。

count(*)、count(主鍵id)和count(1) 都表示返回滿足條件的結果集的總行數;而count(字段),則表示返回滿足條件的資料行裡面,引數「字段」不為null的總個數。

分析效能差別的時候,有這樣幾個原則:

innodb引擎會遍歷整張表,把每一行的id值都取出來,返回給server層。server層拿到id後,判斷不為空,按行累加。

innodb引擎遍歷整張表,但不取值。server層對於返回的每一行,放乙個數字「1」進去,判斷不為空,按行累加。

count(1)執行得要比count(主鍵id)快。因為從引擎返回id會涉及到解析資料行,以及拷貝字段值的操作。

前面的第一條原則:server層要什麼字段,innodb就返回什麼字段。

並不會把全部字段取出來,而是專門做了優化,不取值。count(*)肯定不是null,按行累加。

結論:按照效率排序的話,count(字段) < count(主鍵id) < count(1) ≈ count(*),因此建議盡量使用count(*)。

感覺shopex現在的公升級方式太慢了

我是說產品的更新,484,485是乙個經典的版本,那時候免費,shopex 系統市場佔用率很高。但是485以後呢,只有小版本的更新,fxw ekd 都是改進版本吧,沒用特別大幅度的更新。5年前,10年前,可以搞那種開發設計2年然後再測試投入執行什麼的,但是放到現在,競爭越來越激烈了,更新速度非常快,...

make太慢了,加快編譯速度的方法 make j

make j 既然io不是瓶頸,那cpu就應該是乙個影響編譯速度的重要因素了。用make j帶乙個引數,可以把專案在進行並行編譯,比如在一台雙核的機器上,完全可以用make j4,讓make最多允許4個編譯命令同時執行,這樣可以更有效的利用cpu資源。還是用kernel來測試 用make 40分16...

make太慢了,加快編譯速度的方法 make j

2018 01 18 09 04 05 gonghuihuihui 閱讀數 21957 收藏 更多 分類專欄 linux make j 既然io不是瓶頸,那cpu就應該是乙個影響編譯速度的重要因素了。用make j帶乙個引數,可以把專案在進行並行編譯,比如在一台雙核的機器上,完全可以用make j4...