Explain两个变种:
explain extended:rows*filtered/100 关联的行数,比较少用

  • show warnings;
  • image.png
  • 执行的语句可能呗mysql的优化,大体的优化结果。

explain partition:多了partition字段,分区会显示,很少用到
EXPLAIN EXTENDED SELECT * FROM film where id = 1;
image.png

字段:

id:

  • image.png
  • id指的是这条语句执行的先后顺序,id越大先执行,id越小后执行。
  • id可重复,谁排在前面谁先执行。


select_type:

  • simple:简单查询,查询不包含子查询和union
  • image.png
  • primary:复杂查询中最外层的select
  • subquery:包含在select中的子查询(不在from子句中,select关键字后边的)
  • derived:包含在from子句中的子查询,mysql会讲结果存放在一个临时表中,也称为派生表(在from关键字后边的查询)
  • image.png

table:

  • image.png
  • 为衍生表,真实的衍生变是film(DERIVED)查询的时候生成的临时表的数据。
  • table字段,一条sql语句中有几个查询,每个查询牵扯到那几张表,在table字段显示

partitions:

*type:sql的访问(关联)类型,查找数据行记录的大概范围

  • 包含属性:以下按最优到最差,一般达到range,最好ref system->const->eq_ref->ref->range->index->all

    null:执行的时候用不着再访问表或者索引

    • image.png

      system、const:

    • image.png

    • const:查询的结果利用主键,只有一条,相当于查询常量
    • system:查询表的结果集只有一条,表本身只有一条数据

      eq_ref:

    • image.png

    • 关联,至少两张表
    • 如果有表关联,用的是主键关联,就是eq_ref

      ref:

    • 1:image.png

    • 表关联查询的时候,没有用唯一键去查询,查询到的结果有可能是多条结果。可能使用普通索引(二级索引)或者唯一索引的部分前缀。
    • 2:image.png
    • 联合索引

      range:

    • image.png

    • 通常是范围查找,底层实际还是用到了索引, 在 in(), between ,> ,<, >= 等

      index:

    • image.png

    • image.png
    • film表就两个字段,一个主键ID,一个普通索引字段,没有查询条件,但是还是走的索引,
    • 示例扫描二级索引,查询到的结果,如果二级索引和主键索引都有,优先使用二级索引,因为二级索引更小,主键索引的叶子节点包含所有的数据。
    • 臊面全索引可以拿到结果,一般是扫描某个二级索引。
    • 虽然用到索引,但是效率不高,至少加一个条件。
    • 上面示例全索引扫描,如果加一个where条件扫描索引,会折半扫描,节省很多时间。

      ALL:

    • image.png

    • 聚簇索引,扫描所有ibd里面的所有的子节点,扫描的是主键索引。
    • index为什么比all效率高?因为主键索引表包含了所有的数据,二级索引只有定义索引数据。

      possible_keys:放索引

  • explain 时可能出现 possible_keys 有列,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql认为索引 对此查询帮助不大,选择了全表查询。 如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提 高查询性能,

    key:sql执行的时候,真正用到的索引

  • 如果没有使用索引,则该列是 NULL。如果想强制mysql使用或忽视possible_keys列中的索引,在查询中使用force index、ignore index

    key_len:

    • image.png
    • mysql在索引里面用到的字节数,例如int就是4字节
  • key_len计算规则如下:

    • 字符串char(n)和varchar(n),5.0.3以后版本中,n均代表字符数,而不是字节数,如果是utf-8,一个数字 或字母占1个字节,一个汉字占3个字节
      • char(n):如果存汉字长度就是 3n 字节
      • varchar(n):如果存汉字则长度是 3n + 2 字节,加的2字节用来存储字符串长度,因为 varchar是变长字符串
    • 数值类型
      • tinyint:1字节
      • smallint:2字节
      • int:4字节
      • bigint:8字节
    • 时间类型
      • date:3字节
      • timestamp:4字节
      • datetime:8字节
    • 如果字段允许为 NULL,需要1字节记录是否为 NULL
    • 索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索 引。

      ref:

      这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名(例:film.id)

      rows:

      这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。

      filtered:

      Extra:

      1:Using index

  • 覆盖索引:不是真正的索引,只是一种查询方式,查询的结果字段在索引树里面全包含了。类似于type字段里面的index查询,就是索引树里面用到了所有的索引进行查询。不需要回表。

  • image.png

    2: Using where

  • 使用 where 语句来处理结果,并且查询的列未被索引覆盖

  • image.png

    3: Using index condition

  • 查询的列不完全被索引覆盖,where条件中是一个前导列的范围;

  • image.png

    4: Using temporary

  • mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索 引来优化。

  • 1:distinct会去重,用到临时表,再从临时表去重。查询的字段没有索引
  • image.png
  • 2:对上一种的优化,extra的值为Using index,因为查询的字段是索引字段,索引字段是排好序的,扫描索引树的时候就做到了去重。用到了覆盖索引,查询的字段用到了索引
  • image.png

    5:Using filesort

  • 1:没有用到索引,没有索引,需要把结果集拿出来放到内存中,进行排序

  • image.png
  • 2:用到了索引,就用到了索引树,order by的时候,索引树已经排好序了。
  • image.png

    6: Select tables optimized away

  • image.png

  • :使用某些聚合函数(比如 max、min)来访问存在索引的某个字段是。可以直接从索引树中找到最大最小的值

索引最佳实践

  1. CREATE TABLE `employees` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  4. `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  5. `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  6. `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  7. PRIMARY KEY (`id`),
  8. KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE #联合索引
  9. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='员工记录表';
  10. INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
  11. INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei',23,'dev',NOW());
  12. INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());

全值匹配

下面三个语句都会走联合索引,只是用到的个数不相同,都遵循最左前缀原则。 如果顺序换了,依旧会走索引,因为mysql内部做了优化。

  • 1:只用74字节,name字段 3*24 + 2
  • image.png
  • 2:78字节 3*24+2+4
  • image.png
  • 3:
  • image.png

    最左前缀

    索引最左前缀原理
    建立索引的先后顺序进行创建,如果第一个字段就已经排好序,区分出要查找的值,就不会查找后面的字段了。如果第一个字段都相等,就对比第二个字段,一次类推
    联合主键(模拟三个字段:name,age,position)三个字段不可能相等的
    要用的时候一定要按照建索引的顺序去用,不能跳过第一个直接用后面的,顺序从左开

  • 1:

  • image.png
  • 2:
  • image.png
  • 3:
  • image.png

    不在索隐裂上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效转向全表臊面

  • 第一条语句走索引,第二条没有走索引,因为第二天去了name的前三个值,但是索引树里面没有这个索引字段

    • image.png
    • image.png
  • 下面的语句也不会走索引,计算后的结果去索引树里面找不到,所以不走索引
    • image.png
  • 下面的语句走索引了,因为索引树里面可以匹配到,索引树是排好序的,
    • image.png
  • 还原最初索引 | ALTER TABLE employees DROP INDEX idx_hire_time; | | —- |

存储引擎不能使用索引中范围条件右边的列

  • 第一个走所有的索引,能用到索引树里面所有的字段
  • image.png
  • 第二个只走前两个索引,第一个字段有序的情况下,第二个会根据第一个匹配,第二个是范围查找的话,第三个字段肯定是无序的
  • image.png

    尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少select*语句

  • 查询的时候尽量指明查询的字段,字段尽量都在联合索引中

  • image.png
  • 尽量避免全字段查询
  • image.png

    mysql在使用不等于(!= 或者<>),not in,notexists的时候无法使用索引会导致全表扫描 <小于、>大于、<=/>=这些,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引

image.png

is null,is not null一般情况下也无法使用索引

null字段可能会集中放在一起,跟其他节点做一个双向指针
image.png

like以通配符开头(’$abc…’)mysql索引失效会变成全表扫描操作

百分号在前面,可能查询的关键字前面还有其它的字符,跳过了这些字符串,索引树里面就不是有序的
image.png
用到了name的前缀
image.png
left需要进行计算,
问题:解决like’%字符串%’索引不被使用的方法?
a)使用覆盖索引,查询字段必须是建立覆盖索引字段 ,type优化到index
image.png
b)如果不能使用覆盖索引则可能需要借助搜索引擎

字符串不加单引号索引失效

  • 第一个语句使用了单引号
  • image.png
  • 第二个语句没用单引号,后台可能会帮忙转型。
  • image.png

    少用in或or,用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例,表大小等多个因素整体评估是否使用索引,详见范围查询优化

    or或in有可能会走索引,有可能不会走索引,尽量少好用
    image.png

    范围查询优化

    有可能数据量太多了,全表扫描会更快,范围缩小可能会走索引
    image.png
    优化:进行拆段后,可能会走索引
    image.png
    image.png
    还原字段
ALTER TABLE employees DROP INDEX idx_age;

索引总结:index(a,b,c)

where语句 索引是否被使用
where a = 3 Y,使用到a
where a = 3 and b = 5 Y,使用到a,b
where a = 3 and b = 5 and c = 4 Y,使用到a,b,c
where a = 3 或者 where b = 3 and c = 4 或者where c = 4 N
where a = 3 and c = 5 使用到a,但是c不可以,b中间断了
where a = 3 and b > 4 and c = 5 使用到a和b,c不能用在范围之后,b断了
where a = 3 and b like ‘kk%’ and c = 4 Y,使用到a,b,c
where a = 3 and b like ‘%kk’ and c = 4 Y,使用到a
where a = 3 and b like ‘%kk%’ and c = 4 Y,使用到a
where a = 3 and b like ‘k%kk%’ and c = 4 Y,使用到a,b,c

like KK%相当于=常量,%KK和%KK% 相当于范围

本次知识点所有sql

  1. DROP TABLE IF EXISTS `actor`;
  2. CREATE TABLE `actor` (
  3. `id` int(11) NOT NULL,
  4. `name` varchar(45) DEFAULT NULL,
  5. `update_time` datetime DEFAULT NULL,
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  8. INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (1,'a','20171222
  9. :27:18'), (2,'b','20171222 15:27:18'), (3,'c','20171222 15:27:18');
  10. DROP TABLE IF EXISTS `film`;
  11. CREATE TABLE `film` (
  12. `id` int(11) NOT NULL AUTO_INCREMENT,
  13. `name` varchar(10) DEFAULT NULL,
  14. PRIMARY KEY (`id`),
  15. KEY `idx_name` (`name`)
  16. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  17. INSERT INTO `film` (`id`, `name`) VALUES (3,'film0'),(1,'film1'),(2,'film2');
  18. DROP TABLE IF EXISTS `film_actor`;
  19. CREATE TABLE `film_actor` (
  20. `id` int(11) NOT NULL,
  21. `film_id` int(11) NOT NULL,
  22. `actor_id` int(11) NOT NULL,
  23. `remark` varchar(255) DEFAULT NULL,
  24. PRIMARY KEY (`id`),
  25. KEY `idx_film_actor_id` (`film_id`,`actor_id`)
  26. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  27. INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1),(2,1,2),(3,2,1);
  28. EXPLAIN EXTENDED SELECT * FROM film where id = 1;
  29. show warnings;
  30. explain select * from film where id = 2;
  31. set session optimizer_switch='derived_merge=off';#关闭衍生表的合并优化
  32. explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;
  33. explain select min(id) from film;
  34. explain extended select * from (select * from film where id = 1) tmp;
  35. show warnings;
  36. explain select * from film_actor left join film on film_actor.film_id = film.id
  37. explain select * from film where name = 'film1';
  38. explain select film_id from film left join film_actor on film.id = film_actor.film_id;
  39. explain select * from actor where id > 1;
  40. explain select * from film;
  41. explain select * from actor;
  42. explain select * from film_actor where film_id = 2;
  43. explain select film_id from film_actor where film_id = 1;
  44. explain select * from actor where name = 'a';
  45. explain select * from film_actor where film_id > 1;
  46. explain select distinct name from actor;
  47. explain select distinct name from film;
  48. explain select * from actor order by name;
  49. explain select * from film order by name;
  50. explain select min(id) from film;
  51. CREATE TABLE `employees` (
  52. `id` int(11) NOT NULL AUTO_INCREMENT,
  53. `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  54. `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  55. `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  56. `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  57. PRIMARY KEY (`id`),
  58. KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
  59. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='员工记录表';
  60. INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
  61. INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei',23,'dev',NOW());
  62. INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());
  63. EXPLAIN SELECT * FROM employees WHERE name= 'LiLei';
  64. EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22;
  65. EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager';
  66. EXPLAIN SELECT * FROM employees WHERE name = 'Bill' and age = 31;
  67. EXPLAIN SELECT * FROM employees WHERE age = 30 AND position = 'dev';
  68. EXPLAIN SELECT * FROM employees WHERE position = 'manager';
  69. EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';
  70. EXPLAIN SELECT * FROM employees WHERE left(name,3) = 'LiLei';
  71. ALTER TABLE `employees` ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ;
  72. EXPLAIN select * from employees where date(hire_time) ='2018‐09‐30';
  73. EXPLAIN select * from employees where hire_time >='2018‐09‐30 00:00:00' and hire_time <='2018‐09‐30 23:59:59';
  74. EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager';
  75. EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age > 22 AND position ='manager';
  76. EXPLAIN SELECT name,age FROM employees WHERE name= 'LiLei' AND age = 23 AND position='manager';
  77. EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 23 AND position ='manager';
  78. EXPLAIN SELECT * FROM employees WHERE name != 'LiLei';
  79. EXPLAIN SELECT * FROM employees WHERE name is null
  80. EXPLAIN SELECT * FROM employees WHERE name like '%Lei'
  81. EXPLAIN SELECT * FROM employees WHERE name like 'Lei%'
  82. EXPLAIN SELECT * FROM employees WHERE name = '1000';
  83. EXPLAIN SELECT * FROM employees WHERE name = 1000;
  84. EXPLAIN SELECT name,age,position FROM employees WHERE name like '%Lei%';
  85. EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' or name = 'HanMeimei';
  86. ALTER TABLE `employees` ADD INDEX `idx_age` (`age`) USING BTREE ;
  87. explain select * from employees where age >=1 and age <=2000;
  88. explain select * from employees where age >=1 and age <=1000;
  89. explain select * from employees where age >=1001 and age <=2000;
  90. ALTER TABLE `employees` DROP INDEX `idx_age`;