Mysql 多種Count寫法的區別

今天咱們來看看count的不一樣實現方式數據庫

count(*) 的實現方式

先來看一下 coun(*)的實現,MyISAM和InnoDB的實現上是不一樣的緩存

MyISAM 引擎把一個表的總行數存在了磁盤上,所以執行 count() 的時候會直接返回這個數,效率很高; 而 InnoDB 引擎就麻煩了,它執行 count() 的時候,須要把數據一行一行地從引擎裏面讀出來,而後累積計數併發

若是加了 where條件的話,MyISAM 表也是不能返回得這麼快的函數

爲何 InnoDB 不跟 MyISAM 同樣,也把數字存起來呢?性能

這是由於即便是在同一個時刻的多個查詢,因爲多版本併發控制(MVCC)的緣由, InnoDB 表應該返回多少行也是不肯定的優化

這和 InnoDB 的事務設計有關係,可重複讀是它默認的隔離級別,在代碼上就是經過MVCC 來實現的。每一行記錄都要判斷本身是否對這個會話可見,所以對於 count(*) 請求來講,InnoDB只好把數據一行一行地讀出依次判斷,可見的行才能 夠用於計算「基於這個查詢」的表的總行數線程

InnoDB 是索引組織表,主鍵索引樹的葉子節點是數據,而普通索引樹的葉子節點是主鍵值。因此,普通索引樹比主鍵索引樹小不少。對於count(*) 這樣的操做,遍歷哪一個索引樹獲得的結果邏輯上都是同樣的。所以,MySQL 優化器會找到最小的那棵樹來 遍歷。在保證邏輯正確的前提下,儘可能減小掃描的數據量,是數據庫系統設計的通用法則 之一設計

若是你用過 show table status 命令的話,就會發現這個命令的輸出結果裏面也有一個 TABLE_ROWS 用於顯示這個表當前有多少行,這個命令執行挺快的,那這個 TABLE_ROWS 能代替 count(*) 嗎server

索引統計的值是經過採樣來估算的。實際上,TABLE_ROWS 就是從這個採樣估算得來的,所以它也很不許。有多不許呢,官方文檔說偏差可能達到 40% 到 50%。因此,show table status 命令顯示的行數也不能直接使用排序

不一樣的 count 用法區別

咱們再來看看不一樣count用法的區別

首先你要弄清楚 count() 的語義。count() 是一個聚合函數,對於返回的結果集, 一行行地判斷,若是 count 函數的參數不是 NULL,累計值就加 1,不然不加。最後返回 累計值

因此,count(*)、count(主鍵 id) 和 count(1) 都表示返回知足條件的結果集的總行數;而 count(字段),則表示返回知足條件的數據行裏面,參數「字段」不爲 NULL 的總個數

對於 count(主鍵 id) 來講,InnoDB 引擎會遍歷整張表,把每一行的 id 值都取出來,返回給 server 層。server 層拿到 id 後,判斷是不可能爲空的,就按行累加

對於 count(1) 來講,InnoDB 引擎遍歷整張表,但不取值。server 層對於返回的每一 行,放一個數字「1」進去,判斷是不可能爲空的,按行累加

對於 count(字段) 來講:

  1. 若是這個「字段」是定義爲 not null 的話,一行行地從記錄裏面讀出這個字段,判斷不 能爲 null,按行累加;
  2. 若是這個「字段」定義容許爲 null,那麼執行的時候,判斷到有多是 null,還要把值 取出來再判斷一下,不是 null 才累加。

可是 count() 是例外,並不會把所有字段取出來,而是專門作了優化,不取值。 count() 確定不是 null,按行累加

按照效率排序的話,count(字段)<count(主鍵 id)<count(1)≈count(*)

計數解決方案

每次都調用count來計算比較消耗性能

對於更新很頻繁的庫來講,你可能會第一時間想到,用緩存系統來支持。

這種方式下,讀和更新操做都很快,但你再想一下 這種方式存在什麼問題嗎?

沒錯,緩存系統可能會丟失更新。

將計數保存在緩存系統中的方式,還不僅是丟失更新的問題。即便 Redis 正常工做,這個值仍是邏輯上不精確的

在併發系統裏面,咱們是沒法精確控制不一樣線程的執行時刻的,因此,咱們說即便 Redis 正常工做,這個計數值仍是邏輯上不精確的

在數據庫保存計數能夠解決這個問題,咱們能夠經過事務來保證一致性

相關文章
相關標籤/搜索