explain:通常应用于sql语句的性能分析
通过对查浦酒句的分析,可以了解查询语句的执行情况,找出查询语向执行的瓶颈,从而优化查询语句。MySQL中提供了EXPLAIN语句,用来分析查询语句。本节介绍使用EXPLAIN语句分析查询语句的方法。
EXPLAIN语句的基本语法如下:
EXPLAIN [EXTENDED] SELECT select_ options
使用EXTENED关键字,EXPLAIN语句将产生附加信息。select options 是SELECT语句的查询选项,包括FROM WHERE子句等。
执行该语句,可以分析EXPLAIN后面的SELECT语句的执行情况,并且能够分析出所查 询的表的一些特征。
使用EXPLAIN语句来分析1个查询语句,执行如下语句:
mysql> mysql> explain select * from article where status = 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| 1 | SIMPLE | article | NULL | ALL | NULL | NULL | NULL | NULL | 10856301 | 10.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
下面我们对查询结果进行解释:
字段 | 描述 |
---|---|
id | select识别符。这是select的查询序列号,id的值越大优先级别越高,越先被执行,如果id相同,执行顺序右上至下 |
select_type | select_type表示select语句的类型。 |
table | type表示这一行的数据是关于哪张表的。 |
partitions | |
type | type表示的是表的连接类型。 |
possible_keys | possible_keys列支出mysql能使用哪个索引在该表中找到行。如果这列是null,则没有相关的索引。在这种情况下,可以通过检查where子句查看它是否引用某些列或者适合索引的列来提高查询性能。如果是这样,可以创建适合的索引来提高查询的性能 |
key | key表示查询实际应用到的索引,如果没有选择索引,该列的值是null。想要强制使mysql使用或者忽视possible_type列中的索引,在查询中使用force index,use index 或者 ignore index。 |
key_len | key_len表示mysql选择的索引字段按字节计算的长度,如果键是null,则长度为null,注意通过key_len值可以确定mysql将实际使用一个多列索引中的几个字段 |
ref | ref表示使用哪个列或常数与索引一起来查询记录 |
rows | rows显示mysql表中进行查询时必须检查的行数 |
filtered | |
Extra | extra显示mysql在处理查询时的详细信息。 |
对于以上字段信息不同值的解释:
SIMPLE
SIMPLE表示简单查询,其中不包括连接查询和子查询。
PRIMARY与SUBQUERY
PRIMARY表示主查询或者最外层的查询语句。SUBQUERY : 子查询
例如:
mysql> explain select * from article where id in (select id from article where content = 'LrQ2Ievq9P');
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+----------+----------+-------------+
| 1 | PRIMARY | article | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | SUBQUERY | article | NULL | ALL | NULL | NULL | NULL | NULL | 10856301 | 10.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+----------+----------+-------------+
2rows in set, 1 warning (16.28 sec)
DERIVED: 衍生查询-在select出一批自定义列的数据,概念上相当于一张表,但是该表只在语句执行过程出现和
UNION 与 UNION RESULT
UNION:联合查询,union 后面的那张表就会表示成它
UNION RESULT: 联合结果
例如:
mysql> explain select * from article where id = 6565544 union all select * from article where id = 8484848 ;
+----+-------------+---------+------------+-------+---------------+---------+---------
+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | PRIMARY | article | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | UNION | article | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。
常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)
ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
index: Full Index Scan,index与ALL区别为index类型只遍历索引树
range:只检索给定范围的行,使用一个索引来选择行
ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
Extra列是用来说明一些额外信息的,我们可以通过这些额外信息来更准确的理解MySQL到底将如何执行给定的查询语句。MySQL提供的额外信息有好几十个,就不一个一个介绍了,在这只介绍常见的一些额外信息说明 .
① Using filesort: 如果根据索引列进行排序(order by 索引列)是可以用到索引的,SQL查询引擎会先根据索引列进行排序,然后获取对应记录的主键id执行回表操作,如果排序字段用不到索引则只能在内存中或磁盘中进行排序操作,MySQL把这种在内存或者磁盘上进行排序的方式统称为文件排序(英文名:filesort),如果某个查询需要使用文件排序的方式执行查询,就会在执行计划的Extra列中显示Using filesort
② Using temporary: 许多查询的执行过程中,MySQL会借助临时表来完成一些功能,比如去重、排序之类的,比如我们在执行许多包含distinct、group by、union等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL很有可能寻求通过建立内部的临时表来执行查询。如果查询中使用到了内部的临时表,在执行计划的Extra列将会显示Using temporary提示.
③ USING index: 表示相应的select操作中使用了覆盖索引(Covering Index),避免回表操作,效率不错!
如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表名索引用来读取数据而非执行查找动作。
④ Using where: 使用了where过滤
⑤ using join buffer: 在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度
⑥ impossible where: where子句的值总是false,不能用来获取任何元组
⑦ select tables optimized away: 在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
⑧ distinct: 优化distinct,在找到第一匹配的元组后即停止找同样值的工作
⑨ Using index condition:查找使用了索引,但是需要回表查询数据
当我们要对某一条sql的性能进行分析时,可以使用它。
Profiling是从 mysql5.0.3版本以后才开放的。启动profile之后,所有查询包括错误的语句都会记录在内。 关闭会话或者set profiling=0 就关闭了。(如果将profiling_history_size参数设置为0,同样具有关闭MySQL的profiling效果。)
此工具可用来查询SQL执行状态,System lock和Table lock 花多少时间等等,对定位一条语句的I/O消耗和CPU消耗 非常重要。(SQL 语句执行所消耗的最大两部分资源就是IO和CPU)–在mysql5.7之后,profile信息将逐渐被废弃,mysql推荐使用performance schema
开启以及使用过程:
set profiling=1; //打开分析
mysql> show profiles;
+----------+------------+---------------------------------------------------------------------------------------------------------+
| Query_ID | Duration | Query
|
+----------+------------+---------------------------------------------------------------------------------------------------------+
| 1 | 0.00034350 | select * from staffs where name = "shine"
|
| 2 | 0.00038350 | select id,`name`,city,monthsalary,gender from customers1s where city="" and gender=0 and monthsalary=99 |
+----------+------------+---------------------------------------------------------------------------------------------------------+
2 rows in set, 1 warning (0.01 sec)
select * from staffs where name = "shine";
select id,`name`,city,monthsalary,gender from customers1s where city="" and gender=0 and monthsalary=99;
show profile for query 1; //查看sql1的具体分析
mysql> show profile for query 1;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000106 |
| checking permissions | 0.000006 |
| Opening tables | 0.000037 |
| init | 0.000005 |
| System lock | 0.000009 |
| optimizing | 0.000010 |
| statistics | 0.000064 |
| preparing | 0.000007 |
| executing | 0.000002 |
| Sending data | 0.000038 |
| end | 0.000002 |
| query end | 0.000006 |
| closing tables | 0.000004 |
| freeing items | 0.000041 |
| cleaning up | 0.000008 |
+----------------------+----------+
15 rows in set, 1 warning (0.01 sec)
show profile ALL for query 1; //查看sql1相关的所有分析【主要看i/o与cpu,下边分析中有各项意义介绍】
mysql> show profile ALL for query 1;
+----------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+---------------+-------------------+-------------------+-------------------+-------+-----------------------+---------------------
-+-------------+
| Status | Duration | CPU_user | CPU_system | Context_voluntary |
Context_involuntary | Block_ops_in | Block_ops_out | Messages_sent | Messages_received | Page_faults_major | Page_faults_minor | Swaps | Source_function | Source_file | Source_line |
+----------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+---------------+-------------------+-------------------+-------------------+-------+-----------------------+----------------------+-------------+
| starting | 0.000106 | 0.000000 | 0.000000 | NULL | NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | NULL | NULL |
NULL |
| checking permissions | 0.000006 | 0.000000 | 0.000000 | NULL |NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | check_access | sql_authorization.cc |1892 |
| Opening tables | 0.000037 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | open_tables | sql_base.cc |5526 |
| init | 0.000005 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | Sql_cmd_dml::execute | sql_select.cc |514 |
| System lock | 0.000009 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |
NULL | NULL | NULL | mysql_lock_tables | lock.cc |332 |
| optimizing | 0.000010 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | JOIN::optimize | sql_optimizer.cc |213 |
| statistics | 0.000064 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | JOIN::optimize | sql_optimizer.cc |422 |
| preparing | 0.000007 | 0.000000 | 0.000000 | NULL |NULL | NULL |NULL | NULL | NULL |NULL | NULL | NULL |JOIN::optimize|sql_optimizer.cc |496 |
| executing | 0.000002 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |
NULL | NULL | NULL | JOIN::exec | sql_executor.cc |210 |
| Sending data | 0.000038 | 0.000000 | 0.000000 | NULL |
NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | JOIN::exec | sql_executor.cc |285 |
| end | 0.000002 | 0.000000 | 0.000000 | NULL |NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | Sql_cmd_dml::execute | sql_select.cc |564 |
| query end | 0.000006 | 0.000000 | 0.000000 | NULL |NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | mysql_execute_command | sql_parse.cc |4310 |
| closing tables | 0.000004 | 0.000000 | 0.000000 | NULL |NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | mysql_execute_command | sql_parse.cc |4356 |
| freeing items | 0.000041 | 0.000000 | 0.000000 | NULL |NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | mysql_parse | sql_parse.cc |4968 |
| cleaning up | 0.000008 | 0.000000 | 0.000000 | NULL |NULL | NULL | NULL | NULL | NULL |NULL | NULL | NULL | dispatch_command |sql_parse.cc |1978 |
+----------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+---------------+-------------------+-------------------+-------------------+-------+-----------------------+----------------------+-------------+
15 rows in set, 1 warning (0.00 sec)
set profiling=0; //关闭分析
性能结果分析
SQL:show profile [type] for query 1 [limit m offsetn]
字段描述:
+----------------------+----------+----------+------------+
"Status": "query end", 状态
"Duration": "1.751142", 持续时间
"CPU_user": "0.008999", cpu用户
"CPU_system": "0.003999", cpu系统
"Context_voluntary": "98", 上下文主动切换
"Context_involuntary": "0", 上下文被动切换
"Block_ops_in": "8", 阻塞的输入操作
"Block_ops_out": "32", 阻塞的输出操作
"Messages_sent": "0", 消息发出
"Messages_received": "0", 消息接受
"Page_faults_major": "0", 主分页错误
"Page_faults_minor": "0", 次分页错误
"Swaps": "0", 交换次数
"Source_function": "mysql_execute_command", 源功能
"Source_file": "sql_parse.cc", 源文件
"Source_line": "4465" 源代码行
+----------------------+----------+----------+------------+
值解释:
+----------------------+----------+----------+------------+
starting:开始
checking permissions:检查权限
Opening tables:打开表
init : 初始化
System lock :系统锁
optimizing : 优化
statistics : 统计
preparing :准备
executing :执行
Sending data :发送数据
Sorting result :排序
end :结束
query end :查询 结束
closing tables : 关闭表 /去除TMP 表
freeing items : 释放物品
cleaning up :清理
+----------------------+----------+----------+------------+
最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。
要想理解联合索引的最左匹配原则,先来理解下索引的底层原理。索引的底层是一颗B+树,那么联合索引的底层也就是一颗B+树,只不过联合索引的B+树节点中存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。
举例:创建一个(a,b)的联合索引,那么它的索引树就是下图的样子。
可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。但是我们又可发现a在等值的情况下,b值又是按顺序排列的,但是这种顺序是相对的。这是因为MySQL创建联合索引的规则是首先会对联合索引的最左边第一个字段排序,在第一个字段的排序基础上,然后在对第二个字段进行排序。所以b=2这种查询条件没有办法利用索引。
示例:
表:
字段 | 类型 | 描述 |
---|---|---|
id | int(11) | 主键 |
name | varchar(10) | 名称 |
age | int(11) | 年龄 |
该表中对id列.name列.age列建立了一个联合索引 id_name_age_index,实际上相当于建立了三个索引(id)(id_name)(id_name_age)。
下面介绍下可能会使用到该索引的几种情况:
mysql> explain select * from staffs where name = 'shine' and sex = 1 and age = 20;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |40 | const,const,const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from staffs where age = 20 and name = 'shine' and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys |key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex |idx_name_age_sex |40 | const,const,const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from staffs where sex = 1 and age = 20 and name = 'shine';
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |40 | const,const,const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
通过观察上面的结果图可知,where后面的查询条件,不论是使用(name,age,sex)(age,name,sex)还是(sex,age,name)顺序,在查询时都使用到了联合索引,可能有同学会疑惑,为什么底下两个的搜索条件明明没有按照联合索引从左到右进行匹配,却也使用到了联合索引? 这是因为MySQL中有查询优化器explain,所以sql语句中字段的顺序不需要和联合索引定义的字段顺序相同,查询优化器会判断纠正这条SQL语句以什么样的顺序执行效率高,最后才能生成真正的执行计划,所以不论以何种顺序都可使用到联合索引
mysql> explain select * from staffs where name = 'shine';
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |33 | const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
该搜索是遵循最左匹配原则的,通过key字段也可知,在搜索过程中使用到了联合索引,且使用的是联合索引中的(id)索引
mysql> explain select * from staffs where name = 'shine' and age = 20;
+----+-------------+--------+------------+------+------------------+------------------
+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |38 | const,const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
由于name到age是从左边依次往右边匹配,这两个字段中的值都是有序的,所以也遵循最左匹配原则,通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(name_age)索引.
mysql> explain select * from staffs where name = 'shine' and age = 20 and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |40 | const,const,const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
由于上面三个搜索都是从最左边name依次向右开始匹配的,所以都用到了name_age_sex联合索引。
那如果不是依次匹配呢?
mysql> mysql> explain select * from staffs where name = 'shine' and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |33 | const | 1 | 20.00 | Using where; Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(name)索引,因为联合索引树是按照name字段创建的,但sex相对于name来说是无序的,只有name是有序的,所以他只能使用联合索引中的name索引。
mysql> explain select * from staffs where age = 20;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | index | NULL | idx_name_age_sex |
40 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
通过观察发现上面key字段发现在搜索中也使用了name_age_sex索引,可能许多同学就会疑惑它并没有遵守最左匹配原则,按道理会索引失效,为什么也使用到了联合索引?因为没有从id开始匹配,且name单独来说是无序的,所以它确实不遵循最左匹配原则,然而从type字段可知,它虽然使用了联合索引,但是它是对整个索引树进行了扫描,正好匹配到该索引,与最左匹配原则无关,一般只要是某联合索引的一部分,但又不遵循最左匹配原则时,都可能会采用index类型的方式扫描,但它的效率远不如最做匹配原则的查询效率高,index类型类型的扫描方式是从索引第一个字段一个一个的查找,直到找到符合的某个索引,与all不同的是,index是对所有索引树进行扫描,而all是对整个磁盘的数据进行全表扫描.
mysql> explain select * from staffs where sex = 1;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | index | NULL | idx_name_age_sex |
40 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from staffs where age = 20 and sex = 1;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | index | NULL | idx_name_age_sex |
40 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
这两个结果跟上面的是同样的道理,由于它们都没有从最左边开始匹配,所以没有用到联合索引,使用的都是index全索引扫描。
对于模糊匹配的查询,如果是前缀匹配用的是索引,中坠和后缀用的是全表扫描
mysql> explain select * from staffs where name like "shie%";
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | range | idx_name_age_sex | idx_name_age_sex | 33 | NULL | 1 | 100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from staffs where name like "%ara%";
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | index | NULL | idx_name_age_sex |
40 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from staffs where name like "%ara";
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | index | NULL | idx_name_age_sex |
40 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
一般我们在开发项目时对于一些业务难免会写出一些条件+分组/排序的sql语句,而通常也是这些sql会导致mysql的性能变差,这个时候我们想到使用mysql的索引来进行优化,但是mysql的索引对于条件,分组,排序都存在的情况下是如何去选择索引的呢?
下面我们根据上面的问题来进行一些测试以及分析。
条件与分组排序共存的情况下
mysql> explain select sex,age from staffs where name = "shine" group by sex order by age;
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex,idx_sex |idx_name_age_sex | 33 | const | 1 | 100.00 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
当sql中where条件,分组,排序同时存在时,MySQL的优化器会优先选择条件来确定使用的索引,因为where可以减少更多的sql扫描,而排序和分组往往进行的是全表扫描。
条件与排序共存
mysql> explain select sex,age from staffs where name = "shine" order by age;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_name_age_sex | idx_name_age_sex |33 | const | 1 | 100.00 | Using where; Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
原因:所有的排序都是在条件过滤之后才执行的,所以如果条件过滤了大部分数据的话,几百几千条数据进行排序其实并不是很消耗性能,即使索引优化了排序但实际提升性能很有限。
分组排序共存
mysql> explain select sex,age from staffs group by sex order by age;
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key |key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
| 1 | SIMPLE | staffs | NULL | index | idx_name_age_sex,idx_sex | idx_sex |2 | NULL | 5 | 100.00 | Using temporary; Using filesort |
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
1 row in set, 1 warning (0.00 sec)
对于分组和排序共存的情况下,mysql会优先根据分组去选择索引,那是因为sql需要先将要查询的数据进行分组,随后才会进行数据的排序。
注:字段一般是推荐重复比较少的字段影响到数据的检索,如果是项目需求(可建立联合索引)
下一篇:mysql锁机制 08
上一篇:mysql索引类型与数据存储 06
信息加载中,请等待
微信客服(速回)
微信客服(慢回)