mysql55和57的区别_技术分享 | MySQL:count(*)、count(字段) 实现上区别
我們繼續來討論一下 count(*)、count(字段)實現上的區別。注意我們這里都使用 Innodb 做為存儲引擎,不討論其他引擎。因為了有了前面的討論,更容易看出它們的區別,這里我們有如下注意點:我們需要做到執行計劃一樣,這里以全表掃描為例。實際上 count 很可能使用到覆蓋索引(Using index),本文主要討論它們實現的異同。
count(*) 和 count(字段) 在結果上可能并不一致。比如 count(字段),但是某些行本字段為 NULL 的話那么將不會統計行數,下面將會說明這種 NULL 判斷的位置。本文還是使用簡單的全表掃描來進行對比實現上的區別。首先我們要明確的是 count 使用的是一個 COUNT 計數器。
一、使用示例在示例中我們也可以看到兩個語句的結果實際上并不一致。mysql> show create table ?baguai_f ?\G
*************************** 1. row ***************************
Table: baguai_f
Create Table: CREATE TABLE `baguai_f` (
`id` int(11) DEFAULT NULL,
`a` varchar(20) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
`c` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select * from baguai_f ?;
+------+------+------+------+
| id ? | a ? ?| b ? ?| c ? ?|
+------+------+------+------+
| ? ?1 | g ? ?| g ? ?| NULL |
| ? ?1 | g1 ? | g1 ? | g1 ? |
| ? ?3 | g2 ? | g2 ? | g2 ? |
| ? ?4 | g ? ?| g ? ?| NULL |
| ? ?5 | g ? ?| g ? ?| NULL |
| ? ?6 | g3 ? | g3 ? | g3 ? |
+------+------+------+------+
6 rows in set (0.00 sec)
mysql> desc select count(*) from ?baguai_f where b='g';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table ? ?| partitions | type | possible_keys | key ?| key_len | ref ?| rows | filtered | Extra ? ? ? |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| ?1 | SIMPLE ? ? ?| baguai_f | NULL ? ? ? | ALL ?| NULL ? ? ? ? ?| NULL | NULL ? ?| NULL | ? ?6 | ? ?16.67 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> desc select count(c) from ?baguai_f where b='g';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table ? ?| partitions | type | possible_keys | key ?| key_len | ref ?| rows | filtered | Extra ? ? ? |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| ?1 | SIMPLE ? ? ?| baguai_f | NULL ? ? ? | ALL ?| NULL ? ? ? ? ?| NULL | NULL ? ?| NULL | ? ?6 | ? ?16.67 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> ?select count(*) from ?baguai_f where b='g';
+----------+
| count(*) |
+----------+
| ? ? ? ?3 |
+----------+
1 row in set (0.00 sec)
mysql> ?select count(c) from ?baguai_f where b='g';
+----------+
| count(c) |
+----------+
| ? ? ? ?0 |
+----------+
1 row in set (0.00 sec)
這種不一致來自于 b='g' 的 c 列中都是 NULL 值,因此 count(c) 返回為 0。二、示例中 count(*) 獲取數據流程簡析注意在《MySQL:查詢字段數量多少對查詢效率的影響》一文中我們已經詳細的描述了部分流程,這里不再贅述,如果需要更加詳細的了解,自行參考。
1. MySQL 層 構建 read_set
這里構建的 read_set 實際上只會包含列b,即一個字段。
2. Innodb 層 構建模板
同理根據 read_set 構建的字段模板中只會包含列b。LOOP:這里開始循環返回每一條數據
3. Innodb 層 根據模板返回數據
這里我們可以看看模板的數量和模板對應的具體列名模板的數量斷點:row_sel_store_mysql_rec查看模板數量:(gdb)?p?prebuilt->n_template$1?=?1查看模板對應的字段斷點:row_sel_field_store_in_mysql_format_func查看模板對應的字段:(gdb)?p?field->name$3?=?{m_name?=?0x7ffe7c99cf85?"b"}
顯然這里只是將 b 列的值返回給了 MySQL層,這里也很好理解,因為 b 列在 MySQL 層需要繼續做過濾操作。
4. MySQL 層 過濾條件 b='g'
好了,當前返回給 MySQL 層的數據中只有 b 列的數據,然后施加 b='g' 這個條件進行過濾。
5. MySQL 層 過濾后做一個 COUNT 計數操作
對于普通的 select 語句過濾后的數據就可以返回了,但是對于 count 這種操作,這里做的是一個計數操作,其中行會對 count 字段的 NULL 值進行判斷,當然這里是 count(*)?也就不存在 NULL 值判斷了,下面是這段代碼:bool?Item_sum_count::add(){??if?(aggr->arg_is_null(false))????return?0;??count++;??return?0;}
END LOOP
最終我們只需要返回這個計數就可以了。下面是發送的數據,斷點可以設置在 Query_result_send::send_data 中。$22?=?Item::SUM_FUNC_ITEM(gdb)?p?((Item*)(items)->first->info)->field_type()$23?=?MYSQL_TYPE_LONGLONG(gdb)?p?((Item*)(items)->first->info)->val_int()$24?=?3(gdb)?p?(items)->first->info$26?=?(void?*)?0x7ffe7c006580(gdb)?p?((Item_sum_count*)$26)->count$28?=?3
我們可以發送的數據實際就是這個計數器,最終值為 3。
三、示例中 count(c) 獲取數據流程的不同
實際上整個流程基本一致,但是區別在于:構建的 read_set 不同,模板個數自然不同,因為需要 2 個字段,即 b、c 兩個字段,其中 b 列用于 where 條件過濾,而 b 列用于統計是否有 NULL 值,因此模板數量為 2,如下:(gdb)?p?prebuilt->n_template$29?=?2做 COUNT 計數器的時候會根據 c 列的 NULL 值做實際的過濾,操作只要是 NULL 則 count 計數不會增加 1,這個還是參考這段代碼:bool?Item_sum_count::add(){??if?(aggr->arg_is_null(false))?//過濾NULL值????return?0;??count++;??return?0;}
最終會調入函數 Field::is_null 進行 NULL 值判斷,斷點可以設置在這里。
四、不同點總結示例中的語句 count(c) 返回為 0。現在我們很清楚了,這些數據什么時候過濾掉的,總結如下:Innodb 層返回了全部的行數據。
MySQL 層通過 where 條件過濾,剩下了 b='g' 的行。
MySQL 層通過 NULL 判斷,將剩下的 count(c) 中為 NULL 的行也排除在計數之外。
而 count(*) 則沒有第 3 步,這是一個不同。
然后的不同點就是在返回的字段上:count(c) 很明顯除了 where 條件以外,還需要返回 c 列給 MySQL 層
count(*) 則不需要返回額外的字段給 MySQL 層,只需要 MySQL 層過濾需要的b列即可。
通過上面的分析,實際上效率沒有太大的差別,我覺得同樣執行計劃,同樣返回數據結果的前提下,可能 count(*) 的效率要略微高一點。
五、備用棧幀(下圖需點擊放大查看)NULL 值計數過濾棧幀
最后推薦高鵬的專欄《深入理解 MySQL 主從原理 32 講》,想要透徹了解學習 MySQL 主從原理的朋友不容錯過。
總結
以上是生活随笔為你收集整理的mysql55和57的区别_技术分享 | MySQL:count(*)、count(字段) 实现上区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iis vue history 配置_V
- 下一篇: java %1$s_%1$s %1$d