文章出处:http://inter12.iteye.com/blog/1430144

MYSQL的全表扫描,主键索引(聚集索引、第一索引),非主键索引(非聚集索引、第二索引),覆盖索引四种不同查询的分析

1.前置条件:

本次是基于小数据量,且数据块在一个页中的最理想情况进行分析,可能无具体的实际意义,但是可以借鉴到各种复杂条件下,因为原理是相同的,知小见大,见微知著!

打开语句分析并确认是否已经打开

Java代码  
  1. mysql> set profiling=1;
  2. Query OK, 0 rows affected (0.00 sec)
  3. mysql> select @@profiling;
  4. +-------------+
  5. | @@profiling |
  6. +-------------+
  7. |           1 |
  8. +-------------+
  9. 1 row in set (0.01 sec)

2.数据准备:

2.1全表扫描数据

Java代码  
  1. create table person4all(id int not null  auto_increment, name varchar(30) not null, gender varchar(10) not null ,primary key(id));
  2. insert into person4all(name,gender) values("zhaoming","male");
  3. insert into person4all(name,gender) values("wenwen","female");

2.2根据主键查看数据

Java代码  
  1. create table person4pri(id int not null  auto_increment, name varchar(30) not null, gender varchar(10) not null ,primary key(id));
  2. insert into person4pri(name,gender) values("zhaoming","male");
  3. insert into person4pri(name,gender) values("wenwen","female");

2.3根据非聚集索引查数据

Java代码  
  1. create table person4index(id int not null  auto_increment, name varchar(30) not null, gender varchar(10) not null ,primary key(id) , index(gender));
  2. insert into person4index(name,gender) values("zhaoming","male");
  3. insert into person4index(name,gender) values("wenwen","female");

2.4根据覆盖索引查数据

Java代码  
  1. create table person4cindex(id int not null  auto_increment, name varchar(30) not null, gender varchar(10) not null ,primary key(id) , index(name,gender));
  2. insert into person4cindex(name,gender) values("zhaoming","male");
  3. insert into person4cindex(name,gender) values("wenwen","female");

主要从以下几个方面分析:查询消耗的时间,走的执行计划等方面。

3.开工测试:

第一步:全表扫描

Java代码  
  1. mysql> select * from person4all ;
  2. +----+----------+--------+
  3. | id | name     | gender |
  4. +----+----------+--------+
  5. |  1 | zhaoming | male   |
  6. |  2 | wenwen   | female |
  7. +----+----------+--------+
  8. 2 rows in set (0.00 sec)

查看其执行计划:

Java代码  
  1. mysql> explain select * from person4all;
  2. +----+-------------+------------+------+---------------+------+---------+------+------+-------+
  3. | id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra |
  4. +----+-------------+------------+------+---------------+------+---------+------+------+-------+
  5. |  1 | SIMPLE      | person4all | ALL  | NULL          | NULL | NULL    | NULL |    2 |       |
  6. +----+-------------+------------+------+---------------+------+---------+------+------+-------+
  7. 1 row in set (0.01 sec)

我们可以很清晰的看到走的是全表扫描,而没有走索引!

查询消耗的时间:

Java代码  
  1. mysql> show profiles;
  2. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------+
  3. | Query_ID | Duration   | Query                                                                                                                             |
  4. |       54 | 0.00177300 | select * from person4all                                                                                                          |
  5. |       55 | 0.00069200 | explain select * from person4all                                                                                                  |
  6. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------+

全表扫描总共话了0.0017730秒

各个阶段消耗的时间是:

Java代码  
  1. mysql> show profile for query 54;
  2. +--------------------------------+----------+
  3. | Status                         | Duration |
  4. +--------------------------------+----------+
  5. | starting                       | 0.000065 |
  6. | checking query cache for query | 0.000073 |
  7. | Opening tables                 | 0.000037 |
  8. | System lock                    | 0.000024 |
  9. | Table lock                     | 0.000053 |
  10. | init                           | 0.000044 |
  11. | optimizing                     | 0.000022 |
  12. | statistics                     | 0.000032 |
  13. | preparing                      | 0.000030 |
  14. | executing                      | 0.000020 |
  15. | Sending data                   | 0.001074 |
  16. | end                            | 0.000091 |
  17. | query end                      | 0.000020 |
  18. | freeing items                  | 0.000103 |
  19. | storing result in query cache  | 0.000046 |
  20. | logging slow query             | 0.000019 |
  21. | cleaning up                    | 0.000020 |
  22. +--------------------------------+----------+
  23. 17 rows in set (0.00 sec)

第一次不走缓存的话,需要检查是否存在缓存中,打开表,初始化等操作,最大的开销在于返回数据。

第二步:根据主键查询数据。

Java代码  
  1. mysql> select name ,gender from person4pri where id in (1,2);
  2. +----------+--------+
  3. | name     | gender |
  4. +----------+--------+
  5. | zhaoming | male   |
  6. | wenwen   | female |
  7. +----------+--------+
  8. 2 rows in set (0.01 sec)

查看其执行计划:

Java代码  
  1. mysql> explain select name ,gender from person4pri where id in (1,2);
  2. +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
  3. | id | select_type | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
  4. +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
  5. |  1 | SIMPLE      | person4pri | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where |
  6. +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
  7. 1 row in set (0.00 sec)

从执行计划中我们可以看出,走的是范围索引。

再看其执行消耗的时间:

Java代码  
  1. mysql> show profiles;
  2. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------+
  3. | Query_ID | Duration   | Query                                                                                                                             |
  4. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------+
  5. |       63 | 0.00135700 | select name ,gender from person4pri where id in (1,2)                                                                             |
  6. |       64 | 0.00079200 | explain select name ,gender from person4pri where id in (1,2)                                                                     |
  7. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------+
  8. 15 rows in set (0.01 sec)

这次查询消耗时间为0.00079200。

查看各个阶段消耗的时间:

Java代码  
  1. mysql> show profile for query 63;
  2. +--------------------------------+----------+
  3. | Status                         | Duration |
  4. +--------------------------------+----------+
  5. | starting                       | 0.000067 |
  6. | checking query cache for query | 0.000146 |
  7. | Opening tables                 | 0.000342 |
  8. | System lock                    | 0.000027 |
  9. | Table lock                     | 0.000115 |
  10. | init                           | 0.000056 |
  11. | optimizing                     | 0.000032 |
  12. | statistics                     | 0.000069 |
  13. | preparing                      | 0.000039 |
  14. | executing                      | 0.000022 |
  15. | Sending data                   | 0.000100 |
  16. | end                            | 0.000075 |
  17. | query end                      | 0.000022 |
  18. | freeing items                  | 0.000158 |
  19. | storing result in query cache  | 0.000045 |
  20. | logging slow query             | 0.000019 |
  21. | cleaning up                    | 0.000023 |
  22. +--------------------------------+----------+
  23. 17 rows in set (0.00 sec)

看出最大的消耗也是在Sending data,第一次也是需要一些初始化操作。

第三步:根据非聚集索引查询

Java代码  
  1. mysql> select name ,gender from person4index where gender in ("male","female");
  2. +----------+--------+
  3. | name     | gender |
  4. +----------+--------+
  5. | wenwen   | female |
  6. | zhaoming | male   |
  7. +----------+--------+
  8. 2 rows in set (0.00 sec)

查看器执行计划:

Java代码  
  1. mysql> explain select name ,gender from person4index where gender in ("male","female");
  2. +----+-------------+--------------+-------+---------------+--------+---------+------+------+-------------+
  3. | id | select_type | table        | type  | possible_keys | key    | key_len | ref  | rows | Extra       |
  4. +----+-------------+--------------+-------+---------------+--------+---------+------+------+-------------+
  5. |  1 | SIMPLE      | person4index | range | gender        | gender | 12      | NULL |    2 | Using where |
  6. +----+-------------+--------------+-------+---------------+--------+---------+------+------+-------------+
  7. 1 row in set (0.00 sec)

可以看出,走的也是范围索引。同主键查询,那么就看其消耗时间了

Java代码  
  1. mysql> show profiles;
  2. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+
  3. | Query_ID | Duration   | Query                                                                                                                                               |
  4. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+
  5. |       68 | 0.00106600 | select name ,gender from person4index where gender in ("male","female")                                                                             |
  6. |       69 | 0.00092500 | explain select name ,gender from person4index where gender in ("male","female")                                                                     |
  7. +----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+
  8. 15 rows in set (0.00 sec)

这个非主键索引消耗的时间为:0.00106600,可以看出略大于组件索引消耗的时间。

看其具体消耗的阶段:

Java代码  
  1. mysql> show profile for query 68 ;
  2. +--------------------------------+----------+
  3. | Status                         | Duration |
  4. +--------------------------------+----------+
  5. | starting                       | 0.000059 |
  6. | checking query cache for query | 0.000111 |
  7. | Opening tables                 | 0.000085 |
  8. | System lock                    | 0.000023 |
  9. | Table lock                     | 0.000067 |
  10. | init                           | 0.000183 |
  11. | optimizing                     | 0.000031 |
  12. | statistics                     | 0.000139 |
  13. | preparing                      | 0.000035 |
  14. | executing                      | 0.000020 |
  15. | Sending data                   | 0.000148 |
  16. | end                            | 0.000024 |
  17. | query end                      | 0.000019 |
  18. | freeing items                  | 0.000043 |
  19. | storing result in query cache  | 0.000042 |
  20. | logging slow query             | 0.000017 |
  21. | cleaning up                    | 0.000020 |
  22. +--------------------------------+----------+
  23. 17 rows in set (0.00 sec)

看几个关键词的点;init,statistics,Sending data 这几个关键点上的消耗向比较主键的查询要大很多,特别是Sending data。因为若是走的非聚集索引,那么就需要回表进行再进行一次查询,多消耗一次IO。

第四部:根据覆盖索引查询数据

Java代码  
  1. mysql> select gender ,name from person4cindex where gender in ("male","female");
  2. +--------+----------+
  3. | gender | name     |
  4. +--------+----------+
  5. | female | wenwen   |
  6. | male   | zhaoming |
  7. +--------+----------+
  8. 2 rows in set (0.01 sec)

这里需要注意的是,我的字段查询顺序变了,是gender,name而不在是前面的name,gender,这样是为了走覆盖索引。具体看效果吧

还是先看执行计划:

Java代码  
  1. mysql> explain select gender ,name from person4cindex where gender in ("male","female");
  2. +----+-------------+---------------+-------+---------------+------+---------+------+------+--------------------------+
  3. | id | select_type | table         | type  | possible_keys | key  | key_len | ref  | rows | Extra                    |
  4. +----+-------------+---------------+-------+---------------+------+---------+------+------+--------------------------+
  5. |  1 | SIMPLE      | person4cindex | index | NULL          | name | 44      | NULL |    2 | Using where; Using index |
  6. +----+-------------+---------------+-------+---------------+------+---------+------+------+--------------------------+
  7. 1 row in set (0.00 sec)

最后栏Extra中表示走的就是覆盖索引。

看消耗的时间吧:

Java代码  
  1. mysql> show profiles;
  2. +----------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  3. | Query_ID | Duration   | Query                                                                                                                                                            |
  4. +----------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  5. |       83 | 0.00115400 | select gender ,name from person4cindex where gender in ("male","female")                                                                                         |
  6. |       84 | 0.00074000 | explain select gender ,name from person4cindex where gender in ("male","female")                                                                                 |
  7. +----------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+

我们看到消耗的时间是0.00115400,看这个数字好像挺高的,那么都花在什么地方了呢?

看下具体的消耗情况:

Sql代码  
  1. mysql> show profile for query 83 ;
  2. +--------------------------------+----------+
  3. | Status                         | Duration |
  4. +--------------------------------+----------+
  5. | starting                       | 0.000083 |
  6. | checking query cache for query | 0.000113 |
  7. | Opening tables                 | 0.000039 |
  8. | System lock                    | 0.000026 |
  9. | Table lock                     | 0.000075 |
  10. | init                           | 0.000128 |
  11. | optimizing                     | 0.000193 |
  12. | statistics                     | 0.000056 |
  13. | preparing                      | 0.000038 |
  14. | executing                      | 0.000021 |
  15. | Sending data                   | 0.000121 |
  16. | end                            | 0.000042 |
  17. | query end                      | 0.000021 |
  18. | freeing items                  | 0.000112 |
  19. | storing result in query cache  | 0.000043 |
  20. | logging slow query             | 0.000021 |
  21. | cleaning up                    | 0.000022 |
  22. +--------------------------------+----------+
  23. 17 rows in set (0.00 sec)

很惊奇吧,在初始化和优化上消耗了这么多时间,取数据基恩差不多。

总结:

有了上面这些数据,那么我们整理下吧。未存在缓存下的数据。

看这个表,全表扫描最慢,我们可以理解,同时主键查询比覆盖所有扫描慢也还能接受,但是为什么主键扫描会比非主键扫描慢?而且非主键查询需要消耗的1次查询的io+一次回表的查询IO,理论上是要比主键扫描慢,而出来的数据缺不是如此。那么就仔细看下是个查询方式在各个主要阶段消耗的时间吧。

查询是否存在缓存,打开表及锁表这些操作时间是差不多,我们不会计入。具体还是看init,optimizing等环节消耗的时间。

1.从这个表中,我们看到非主键索引和覆盖索引在准备时间上需要开销很多的时间,预估这两种查询方式都需要进行回表操作,所以花在准备上更多时间。

2.第二项optimizing上,可以清晰知道,覆盖索引话在优化上大量的时间,这样在二级索引上就无需回表。

3. Sendingdata,全表扫描慢就慢在这一项上,因为是加载所有的数据页,所以花费在这块上时间较大,其他三者都差不多。

4. 非主键查询话在freeingitems上时间最少,那么可以看出它在读取数据块的时候最少。

5.相比较主键查询和非主键查询,非主键查询在Init,statistics都远高于主键查询,只是在freeingitems开销时间比主键查询少。因为这里测试数据比较少,但是我们可以预见在大数据量的查询上,不走缓存的话,那么主键查询的速度是要快于非主键查询的,本次数据不过是太小体现不出差距而已。

6.在大多数情况下,全表扫描还是要慢于索引扫描的。

tips:

过程中的辅助命令:

1.清楚缓存

reset query cache ;

flush tables;

2.查看表的索引:

show index from tablename;

MYSQL的全表扫描,主键索引(聚集索引、第一索引),非主键索引(非聚集索引、第二索引),覆盖索引四种不同查询的分析...相关推荐

  1. MYSQL避免全表扫描__如何查看sql查询是否用到索引(mysql)

    MYSQL避免全表扫描 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引 2.应尽量避免在 where 子句中对字段进行 null 值判断,否 ...

  2. MySQL的四种不同查询的分析

    1.前置条件: 本次是基于小数据量,且数据块在一个页中的最理想情况进行分析,可能无具体的实际意义,但是可以借鉴到各种复杂条件下,因为原理是相同的,知小见大,见微知著! 打开语句分析并确认是否已经打开 ...

  3. MySQL高级 全表扫描更快

    如果MySQL评估使用索引比全表更慢,则不使用索引.

  4. 我说 SELECT COUNT(*) 会造成全表扫描,面试官让我回去等通知

    来自:码海 前言 上篇 SQL 进阶技巧(下) 中提到使用以下 sql 会导致慢查询 SELECT COUNT(*) FROM SomeTable SELECT COUNT(1) FROM SomeT ...

  5. 哪些SQL语句会引起全表扫描

    大家都知道,用SQL语句对数据库进行操作时,如果引起全表扫描会对数据库的性能形成影响,下面简单介绍下SQL中哪些情况会引起全表扫描. 1.模糊查询效率很低: 原因:like本身效率就比较低,应该尽量避 ...

  6. oracle trim 性能,ORACLE sql调优之记录一次trim函数引发的大表全表扫描

    2017年8月14日,一地市oracle相关的调度程序ETL抽取速度奇慢,sql语句每次执行平均时间要9秒左右,如果所示: 该调度过程涉及的sql语句如下: select count(*) from ...

  7. mysql没加引号导致全表扫描_mysql隐蔽的索引规则导致数据全表扫描

    索引是为了加速数据的检索,但是不合理的表结构或适应不当则会起到反作用.我们在项目中就遇到过类似的问题,两个十万级别的数据表,在做连接查询的时候,查询时间达到了7000多秒还没有查出结果. 首先说明,关 ...

  8. mysql in 子查询无法使用索引全表扫描 慎用in

    背景 最近慢sql 性能优化 发现一个调用频率高的sql 使用 in 子查询,导致外层全表扫描的问题? 为什么会产生这样的问题?特别强调在优化的使用发现 使用in 和优化后的效果差距 300倍,可见全 ...

  9. 范围查找(比如日期范围)下查询出现全表扫描MySQL数据库索引失效

    范围查找(比如日期范围)下查询出现全表扫描MySQL数据库索引失效 当下MySQL数据库在多款数据库中脱颖而出,成为使用最广泛的数据库之一,这里我们来看看数据库索引上的一个问题.我们知道在数据量上去以 ...

最新文章

  1. maven <skip></skip>属性
  2. 为帐户创建一个Apex触发器,以根据自定义字段将送货地址邮政编码与账单地址邮政编码匹配
  3. vscode设置键盘快捷键
  4. TabLayout+ViewPager实现选项卡动态滑动效果
  5. 【渝粤教育】广东开放大学 动画原画设计 形成性考核 (22)
  6. 过年回家送什么?这10个教小朋友编程玩具最有科技感
  7. Java 面试题 —— java 源码
  8. iis7 下php 环境配置文件,IIS7.X配置PHP运行环境小结_php
  9. c语言中英文转换器在线转换器,汉英转换器
  10. 【WLAN】【软件】MTK芯片方案用户态和内核态通讯方式小结
  11. 如何利用树莓派制作路由器
  12. (转)旅游拍照必读:10个经典人像摄影案例@『国家地理』
  13. Hi3516DV300环境搭建
  14. ASM - 指令01 MOV
  15. Windows Azure Marketplace DataMarket概述
  16. 谈一下我是如何从使用json-lib到投入fastjson的怀抱....
  17. 【Class 46】【实例】python爬虫实现 自动搜索 并 打开浏览器
  18. python爬取链家网的房屋数据
  19. 成都Java开发工程师工资有多高
  20. Fabric 各种配置文件梳理(二)

热门文章

  1. 查询去重_【Freya的MySQL课堂】DQL基础查询
  2. MKL学习——矩阵矩阵操作
  3. 生成特定分布随机数的方法:Python seed() 函数numpy scikit-learn随机数据生成
  4. weka 初练之 文本分类
  5. ArcGIS.Server.9.2.DotNet自带例子分析(三、一)
  6. 移动IM开发那些事:技术选型和常见问题
  7. 操作系统原理之I/O设备管理(第六章上半部分)
  8. MyEclipse连接MySQL
  9. 做fzu oj 1045 做减法学到的sprintf()函数
  10. Qt 独立运行时伴随CMD命令窗口