MySQL Execution Plan--COUNT相关测试
COUNT全表记录
在MySQL中,相同的SQL不同的存储引擎执行计划不同:
对于MyISAM引擎,由于使用表锁进行并发控制,同一时间点多个并发线程执行相同查询获得的结果相同,且MyISAM存储引擎专门存储表总记录数,因此使用COUNT(*)查询全表记录时能直接返回。 而对于InnoDB存储引擎,由于使用MVCC和行锁进行并发控制,同一时间点多个并发线程执行相同查询获得的结果存在差异(每个回话的READVIEW不同),且没有专门存储表总记录数,因此每次查询都需要扫描全表或扫描某个索引的全部记录。 For transactional storage engines such as InnoDB, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count. InnoDB does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently, SELECT COUNT(*) statements only count rows visible to the current transaction. Prior to MySQL 5.7.18, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index. As of MySQL 5.7.18, InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, the clustered index is scanned. Processing SELECT COUNT(*) statements takes some time if index records are not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does. However, this method may not scale well in situations where thousands of concurrent transactions are initiating updates to the same counter table. If an approximate row count is sufficient, use SHOW TABLE STATUS. InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference. For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause. This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL。
现有测试表TB101:
CREATE TABLE `tb101` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`C1` int(11) NOT NULL,
`C2` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM AUTO_INCREMENT=140001 DEFAULT CHARSET=utf8
对于没有WHERE条件的COUNT(*)/COUNT(1)/COUNT(ID)/COUNT(C1)的执行计划为:
mysql> EXPLAIN SELECT COUNT(*) FROM TB101 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Select tables optimized away
1 row in set, 1 warning (0.00 sec)
对于没有WHERE条件的COUNT(C2)的执行计划为:
mysql> EXPLAIN SELECT COUNT(C2) FROM TB101 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: TB101
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 140000
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
可以发现,对于MyISAM存储引擎,在没有WHERE条件下,如果C1列为NOT NULL,则可以将COUNT(C1)与COUNT(*)和COUNT(1)做相同的处理。
针对上面的测试,对于InnoDB存储引擎,在没有WHERE条件下:
1、ID列为NOT NULL主键,COUNT(ID)和COUNT(1)或COUNT(*)的执行计划相同,返回结果相同。 2、C1列为NOT NULL,COUNT(C1)和COUNT(1)或COUNT(*)的执行结果相同,但执行计划不同。
COUNT(expr)异同
1、COUNT(1)和COUNT(*)等价,两者在执行计划和执行效率上完全相同。
个人推荐使用COUNT(1)替换COUNT(*),原因是简单直观,
他人推荐使用COUNT(1),原因是符合SQL92标准,阿里巴巴Java开发手册推荐。
2、COUNT(*)和COUNT(C1)不一定等价,两者执行计划和执行结果会存在差异。
COUNT(*):执行返回满足WHERE条件的行数,不考虑NULL值问题
COUNT(C1): 执行返回满足WHERE条件且C1不等于NULL的行数,不统计C1等于NULL的行。 换种理解思路:
对于MyISAM引擎表和InnoDB引擎表,无论是显式主键还是因此ROWID,都要求非空唯一,每行记录都肯定存在一个不为NULL的列(列组),因此计算COUNT(*)时不需要考虑NULL值问题。
一个有趣的扩展,如果C1为NOT NULL,那么COUNT(C1)与COUNT(1)的返回结果相同,那么MySQL会对此进行优化么?
现有测试表结果如下:
CREATE TABLE `tb01` (
`ID` int() NOT NULL AUTO_INCREMENT,
`C1` int() NOT NULL,
`C2` int() NOT NULL,
PRIMARY KEY (`ID`),
KEY `IDX_C2` (`C2`)
) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8
查看COUNT(*)和COUNT(C1)的执行计划:
mysql> EXPLAIN SELECT COUNT(*) FROM TB01 WHERE C2<100 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: TB01
partitions: NULL
type: range
possible_keys: IDX_C2
key: IDX_C2
key_len: 4
ref: NULL
rows: 99
filtered: 100.00
Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec) mysql> EXPLAIN SELECT COUNT(C1) FROM TB01 WHERE C2<100 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: TB01
partitions: NULL
type: range
possible_keys: IDX_C2
key: IDX_C2
key_len: 4
ref: NULL
rows: 99
filtered: 100.00
Extra: Using index condition
1 row in set, 1 warning (0.00 sec)
从上面执行计划可以发现,在处理COUNT(*)时,仅需要使用IDX_C2即可完成查询,因此Extra为Using index,而在处理COUNT(C1)时,需要使用IDX_C2进行过滤后再执行回表查询,因此Extra为Using index condition。
针对上面的测试,MyISAM存储引擎和InnoDB存储引擎的测试结果相同。
COUNT(DISTINC ...)操作
MySQL官网解释为:
COUNT(DISTINCT expr,[expr...]) Returns a count of the number of rows with different non-NULL expr values. In MySQL, you can obtain the number of distinct expression combinations that do not contain NULL by giving a list of expressions. In standard SQL, you would have to do a concatenation of all expressions inside COUNT(DISTINCT ...).
在MySQL中允许执行:
SELECT COUNT(DISTINCT ID,C1) FROM TB02;
但不允许执行:
SELECT COUNT(ID,C1) FROM TB02;
总结
1、对于InnoDB和MyISAM存储引擎,COUNT(1)和COUNT(*)在任何场景下都等价,执行性能和执行计划相同。 2、在查询全表记录(没有WHERE条件)时,对于MyISAM存储引擎,存储引擎存储表总记录数,无需扫描数据因此查询可以很快返回,对于InnoDB存储引擎,需要扫描全表或某个索引的全部记录因此查询可能比较耗时。 3、对于MyISAM存储引擎,在没有WHERE条件情况下,如果列C1为NOT NULL,那么COUNT(C1)和COUNT(*)执行操作相同。 4、对于InnoDB存储引擎,如果列C1为主键,那么COUNT(C1)和COUNT(*)执行计划和执行效率相同,如果C1为NOT NULL,那么COUNT(C1)和COUNT(*)执行计划和执行效率不一定相同,只有在查询使用C1列上索引时才可能相同。
参考链接:
https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_count
https://dev.mysql.com/doc/refman/5.7/en/create-index.html
https://mp.weixin.qq.com/s/IOHvtel2KLNi-Ol4UBivbQ
MySQL Execution Plan--COUNT相关测试的更多相关文章
- Oracle SQL explain/execution Plan
From http://blog.csdn.net/wujiandao/article/details/6621073 1. Four ways to get execution plan(anyti ...
- sql server 执行计划(execution plan)介绍
大纲:目的介绍sql server 中执行计划的大致使用,当遇到查询性能瓶颈时,可以发挥用处,而且带有比较详细的学习文档和计划,阅读者可以按照我计划进行,从而达到对执行计划一个比较系统的学习. 什么是 ...
- MySQL优化之COUNT(*)效率
MySQL优化之COUNT(*)效率 刚给一个朋友解决他写的Discuz!插件的问题,说到MySQL的COUNT(*)的效率,发现越说越说不清楚,干脆写下来,分享给大家. COUNT(*)与COUNT ...
- 用count(*)还是count(列名) || Mysql中的count()与sum()区别
Mysql中的count()与sum()区别 首先创建个表说明问题 CREATE TABLE `result` ( `name` varchar(20) default NULL, `su ...
- mysql中的count(primary_key)、count(1)、count(*)的区别
表结构如下: mysql> show create table user\G; *************************** 1. row ********************** ...
- mysql提示Column count doesn't match value count at row 1错误
mysql提示Column count doesn't match value count at row 1错误,后来发现是由于写的SQL语句里列的数目和后面的值的数目不一致, 比如insert in ...
- mysql错误:Column count doesn't match value count at row 1
mysql错误:Column count doesn't match value count at row 1 mysql错误:Column count doesn't match value cou ...
- Execution Plan 执行计划介绍
后面的练习中需要下载 Demo 数据库, 有很多不同的版本, 可以根据个人需要下载. 下载地址 -http://msftdbprodsamples.codeplex.com/ 1. 什么是执行计划 ...
- 【mysql】mysql统计查询count的效率优化问题
mysql统计查询count的效率优化问题 涉及到一个问题 就是 mysql的二级索引的问题,聚簇索引和非聚簇索引 引申地址:https://www.cnblogs.com/sxdcgaq8080/p ...
随机推荐
- k8s资产清单(二)
什么是清单 说白了清单是k8s当中用来定义pod的文件,语法格式遵循yaml语法,在yaml当中可以定义控制器类型,元数据,容器端口号等等等....,也可以针对于清单对pod进行删除等操作 为什么太学 ...
- 8. Go语言—指针类型
一.指针类型介绍 普通类型,变量存的就是值,也叫值类型. 获取变量的地址,用&,比如:var a int ,获取a的地址:&a 指针类型,变量存的是一个地址,这个地址存的才是值(指针存 ...
- Pwn-level4
题目地址 https://dn.jarvisoj.com/challengefiles/level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0 友情连接 https://www ...
- 201871010110-李华《面向对象程序设计(java)》第十七周学习总结
博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...
- 剑指Offer-2.替换空格(C++/Java)
题目: 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 分析: 题意明确,就是将一个字符 ...
- ABP docker发布
环境:CentOS 7.6 64位 linux基本命令: cd:进入某个文件夹 mkdir:创建文件夹 ls:显示文件 ll:罗列出当前文件或目录的详细信息 判断是文件 还是文件夹: Linux系统中 ...
- ASP.NET开发实战——(十三)ASP.NET MVC 与数据库之EF实体类与数据库结构
大家都知道在关系型数据库中每张表的每个字段都会有自己的属性,如:数据类型.长度.是否为空.主外键.索引以及表与表之间的关系.但对于C#编写的类来说,它的属性只有一个数据类型和类与类之间的关系,但是在M ...
- 【ECNU3542】神奇的魔术(二分交互题)
点此看题面 大致题意: 有一个\(1\sim 2^n\)的排列,\(n\le7\),每次交互告诉你有几个位置上的数是正确的,让你在\(1000\)轮以内猜出每个位置上的数. 二分 显然,我们可以通过二 ...
- DFS(三):八皇后问题
[例1]八皇后问题. 在一个8×8国际象棋盘上,放置8个皇后,每个皇后占一格,要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行.同一列或同一对角线上.问共有多少种不同的放置方法? (1 ...
- 百度APP移动端网络深度优化实践分享(三):移动端弱网优化篇
本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<三>弱网优化>,感谢原作者的无私分享. 一.前言 网络优化解决的核心问题有三个 ...