count(1) 比 count(*) 效率高么
有 Where 條件的 count,會根據掃碼結果count 一下所有的行數,其性能更依賴於你的 Where 條件
MyISAM 引擎會把一個表的總行數記錄了下來,所以在執行 count(*) 的時候會直接返回數量,執行效率很高。
在 MySQL 5.5 以后默認引擎切換為 InnoDB,InnoDB 因為增加了版本控制(MVCC)的原因,同時有多個事務訪問數據並且有更新操作的時候,每個事務需要維護自己的可見性,那么每個事務查詢到的行數也是不同的,所以不能緩存具體的行數,他每次都需要 count 一下所有的行數。
官方解釋
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
InnoDB 處理 SELECT COUNT(*) 和 SELECT COUNT(1) 使用榮陽的操作方式,兩者沒有實質性的區別
count的定義
Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement. The result is a BIGINT value.
返回 SELECT 語句檢索的行中 expr 的非 NULL 值的計數
也就是說COUNT這個聚合函數,對 SELECT 的結果集進行計數,但是需要參數不為 NULL
COUNT(*) is somewhat different in that it returns a count of the number of rows retrieved, whether or not they contain NULL values.
count() 不同,他不關心這個返回值是否為空都會計算他的count,因為 count(1) 中的 1 是恆真表達式,那么 count() 還是 count(1) 都是對所有的結果集進行 count,所以他們本質上沒有什么區別
InnoDB的優化
這個地方 InnoDB 本身也做了一些優化,它會使用最小的二級索引來進行 count 的查詢優化。如果沒有二級索引才會選擇聚簇索引,這樣的設計單從 IO 的角度就節省了很多開銷
count(column) 也是會遍歷整張表,但是不同的是它會拿到 column 的值以后判斷是否為空,然后再進行累加,那么如果針對主鍵需要解析內容,如果是二級索引所以需要再次根據主鍵獲取內容,又是一次 IO 操作,所以 count(column) 的性能肯定不如前兩者
count(*)=count(1)>count(primary key)>count(column)
count(*) 在查詢上依賴於所有的數據集,我們也需要盡量的規避全量 count, 我們針對可預見的 count 查詢做適當的緩存,可以是 Redis,也可以是獨立的 MySQL count 表,當然無論是哪種方式我們都需要考慮一致性的問題。
文章中的其他衍生點
什么是mvcc
多版本並發控制,樂觀鎖的一種實現方式
MVCC主要是為Repeatable-Read(可重復讀)事務隔離級別做的,大多數情況下代替了行鎖,實現了對讀的非阻塞,讀不加鎖,讀寫不沖突。但是每行記錄都需要額外的存儲空間,需要做更多的行維護和檢查工作
MVCC 並發控制下的讀事務一般使用時間戳或者事務 ID去標記當前讀的數據庫的狀態。讀寫並存的時候,寫操作會根據目前數據庫的狀態,創建一個新版本,並發的讀則依舊訪問舊版本的數據
MVCC就是用 同一份數據臨時保留多版本的方式 的方式,實現並發控制,保證一個讀事務永遠不會被阻塞
innodb存儲的最基本row中包含一些額外的存儲信息,MVCC相關的有: DB_TRX_ID ,DB_ROLL_PTR,DB_ROW_ID
DB_TRX_ID 插入或更新行的最后一個事務的事務標識符
DB_ROLL_PTR 寫入回滾段的撤消日志記錄,找之前版本的數據就是通過這個
DB_ROW_ID 行標識.這個用於索引當中
DB_TRX_ID記錄了行的創建的時間
在insert操作時, “創建時間”=DB_TRX_ID。“刪除時間無”;
在update操作時,復制新增行的“創建時間”=DB_TRX_ID,刪除時間無,舊數據行“創建時間”不變,刪除時間=該事務DB_TRX_ID;
在delete操作時,相應數據行的“創建時間”不變,刪除時間=該事務的DB_ROW_ID;
在select操作時,對兩者都不修改,只讀相應的數據
什么是最小二級索引
二級索引是每個葉子節點中包含了不是主鍵的索引列,緊接着就是主鍵列的這種索引
為什么二級索引需要兩次索引?
二級索引的葉子節點保存的不是指向行的物理位置的指針,而是行的主鍵值,所以二級索引需要在查找一次聚簇索引。
葉子節點存儲主鍵值的目的:
減少了當出現行移動或者數據分裂時二級索引的維護工作。使用主鍵值當作指針會讓二級索引占用更多的空間,但換來在移動時無須更新二級索引中的主鍵指針。
什么是聚簇索引
InnoDB的聚簇索引實際上是在同一個結構中保存了B-Tree索引和數據行。 可以將聚簇索引理解為數據庫中完整的一張數據表
每一個表都有一個聚簇索引,並且只有一個
聚簇索引的優點:
- 將相關數據保存在一起
- 數據訪問速度更快
- 使用覆蓋索引掃描的查詢可以直接使用頁節點中的主鍵值
聚簇索引的缺點:
- 插入速度依賴插入順序
- 使全表掃描速度變慢
- 更新聚簇索引列(主鍵)的代價很高
如何保證數據一致性
分布式事務
什么是分布式事務? 如何實現分布式事務?
另起一片文章