文章目录

  • 前言
  • 一、索引设计原则
  • 二、索引的分类
    • 1.主键索引
    • 2.联合索引
  • 三、EXPLAIN
    • 1.id
    • 2.select_type
    • 3.type
    • 4.key_len
    • 5.Extra
    • 6.覆盖索引与回表查询
  • 总结

前言

本文主要介绍设计索引的原则及如何使用EXPLAIN对SQL进行分析


一、索引设计原则

索引的设计可以遵守一些原则,创建索引时可以尽可能考虑这些原则,便于提升索引的使用效率。

  • 最适合索引的列是出现在WHERE子句中的列,或链接子句中的列,而不是出现在SELECT关键字后选择类表中的列。
  • 使用唯一索引。区分度越高,使用索引的效率越高
  • 使用短索引;例如有一个CHAR(200)列,如果前10或者20个字符内,多数值是唯一的,那么就不要对整个列进行索引。对前10个或者20个字符进行索引能够节省大量索引空间。对于较短的键值,索引高速缓存中的块能容纳更多的键值,减少IO。
  • 利用最左前缀。在创建一个n列的索引时实际上市创建了MySQL可利用的n个索引。多列索引可起到几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀。
  • 索引可以有效的提升查询数据的效率,但索引数量不是多多益善,索引越多,维护索引的代价也高。对于插入、更新、删除等DML操作比较频繁的表来说,索引过多,会引入相当高的维护代价,降低DML操作的效率,增加相应操作的时间消耗。

二、索引的分类

1.主键索引

在InnoDB存储引擎中,每张表都会有主键。数据按照主键顺序组织存放,如果表定义时没有显示定义主键,则会按照以下方式选择或创建主键:

  • 先判断表中是否有“非空的唯一索引”,如果有

    • 如果仅有一条”非空唯一索引“,则该索引为主键
    • 如果有多条“非空唯一索引”则根据索引的先后顺序,选择第一个定义的非空唯一索引为主键
  • 如果表中无“非空唯一索引”,则自动创建一个6字节大小的指针作为主键。但是该主键不能被查询的
--创建表
CREATE TABLE `tb_test1` (
`id` int(11) NOT NULL, -- 非空
`name` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
UNIQUE KEY `id` (`id`) USING BTREE -- 唯一索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `tb_test1` (`id`, `name`, `age`) VALUES ('1', 'zhangsan', '20');
INSERT INTO `tb_test1` (`id`, `name`, `age`) VALUES ('2', 'lisi', '21');
--查询, _rowid就是视为主键,如果表⾥设置了主键之后, _rowid就是对应主键。
mysql> SELECT *,_rowid FROM tb_test1;
--查询结果,可以看到_rowid的值与id值相同
+----+----------+------+--------+
| id | name     | age  | _rowid |
+----+----------+------+--------+
|  1 | zhangsan |   20 |      1 |
|  2 | lisi     |   21 |      2 |
+----+----------+------+--------+

2.联合索引

联合索引就是将表中的多个列一起进行索引,需要注意的是,联合索引是有顺序的,比如:A、B、C列的索引与A、C、B列的索引是不一样的。

CREATE TABLE `tb_contact` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`index_code` varchar(1) DEFAULT NULL COMMENT '索引编号',
`surname` varchar(5) DEFAULT NULL COMMENT '姓',
`name` varchar(10) DEFAULT NULL COMMENT '名',
`mobile_code` varchar(11) DEFAULT NULL COMMENT '手机号',
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_code` (`index_code`,`surname`,`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('1', 'Z', 'zhang', 'san', '13911111111', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('2', 'L', 'li', 'si', '13922222222', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('3', 'W', 'wang', 'wu', '13933333333', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('4', 'Z', 'zhao', 'liu', '13944444444', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('5', 'L', 'liu', 'hulan', '13955555555', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('6', 'L', 'lei', 'jun', '13966666666', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('7', 'M', 'ma', 'yun', '13977777777', now(), now());
INSERT INTO `tb_contact` (`id`, `index_code`, `surname`, `name`, `mobile_code`,`created`, `updated`) VALUES ('8', 'Q', 'qian', 'laoda', '13988888888', now(), now());mysql> show index from tb_contact;
+------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table      | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tb_contact |          0 | PRIMARY    |            1 | id          | A         |           8 |     NULL | NULL   |      | BTREE      |         |               |
| tb_contact |          1 | index_code |            1 | index_code  | A         |           8 |     NULL | NULL   | YES  | BTREE      |         |               |
| tb_contact |          1 | index_code |            2 | surname     | A         |           8 |     NULL | NULL   | YES  | BTREE      |         |               |
| tb_contact |          1 | index_code |            3 | name        | A         |           8 |     NULL | NULL   | YES  | BTREE      |         |               |
+------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

底层数据结构
联合索引的底层结构也是基于B+TREE,结构如下

InnoDB会使用主键索引在B+TREE维护索引和数据文件,然后我们创建一个联合索引(index_code,surname,name)也会生成一个索引树,同一是B+TREE结构,它的data部分存储的是联合索引所在行的的主键值。

联合索引比单值索引多了几列,而这些索引列全部出现在索引树上。对于联合索引,存储引擎会首先根据第一个索引列排序,如果第一列相对再根据第二列排序,以此类推构成索引树。

查找过程

select * from tb_contact where index_code='M' AND surname='ma' and name='yun';

首先从根节点开始查找,根节点一般是常驻内存中的,第一个列为index_code,其值为: M,在L与W之间,会继续向子节点查询:

在查找到字节点后,将子节点数据从磁盘中价值到内存中,找到对应的数据 M ma yun。再继续查找子节点,读取到子节点中的data数据,其数据就是这条记录的主键。然后再根据主键索引查找数据,最终在主键索引中查询到数据。

三、EXPLAIN

使用EXPLAIN可以查看SQL的执行计划,从而可以知道SQL的瓶颈在哪里,就可以有针对性地进行优化。

mysql>  EXPLAIN SELECT * FROM tb_contact WHERE index_code='M'\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: ref
possible_keys: index_codekey: index_codekey_len: 6ref: constrows: 1filtered: 100.00Extra: NULL
1 row in set, 1 warning (0.01 sec)
字段 描述
id select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。
select_type 表示 SELECT 的类型,常见的取值有 SIMPLE、 PRIMARY、UNION、SUBQUERY等
table 输出结果集的表
type 表示关联类型或访问类型,即MySQL决定如何查找表中的⾏,查找数据⾏记录的⼤概范围。依次从最优到最差分别为: system > const > eq_ref > ref > range > index > ALL
possible_keys 查询可能使用哪些索引来查找。
key 表示实际用到的索引如果为null,则没有使用
key_len 索引字段的长度
ref 显示了在key列记录索引中, 表查找值所用到的列或常量
rows 扫描行的数量 (不是结果集里的行数)
filtered 返回结果的行占需要读到的行(rows列的值)的百分比
Extra 展示额外的信息。常见有:Distinct,Not exists,Using filesort,Using index,Using where等

下面对每列具体说明
数据准备:

CREATE TABLE `t_role` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) DEFAULT NULL,
`role_code` varchar(32) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_role_name` (`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `t_user` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` varchar(96) NOT NULL,
`name` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_user_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `user_role` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int DEFAULT NULL,
`role_id` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_ur_user_id` (`user_id`),
KEY `fk_ur_role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into `t_user` (`id`, `username`, `password`, `name`)values(null,'super','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','超级管理员');
insert into `t_user` (`id`, `username`, `password`, `name`)values(null,'admin','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','系统管理员');
insert into `t_user` (`id`, `username`, `password`, `name`)values(null,'itcast','$2a$10$8qmaHgUFUAmPR5pOuWhYWOr291WJYjHelUlYn07k5ELF8ZCrW0Cui','test02');
insert into `t_user` (`id`, `username`, `password`, `name`)values(null,'stu1','$2a$10$pLtt2KDAFpwTWLjNsmTEi.oU1yOZyIn9XkziK/y/spH5rftCpUMZa','学生1');
insert into `t_user` (`id`, `username`, `password`, `name`)values(null,'stu2','$2a$10$nxPKkYSez7uz2YQYUnwhR.z57km3yqKn3Hr/p1FR6ZKgc18u.Tvqm','学生2');
insert into `t_user` (`id`, `username`, `password`, `name`)values(null,'t1','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','老师1');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES(null,'学生','student','学生');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES(null,'老师','teacher','老师');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES(null,'教学管理员','teachmanager','教学管理员');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES(null,'管理员','admin','管理员');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES(null,'超级管理员','super','超级管理员');INSERT INTO user_role(id,user_id,role_id) VALUES(NULL, '1', '5'),(NULL, '1', '7'),(NULL, '2', '8'),(NULL, '3', '9'),(NULL, '4', '8'),(NULL, '5', '10') ;

1.id

id列的编号是select的序列号,有几个select就有几个id,并且id的顺序是按select出现的顺序增长的。其值越大执行优先级越高,id相同则由上往下执行,id为null最后执行。

mysql> EXPLAIN SELECT * FROM t_role r,t_user u,user_role ur WHERE r.id=ur.role_id AND u.id=ur.user_id;
+----+-------------+-------+------------+--------+-----------------------------+---------------+---------+---------------------+------+----------+------------------------------------+
| id | select_type | table | partitions | type   | possible_keys               | key           | key_len | ref                 | rows | filtered | Extra                              |
+----+-------------+-------+------------+--------+-----------------------------+---------------+---------+---------------------+------+----------+------------------------------------+
|  1 | SIMPLE      | r     | NULL       | ALL    | PRIMARY                     | NULL          | NULL    | NULL                |    5 |   100.00 | NULL                               |
|  1 | SIMPLE      | ur    | NULL       | ref    | fk_ur_user_id,fk_ur_role_id | fk_ur_role_id | 5       | order_db.r.id       |    1 |   100.00 | Using index condition; Using where |
|  1 | SIMPLE      | u     | NULL       | eq_ref | PRIMARY                     | PRIMARY       | 4       | order_db.ur.user_id |    1 |   100.00 | Using where                        |
+----+-------------+-------+------------+--------+-----------------------------+---------------+---------+---------------------+------+----------+------------------------------------+mysql> EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id =(SELECT id FROM t_user WHERE username='stu1'));
+----+-------------+-----------+------------+-------+----------------------+----------------------+---------+-------+------+----------+--------------------------------+
| id | select_type | table     | partitions | type  | possible_keys        | key                  | key_len | ref   | rows | filtered | Extra                          |
+----+-------------+-----------+------------+-------+----------------------+----------------------+---------+-------+------+----------+--------------------------------+
|  1 | PRIMARY     | NULL      | NULL       | NULL  | NULL                 | NULL                 | NULL    | NULL  | NULL |     NULL | no matching row in const table |
|  2 | SUBQUERY    | user_role | NULL       | ref   | fk_ur_user_id        | fk_ur_user_id        | 5       | const |    1 |   100.00 | Using where                    |
|  3 | SUBQUERY    | t_user    | NULL       | const | unique_user_username | unique_user_username | 98      | const |    1 |   100.00 | Using index                    |
+----+-------------+-----------+------------+-------+----------------------+----------------------+---------+-------+------+----------+--------------------------------+

2.select_type

表示SELECT语句的类型,有如下几种类型

select_type 描述
SIMPLE 简单select查询,查询中不包含子查询或UNION
PRIMARY 查询中若包含任何子查询,最外层查询标记为该标识
SUBQUERY 在SELECT 或 WHERE 列表中包含了子查询
DERIVED 在from列表中包含的子查询,被标记为DERIVED(衍生) MySQL会递归执行这些子查询,把结果放到临时表中
UNION 若第二个SELECT出现在UNION之后,则标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED
UNION RESULT 从UNION表获取结果的SELECT
mysql> EXPLAIN SELECT id FROM t_user WHERE username='stu1';
+----+-------------+--------+------------+-------+----------------------+----------------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys        | key                  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+-------+----------------------+----------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t_user | NULL       | const | unique_user_username | unique_user_username | 98      | const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+-------+----------------------+----------------------+---------+-------+------+----------+-------------+mysql> EXPLAIN SELECT role_id FROM user_role WHERE user_id =(SELECT id FROM t_user WHERE username='stu1');
+----+-------------+-----------+------------+-------+----------------------+----------------------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys        | key                  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+----------------------+----------------------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | user_role | NULL       | ref   | fk_ur_user_id        | fk_ur_user_id        | 5       | const |    1 |   100.00 | Using where |
|  2 | SUBQUERY    | t_user    | NULL       | const | unique_user_username | unique_user_username | 98      | const |    1 |   100.00 | Using index |
+----+-------------+-----------+------------+-------+----------------------+----------------------+---------+-------+------+----------+-------------+

3.type

type 显示的是访问类型,是较为重要的一个指标,可取值为:

type 描述
NULL MySQL不访问任何表、索引,直接返回结果
system 表只有一行记录,这是cost类型的特例,一般不会出现
const 数据表最多只有一个匹配行,因为只匹配一行数据,所以很快;常出现于根据primary key或者unique索引查询
eq_ref primary key 或unique key索引的所有部分被连接使用,最多只会返回一条符合条件的记录
ref 相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行
range 只检索给定返回的行,使用一个索引来选择行。where之后出现between,<,>,in等操作
index index与ALL的区别是index类型只遍历了索引树,通常比ALL快,ALL是遍历数据文件
ALL 将遍历全表以找到匹配的行

结果从最好到最坏:
system > const > eq_ref > ref > range > index > ALL
一般来说, 我们需要保证查询至少达到 range 级别, 最好达到ref 。

mysql> EXPLAIN SELECT * FROM t_user WHERE id=1;
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_user | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+mysql> EXPLAIN SELECT * FROM t_user u,user_role ur WHERE u.id=ur.user_id;
+----+-------------+-------+------------+------+---------------+---------------+---------+---------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key           | key_len | ref           | rows | filtered | Extra                 |
+----+-------------+-------+------------+------+---------------+---------------+---------+---------------+------+----------+-----------------------+
|  1 | SIMPLE      | u     | NULL       | ALL  | PRIMARY       | NULL          | NULL    | NULL          |    6 |   100.00 | NULL                  |
|  1 | SIMPLE      | ur    | NULL       | ref  | fk_ur_user_id | fk_ur_user_id | 5       | order_db.u.id |    1 |   100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+---------------+---------+---------------+------+----------+-----------------------+mysql> EXPLAIN SELECT * FROM t_user WHERE id IN (1,4,5);
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_user | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    3 |   100.00 | Using where |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+

4.key_len

这一列显示了MySQL在所有中使用的字节数。通过这个值,可以算出具体使用了索引中的哪些列
key_len的计算规则如下:

  • 字符串

    • char(n): n字节⻓度
    • varchar(n): 2字节存储字符串⻓度,如果是utf-8,则长度度 3n + 2
  • 数值类型
    • tinyint:1字节
    • smallint:2字节
    • int:4字节
    • bigint:8字节
  • 时间类型
    • date:3字节
    • timestamp:4字节
    • datetime:8字节
  • 如果字段允许为null,需要1字节记录是否为null
mysql> SHOW CREATE TABLE tb_contact\G;
*************************** 1. row ***************************Table: tb_contact
Create Table: CREATE TABLE `tb_contact` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`index_code` varchar(1) DEFAULT NULL COMMENT '索引编号',`surname` varchar(5) DEFAULT NULL COMMENT '姓',`name` varchar(10) DEFAULT NULL COMMENT '名',`mobile_code` varchar(11) DEFAULT NULL COMMENT '手机号',`created` datetime DEFAULT NULL,`updated` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `index_code` (`index_code`,`surname`,`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM tb_contact WHERE index_code = 'Z' AND surname='zhang'\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: ref
possible_keys: index_codekey: index_codekey_len: 24ref: const,constrows: 1filtered: 100.00Extra: NULL
1 row in set, 1 warning (0.01 sec)

上面的key_len计算:(31+2)+(35+2)+1+1=24
索引最大长度是768字节,当字符串过长时, mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

5.Extra

Distinct
发现第一个匹配行后,停止为当前的行组合搜索更多的行
Not exists
MySQL能够对查询进行LEFT JOIN优化,发现1个匹配LEFT JOIN标准的行为后,不再为前面的行组合在该表内检查更多的行。
Using filesort
MySQL会对结果使用一个外部索引排序,而不是按照索引次序从表里读取行。此时MySQL会根据联结类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况一般也是要考虑使用索引来优化的

mysql>  EXPLAIN SELECT id,name FROM tb_contact WHERE index_code='Z' ORDER BY mobile_code\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: ref
possible_keys: index_codekey: index_codekey_len: 6ref: constrows: 2filtered: 100.00Extra: Using index condition; Using filesort
1 row in set, 1 warning (0.01 sec)mysql> EXPLAIN SELECT id,name FROM tb_contact WHERE index_code='Z' ORDER BY surname\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: ref
possible_keys: index_codekey: index_codekey_len: 6ref: constrows: 2filtered: 100.00Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

Using index
从只使用索引树中的信息而不需要进一步搜索读取实际的行来检索表中的列信息

-- 这里要查询的id和name都是索引
mysql> EXPLAIN SELECT id,name FROM tb_contact WHERE index_code='Z'\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: ref
possible_keys: index_codekey: index_codekey_len: 6ref: constrows: 2filtered: 100.00Extra: Using index
1 row in set, 1 warning (0.00 sec)

Using temporary
MySQL需要创建一张临时表来处理查询,出现这种情况一般要进行优化。常见于 order by 和group by。

mysql> EXPLAIN SELECT COUNT(*) FROM tb_contact GROUP BY name\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: index
possible_keys: index_codekey: index_codekey_len: 57ref: NULLrows: 8filtered: 100.00Extra: Using index; Using temporary; Using filesort
1 row in set, 1 warning (0.00 sec)

Using where
MySQL服务器将在存储引擎检索行之后再进行过滤。先读取整行数据,再按WHERE条件进行检查,符合则留下,否则丢弃。

mysql> EXPLAIN SELECT * FROM tb_contact WHERE name='test02'\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: tb_contactpartitions: NULLtype: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 8filtered: 12.50Extra: Using where
1 row in set, 1 warning (0.00 sec)

NULL
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引
Using index condition
与Using where类似,查询的列不完全被索引覆盖, where条件中是一个前导列的范围;

小结

  • using index :使用覆盖索引的时候就会出现
  • using where:在查找使用索引的情况下,需要回表去查询所需的数据
  • using index condition:查找使用了索引,但是需要回表查询数据
  • using index ; using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

6.覆盖索引与回表查询

回表查询:先定位主键值,再定位行记录,它的性能较扫一遍索引树低
覆盖索引:只需要在一颗索引树上就能获取SQL所需要的所有列数据,无需回表

mysql> SHOW CREATE TABLE tb_contact\G;
*************************** 1. row ***************************Table: tb_contact
Create Table: CREATE TABLE `tb_contact` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`index_code` varchar(1) DEFAULT NULL COMMENT '索引编号',`surname` varchar(5) DEFAULT NULL COMMENT '姓',`name` varchar(10) DEFAULT NULL COMMENT '名',`mobile_code` varchar(11) DEFAULT NULL COMMENT '手机号',`created` datetime DEFAULT NULL,`updated` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `index_code` (`index_code`,`surname`,`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)-- 覆盖索引
mysql> EXPLAIN SELECT id,name FROM tb_contact WHERE index_code='L';
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | tb_contact | NULL       | ref  | index_code    | index_code | 6       | const |    3 |   100.00 | Using index |
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+-------------+-- 回表查询
mysql> EXPLAIN SELECT * FROM tb_contact WHERE index_code='L';
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | tb_contact | NULL       | ref  | index_code    | index_code | 6       | const |    3 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+------------+---------+-------+------+----------+-------+

总结

本篇主要是用EXPLAIN如何分析SQL,在此基础上。下一篇介绍如何避免SQL的索引失效。

MySQL索引设计与EXPLAIN相关推荐

  1. MySQL 索引分析除了 EXPLAIN 还有什么方法?

    作者 | adrninistrat0r 责编 | 夕颜 出品 | CSDN(ID:CSDNnews) 前言 对于非数据库开发人员而言,难以对MySQL源码进行分析或调试,接近一个黑盒,但MySQL提供 ...

  2. MySQL 索引设计

    原文链接:MySQL 索引设计概要 - 面向信仰编程 在关系型数据库中设计索引其实并不是复杂的事情,很多开发者都觉得设计索引能够提升数据库的性能,相关的知识一定非常复杂. 然而这种想法是不正确的,索引 ...

  3. 深入理解MySQL索引设计和优化原则

    索引类型 探讨索引设计和优化原则之前,先给大家熟悉一下索引类型: 主键索引PRIMARY KEY:它是一种特殊的唯一索引,不允许有空值.一般是在建表的时候同时创建主键索引. 唯一索引UNIQUE:唯一 ...

  4. MYSQL——索引设计原则与案例分析

    摘要 B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护.假如表中 R1~R5 的 (ID,k) 值分别为 (100,1).(200,2).(300,3).(500,5) 和 (600,6) ...

  5. MySql索引优化及Explain工具使用

    更多内容请浏览本人博客 explain 工具介绍 使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈 在 select 语句之前增加 explain 关键字,My ...

  6. mysql 索引设计_MySQL 索引原理及设计

    原标题:MySQL 索引原理及设计 索引一直是数据库中非常重要的概念,所以了解索引相关的知识是转入后端开发中必不可少的一环.这篇文章是我从开始做后端开发之后至今学习关于索引知识的一个总结,从原先很多概 ...

  7. python mysql索引 优化神器explain 慢查询

    ##############总结########## 数据库中专门帮助用户快速找到数据的一种数据结构 类似于字典的目录的索引 索引的作用:约束和加速查找 工作原理: b+树形结构 最上层是树根,中间是 ...

  8. MySQL索引设计原则

    一.MySQL常用的索引类型 1.1主键索引 primary key 1.2唯一索引 unique 1.3普通索引 index 1.4全文索引 1.5组合索引 二.MySQL常用的数据结构 2.1B- ...

  9. explain的用法_这次是真拯救了我,MySQL索引优化,explain讲得非常清楚了

    前言: 这篇文章主要讲 explain 如何使用,还有 explain 各种参数概念,之后会讲优化 一.Explain 用法 模拟Mysql优化器是如何执行SQL查询语句的,从而知道Mysql是如何处 ...

  10. mysql索引设计策略_MySQL索引设计一些策略

    前言 索引加快了检索的速度,但是却降低了数据列里插入.删除以及修改数值的速度.也就是说,索引降低了许多涉及写入的操作速度.之所以出现这种情况,是由于写入一条数据不仅仅是要写入到数据行,还需要所有的索引 ...

最新文章

  1. POJ2796 Feel Good(单调栈)
  2. 公司数据部培训讲义:ArcMap数字化培训教程
  3. [资料]Keychain 获取设备唯一
  4. Python:3种方式实现随机生成8位字符
  5. Git——Git基本教程
  6. 用pycharm写appium脚本的时候,导入一个模块有时候会报错
  7. c++制表符_在Linux命令行中将制表符(tab)转换为空格
  8. spring中bean的作用域属性single与prototype的区别
  9. 判断浏览器设置相应的CSS
  10. mysql执行计划缓存在哪_怎么去看懂mysql的执行计划
  11. Mac 终端以及常用终端命令
  12. git覆盖覆盖推送_强制“git Push”覆盖远程文件
  13. 【VS环境配置】海康SDK二次开发【亲测有用】
  14. 中彩体彩排列3彩票软件 解压缩软件
  15. 2017.2.15 开涛shiro教程-第二十一章-授予身份与切换身份(二) controller
  16. 计算机应用基础2008版试卷,《计算机应用基础》考试试卷(A卷)
  17. Mysql_sql存储过程
  18. 【C/C++服务器开发】文件,文件描述符,I/O多路复用,select / poll / epoll 详解
  19. 搞算法的都很强?不一定!你见过最差的算法工程师什么样?
  20. TOA与TDOA算法实现【MATLAB】

热门文章

  1. Gartner预测:2010年个人电脑销售增长两成
  2. springcloud整合sentinel
  3. Linux学习笔记:Jenkins的使用
  4. Android 两种方式实现类似水波扩散效果
  5. mongo的‘模糊匹配’
  6. ieee trans pami latex模板
  7. java List的简单运用
  8. Wowza Media Server 入门系列--Wowza Media Server 安装及演示
  9. 如何开发 Web 应用程序
  10. Hibernate简介与运行原理