• 欢迎访问蜷缩的蜗牛博客 蜷缩的蜗牛
  • 微信搜索: 蜷缩的蜗牛 | 联系站长 kbsonlong@qq.com
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

MySQL 索引管理与执行计划

Mysql 蜷缩的蜗牛 6个月前 (03-30) 25次浏览 已收录

1.1 索引的介绍

  索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息。

  索引的一个主要目的就是加快检索表中数据的方法,亦即能协助信息搜索者尽快的找到符合限制条件的记录 ID 的辅助数据结构。

1.1.1 唯一索引

  唯一索引是不允许其中任何两行具有相同索引值的索引。当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。

  例如,如果在 employee 表中职员的姓(lname)上创建了唯一索引,则任何两个员工都不能同姓。

1.1.2 主键索引

  数据库表经常有一列或多列组合,其值唯一标识表中的每一行。该列称为表的主键。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。

  该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

1.1.3 聚集索引

  在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。

  聚集索引和非聚集索引的区别,如字典默认按字母顺序排序,读者如知道某个字的读音可根据字母顺序快速定位。因此聚集索引和表的内容是在一起的。如读者需查询某个生僻字,则需按字典前面的索引,举例按偏旁进行定位,找到该字对应的页数,再打开对应页数找到该字。

  这种通过两个地方而查询到某个字的方式就如非聚集索引。

1.1.4 索引列

  可以基于数据库表中的单列或多列创建索引。多列索引可以区分其中一列可能有相同值的行。如果经常同时搜索两列或多列或按两列或多列排序时,索引也很有帮助。

  例如,如果经常在同一查询中为姓和名两列设置判据,那么在这两列上创建多列索引将很有意义。

  检查查询的 WHERE 和 JOIN 子句。在任一子句中包括的每一列都是索引可以选择的对象。对新索引进行试验以检查它对运行查询性能的影响。考虑已在表上创建的索引数量。最好避免在单个表上有很多索引。

  检查已在表上创建的索引的定义。最好避免包含共享列的重叠索引。

检查某列中唯一数据值的数量,并将该数量与表中的行数进行比较。比较的结果就是该列的可选择性,这有助于确定该列是否适合建立索引,如果适合,确定索引的类型。

1.1.5 B 树算法

  B 树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中;否则,如果查询关键字比结点关键字小,就进入左边;如果比结点关键字大,就进入右边;如果左边或右边的指针为空,则报告找不到相应的关键。

如果 B 树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么 B 树的搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变 B 树结构(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销。

1.1.6 B+树算法

    B+树是 B-树的变体,也是一种多路搜索树:

       1.其定义基本与 B-树同,除了:
       2.非叶子结点的子树指针与关键字个数相同;
       3.非叶子结点的子树指针 P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
       5.为所有叶子结点增加一个链指针;
       6.所有关键字都在叶子结点出现;
       如:(M=3

 

   B+的搜索与 B-树也基本相同,区别是 B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

       B+的特性:

       1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
       2.不可能在非叶子结点命中;
       3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
       4.更适合文件索引系统;

1.1.7 HASH:HASH 算法

  哈希索引只有 Memory, NDB 两种引擎支持,Memory 引擎默认支持哈希索引,如果多个 hash 值相同,出现哈希碰撞,那么索引以链表方式存储。

  但是,Memory 引擎表只对能够适合机器的内存切实有限的数据集。

  要使 InnoDB 或 MyISAM 支持哈希索引,可以通过伪哈希索引来实现,叫自适应哈希索引。

  主要通过增加一个字段,存储 hash 值,将 hash 值建立索引,在插入和更新的时候,建立触发器,自动添加计算后的 hash 到表里。

1.1.8 其他的索引

FULLTEXT:全文索引
RTREE:R 树索引

1.2 MySQL 索引管理

  索引建立在表的列上(字段)的。

  在 where 后面的列建立索引才会加快查询速度。

  pages<—索引(属性)<—-查数据。

添加索引的方法:

alter table test add index index_name(name);
create index index_name on test(name);

语法格式:

alter tableadd index 索引名称(name);

1.2.1 创建普通索引

创建普通索引方法一:

mysql> ALTER TABLE PLAYERS ADD INDEX   name_idx(NAME);
mysql> desc PLAYERS;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| NAME       | char(15)    | NO   | MUL | NULL    |       |

创建普通索引方法二:

mysql> ALTER TABLE PLAYERS ADD INDEX   name_idx(NAME);
mysql> desc PLAYERS;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| NAME       | char(15)    | NO   | MUL | NULL    |       |

1.2.2 删除索引

alter table PLAYERS delete INDEX  name_idx;
mysql> show index from  PLAYERS\G
*************************** 1. row ***************************
        Table: PLAYERS
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: PLAYERNO
    Collation: A
  Cardinality: 14
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment:

1.3 MySQL 中的约束索引

主键索引

  只能有一个主键。

  主键索引:列的内容是唯一值,例如学号.

  表创建的时候至少要有一个主键索引,最好和业务无关。

普通索引

  加快查询速度,工作中优化数据库的关键。

  在合适的列上建立索引,让数据查询更高效。

create index index_name on test(name);
alter table test add index index_name(name);

用了索引,查一堆内容。

  在 where 条件关键字后面的列建立索引才会加快查询速度.

select id,name from test where state=1 order by id group by name;

唯一索引

  内容唯一,但不是主键。

create unique index index_name on test(name);

1.3.1 创建主键索引

  建立表时

CREATE TABLE `test` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` char(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;

  建立表后增加

CREATE TABLE `test` (
`id` int(4) NOT NULL,
`name` char(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;

增加自增主键

alter table test change id id int(4) primary key 
not null auto_increment;

1.3.2 使用字段前缀创建索引及联合索引

前缀索引:根据字段的前 N 个字符建立索引

create index index_name on test(name(8));

联合索引:多个字段建立一个索引。

where a 女生 and b 身高 165 and c 身材好
index(a,b,c)

  特点:前缀生效特性。

a,ab,abc 可以走索引。
b ac bc c 不走索引(5.6 之后 ac 可以走主键索引)。

  原则:把最常用来作为条件查询的列放在前面。

示例:

创建表

 create table people (id int not null auto_increment ,name char(20),sr(20),sex int ,age int, primary key (id));

创建联合索引

mysql> alter table people  add key name_sex_idx(name,sex)
    -> ;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

   查看索引的类型

mysql> desc people;
+-------+----------+------+-----+---------+----------------+
| Field | Type     | Null | Key | Default | Extra          |
+-------+----------+------+-----+---------+----------------+
| id    | int(11)  | NO   | PRI | NULL    | auto_increment |
| name  | char(20) | YES  | MUL | NULL    |                |
| sex   | int(11)  | YES  |     | NULL    |                |
| age   | int(11)  | YES  |     | NULL    |                |
+-------+----------+------+-----+---------+----------------+

建立唯一键索引

mysql> alter table people add unique key age_uidx(age);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

   查看数据表

mysql> desc people;
+-------+----------+------+-----+---------+----------------+
| Field | Type     | Null | Key | Default | Extra          |
+-------+----------+------+-----+---------+----------------+
| id    | int(11)  | NO   | PRI | NULL    | auto_increment |
| name  | char(20) | YES  | MUL | NULL    |                |
| sex   | int(11)  | YES  |     | NULL    |                |
| age   | int(11)  | YES  | UNI | NULL    |                |
+-------+----------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

  联合主键是联合索引的特殊形式

PRIMARY KEY (`Host`,`User`)
alter table test add sex char(4) not null;
create index ind_name_sex on test(name,sex);

前缀加联合索引

create index index_name on test(name(8),sex(2));

1.4 SQL 语句优化

1.4.1 企业 SQL 优化思路

  1、把一个大的不使用索引的 SQL 语句按照功能进行拆分

  2、长的 SQL 语句无法使用索引,能不能变成 2 条短的 SQL 语句让它分别使用上索引。

  3、对 SQL 语句功能的拆分和修改

  4、减少“烂”SQL 由运维(DBA)和开发交流(确认),共同确定如何改,最终由 DBA 执行

  5、制定开发流程

1.4.2 不适合走索引的场景

  1、唯一值少的列上不适合建立索引或者建立索引效率低。例如:性别列

  2、小表可以不建立索引,100 条记录。

  3、对于数据仓库,大量全表扫描的情况,建索引反而会慢

1.4.3 查看表的唯一值数量

select count(distinct user) from mysql.user;
select count(distinct user,host) from mysql.user;

1.4.4 建立索引流程

  1、找到慢 SQL。

show processlist;

    记录慢查询日志。

  2、explain select 句,条件列多。

  3、查看表的唯一值数量:

select count(distinct user) from mysql.user;
select count(distinct user,host) from mysql.user;

    条件列多。可以考虑建立联合索引。

  4、建立索引(流量低谷)

force index

  5、拆开语句(和开发)。

  6、like ‘%%’不用 mysql

  7、进行判断重复的行数

查看行数:

mysql> select count(*) from city;
+----------+
| count(*) |
+----------+
|     4079 |
+----------+
1 row in set (0.00 sec)

查看去重后的行数:

mysql> select count(distinct countrycode) from city;
+-----------------------------+
| count(distinct countrycode) |
+-----------------------------+
|                         232 |
+-----------------------------+
1 row in set (0.00 sec)

1.5 用 explain 查看 SQL 的执行计划

  在工作中,我们用于捕捉性能问题最常用的就是打开慢查询,定位执行效率差的 SQL,那么当我们定位到一个 SQL 以后还不算完事,我们还需要知道该 SQL 的执行计划,比如是全表扫描,还是索引扫描,这些都需要通过 EXPLAIN 去完成。

  EXPLAIN 命令是查看优化器如何决定执行查询的主要方法。可以帮助我们深入了解 MySQL 的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行 SQL 语句时哪种策略预计会被优化器采用。

  需要注意的是,生成的 QEP 并不确定,它可能会根据很多因素发生改变。MySQL 不会将一个 QEP 和某个给定查询绑定,QEP 将由 SQL 语句每次执行时的实际情况确定,即便使用存储过程也是如此。尽管在存储过程中 SQL 语句都是预先解析过的,但 QEP 仍然会在每次调用存储过程的时候才被确定。

1.5.1 查看 select 语句的执行过程

mysql> explain select id,name from test where name='clsn';
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
|  1 | SIMPLE      | test  | ref  | name_idx      | name_idx | 24      | const |    1 | Using where |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
1 row in set (0.00 sec)

  SQL_NO_CACHE 的作用是禁止缓存查询结果。

使用 where条件查找

mysql> explain select user,host from mysql.user where user='root' and host='127.0.0.1';
+----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref         | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+
|  1 | SIMPLE      | user  | const | PRIMARY       | PRIMARY | 228     | const,const |    1 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+
1 row in set (0.00 sec)

1.5.2 通过执行计划可以知道什么?

mysql> explain select d1.age, t2.id from (select age,name from t1 where id in (1,2))d1, t2 where d1.age=t2.age group by d1.age, t2.id order by t2.id;
+----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+
| id | select_type | table      | type  | possible_keys | key     | key_len | ref    | rows | Extra                           |
+----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL    | NULL    | NULL   |    2 | Using temporary; Using filesort |
|  1 | PRIMARY     | t2         | ref   | age           | age     | 5       | d1.age |    1 | Using where; Using index        |
|  2 | DERIVED     | t1         | range | PRIMARY       | PRIMARY | 4       | NULL   |    2 | Using where                     |
+----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+
3 rows in set (0.00 sec)

1.5.3 MySQL 执行计划调用方式

1.EXPLAIN SELECT ……
2.EXPLAIN EXTENDED SELECT ……
  将执行计划"反编译"成 SELECT 语句,运行 SHOW WARNINGS 可得到被 MySQL 优化器优化后的查询语句
3.EXPLAIN PARTITIONS SELECT ……
  用于分区表的 EXPLAIN 生成 QEP 的信息

1.5.4 执行计划包含的信息

+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+

1.5.5 id

  包含一组数字,表示查询中执行 select 子句或操作表的顺序

【示例一】id 相同,执行顺序由上至下

mysql> explain select t2.* from t1, t2, t3 where t1.id=t2.id and t1.id=t3.id and t1.name='';
+----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref        | rows | Extra                    |
+----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+
|  1 | SIMPLE      | t1    | ref    | PRIMARY,name  | name    | 63      | const      |    1 | Using where; Using index |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t1.id |    1 |                          |
|  1 | SIMPLE      | t3    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t1.id |    1 | Using index              |
+----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+
3 rows in set (0.00 sec)

【示例二】如果是子查询,id 的序号会递增,id 值越大优先级越高,越先被执行

mysql> explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.name=''));
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
|  1 | PRIMARY     | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables |
|  2 | SUBQUERY    | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | no matching row in const table                      |
|  3 | SUBQUERY    | t3    | ref  | name          | name | 63      |      |    1 | Using where; Using index                            |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
3 rows in set (0.00 sec)

【示例三】id 如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id 值越大,优先级越高,越先执行

mysql> explain select t2.* from (select t3.id from t3 where t3.name='')s1, t2 where s1.id=t2.id;
+----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra                    |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL  |    1 |                          |
|  1 | PRIMARY     | t2         | const  | PRIMARY       | PRIMARY | 4       | const |    1 |                          |
|  2 | DERIVED     | t3         | ref    | name          | name    | 63      |       |    1 | Using where; Using index |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+
3 rows in set (0.00 sec)

1.5.6 select_type

示查询中每个 select 子句的类型(简单 OR 复杂)
    a. SIMPLE:查询中不包含子查询或者 UNION
    b. 查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
    c. 在 SELECT 或 WHERE 列表中包含了子查询,该子查询被标记为:SUBQUERY
    d. 在 FROM 列表中包含的子查询被标记为:DERIVED(衍生)用来表示包含在 from 子句中的子查询的 select,mysql 会递归执行并将结果放到一个临时表中。服务器内部称为"派生表",因为该临时表是从子查询中派生出来的
    e. 若第二个 SELECT 出现在 UNION 之后,则被标记为 UNION;若 UNION 包含在 FROM 子句的子查询中,外层 SELECT 将被标记为:DERIVED
    f. 从 UNION 表获取结果的 SELECT 被标记为:UNION RESULT

说明:

SUBQUERY 和 UNION 还可以被标记为 DEPENDENT 和 UNCACHEABLE。
DEPENDENT 意味着 select 依赖于外层查询中发现的数据。
UNCACHEABLE 意味着 select 中的某些 特性阻止结果被缓存于一个 item_cache 中。

【示例】

mysql> explain select d1.name, ( select id from t3) d2 from (select id,name from t1 where name='')d1 union (select name,id from t2);
+----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+
| id | select_type  | table      | type   | possible_keys | key  | key_len | ref  | rows | Extra                    |
+----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+
|  1 | PRIMARY      | <derived3> | system | NULL          | NULL | NULL    | NULL |    0 | const row not found      |
|  3 | DERIVED      | t1         | ref    | name          | name | 63      |      |    1 | Using where; Using index |
|  2 | SUBQUERY     | t3         | index  | NULL          | age  | 5       | NULL |    6 | Using index              |
|  4 | UNION        | t2         | index  | NULL          | name | 63      | NULL |    4 | Using index              |
| NULL | UNION RESULT | <union1,4> | ALL    | NULL          | NULL | NULL    | NULL | NULL |                          |
+----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+
5 rows in set (0.00 sec)

内容说明:

第一行:id 列为 1,表示第一个 select,select_type 列的 primary 表 示该查询为外层查询,table 列被标记为<derived3>,表示查询结果来自一个衍生表,其中 3 代表该查询衍生自第三个 select 查询,即 id 为 3 的 select。
第二行:id 为 3,表示该查询的执行次序为 2( 4 => 3),是整个查询中第三个 select 的一部分。因查询包含在 from 中,所以为 derived。
第三行:select 列表中的子查询,select_type 为 subquery,为整个查询中的第二个 select。
第四行:select_type 为 union,说明第四个 select 是 union 里的第二个 select,最先执行。
第五行:代表从 union 的临时表中读取行的阶段,table 列的<union1,4>表示用第一个和第四个 select 的结果进行 union 操作。

1.5.7 type

  表示 MySQL 在表中找到所需行的方式,又称“访问类型”,常见类型如下:

ALL, index,  range, ref, eq_ref, const, system, NULL

  从左到右,性能从最差到最好

【示例一】ALL:Full Table Scan, MySQL 将遍历全表以找到匹配的行

mysql> explain select * from t1 where email='';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

【示例二】index:Full Index Scan,index 与 ALL 区别为 index 类型只遍历索引树

mysql> explain select id from t1;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | age  | 5       | NULL |    4 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

【示例三】range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。

显而易见的索引范围扫描是带有 between 或者 where 子句里带有<, >查询。当 mysql 使用索引去查找一系列值时,例如 IN()和 OR 列表,也会显示 range(范围扫描),当然性能上面是有差异的。

mysql> explain select * from t1 where id in (1,4);
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from t1 where id between 1 and 4;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    3 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from t1 where id=1 or id=4;       
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.01 sec)
mysql> explain select * from t1 where id > 1;      
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    3 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)

【示例四】ref:使用非唯一索引扫描或者唯一索引的前缀扫描,返回匹配某个单独值的记录行

mysql> explain select * from t1 where name='guo';
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | name          | name | 63      | const |    1 | Using where |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
1 row in set (0.00 sec)

【示例五】eq_ref:类似 ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用 primary key 或者 unique key 作为关联条件。

mysql> explain select t1.name from t1, t2 where t1.id=t2.id;
+----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref        | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+
|  1 | SIMPLE      | t1    | index  | PRIMARY       | name    | 63      | NULL       |    4 | Using index |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t1.id |    1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+
2 rows in set (0.00 sec)

【示例六】const、system:当 MySQL 对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。

  如将主键置于 where 列表中,MySQL 就能将该查询转换为一个常量

mysql> explain select * from ( select * from t1 where id=1)b1;
+----+-------------+------------+--------+---------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+---------------+---------+---------+------+------+-------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | t1         | const  | PRIMARY       | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+---------------+---------+---------+------+------+-------+
2 rows in set (0.00 sec)

  注:system 是 const 类型的特例,当查询的表只有一行的情况下,使用 system

【示例七】NULL:MySQL 在优化过程中分解语句,执行时甚至不用访问表或索引,   例如从一个索引列里选取最小值可以通过单独索引查找完成。

mysql> explain select * from t1 where id = (select min(id) from t2);
+----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra                        |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+
|  1 | PRIMARY     | t1    | const | PRIMARY       | PRIMARY | 4       | const |    1 |                              |
|  2 | SUBQUERY    | NULL  | NULL  | NULL          | NULL    | NULL    | NULL  | NULL | Select tables optimized away |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+
2 rows in set (0.00 sec)

1.5.8 possible_keys

  指出 MySQL 能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用

1.5.9 key

  显示 MySQL 在查询中实际使用的索引,若没有使用索引,显示为 NULL

【示例】

mysql> explain select id,age from t1;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | age  | 5       | NULL |    4 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

1.5.10 key_len

  表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len 显示的值为索引字段的最大可能长度,并非实际使用长度,即 key_len 是根据表定义计算而得,不是通过表内检索出的)。

1.5.11 ref

  表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

1.5.12 rows

  表示 MySQL 根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。

【示例】

mysql> explain select * from t1 , t2 where t1.id=t2.id and t2.name='atlas';
+----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref        | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+
|  1 | SIMPLE      | t2    | ref    | PRIMARY,name  | name    | 63      | const      |    1 | Using where |
|  1 | SIMPLE      | t1    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t2.id |    1 |             |
+----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+
2 rows in set (0.00 sec)

1.5.13 Extra

  包含不适合在其他列中显示但十分重要的额外信息

【示例一】Using index

该值表示相应的 select 操作中使用了覆盖索引(Covering Index)

mysql> explain select id from t1;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | age  | 5       | NULL |    4 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

    覆盖索引(Covering Index)

    MySQL 可以利用索引返回 select 列表中的字段,而不必根据索引再次读取数据文件

    包含所有满足查询需要的数据的索引称为覆盖索引(Covering Index)

    注意:如果要使用覆盖索引,一定要注意 select 列表中只取出需要的列,不可 select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降

【示例二】Using where

  表示 mysql 服务器将在存储引擎检索行后再进行过滤。许多 where 条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带 where 字句的查询都会显示”Using where”。

  有时”Using where”的出现就是一个暗示:查询可受益与不同的索引。

mysql> explain select id,name from t1 where id<4;
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
|  1 | SIMPLE      | t1    | index | PRIMARY       | name | 63      | NULL |    4 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

【示例三】Using temporary

  表示 MySQL 需要使用临时表来存储结果集,常见于排序和分组查询

  这个值表示使用了内部临时(基于内存的)表。一个查询可能用到多个临时表。有很多原因都会导致 MySQL 在执行查询期间创建临时表。两个常见的原因是在来自不同表的上使用了 DISTINCT,或者使用了不同的 ORDER BY 和 GROUP BY 列。可以强制指定一个临时表使用基于磁盘的 MyISAM 存储引擎。这样做的原因主要有两个:

    1)内部临时表占用的空间超过 min(tmp_table_size,max_heap_table_size)系统变量的限制

    2)使用了 TEXT/BLOB 列

mysql> explain select id from t1 where id in (1,2) group by age,name;
+----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                                        |
+----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
|  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where; Using temporary; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
1 row in set (0.00 sec)

【示例四】Using filesort

  MySQL 中无法利用索引完成的排序操作称为“文件排序”

mysql> explain select id,age from t1 order by name; 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)
mysql> explain select id,age from t1 order by age; 
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | age  | 5       | NULL |    4 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

【示例五】Using join buffer

  该值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。

  如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

mysql> explain select t1.name from t1 inner join t2 on t1.name=t2.name;
+----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref          | rows | Extra                    |
+----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+
|  1 | SIMPLE      | t1    | index | name          | name | 63      | NULL         |    4 | Using index              |
|  1 | SIMPLE      | t2    | ref   | name          | name | 63      | test.t1.name |    2 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+
2 rows in set (0.00 sec)

删除 t1 索引

mysql> alter table t1 drop key name;                                   
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

删除 t2 索引

mysql> alter table t2 drop key name; 
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

经常查找

mysql> explain select t1.name from t1 inner join t2 on t1.name=t2.name;
+----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                          |
+----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
|  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL |    4 |                                |
|  1 | SIMPLE      | t2    | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using where; Using join buffer |
+----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
2 rows in set (0.00 sec)

【示例六】Impossible where

  这个值强调了 where 语句会导致没有符合条件的行。

mysql> EXPLAIN SELECT * FROM t1 WHERE 1=2;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra            |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.00 sec)

【示例七】Select tables optimized away

  这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行.

mysql> explain select max(id) from t1;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                        |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
1 row in set (0.00 sec)

【示例八】Index merges

  当 MySQL 决定要在一个给定的表上使用超过一个索引的时候,就会出现以下格式中的一个,详细说明使用的索引以及合并的类型。

Using sort_union(...)
Using union(...)
Using intersect(...)

1.5.14 小结

  EXPLAIN 不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况。

  EXPLAIN 不考虑各种 Cache。

  EXPLAIN 不能显示 MySQL 在执行查询时所作的优化工作。

  部分统计信息是估算的,并非精确值。

  EXPALIN 只能解释 SELECT 操作,其他操作要重写为 SELECT 后查看执行计划。

1.6 mysql 不走索引的原因

1.6.1 一些常见的原因

  1) 没有查询条件,或者查询条件没有建立索引

  2) 在查询条件上没有使用引导列

  3) 查询的数量是大表的大部分,应该是 30%以上。

  4) 索引本身失效

  5) 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)

    错误的例子:select * from test where id-1=9; 正确的例子:select * from test where id=10;

  6) 对小表查询

  7) 提示不使用索引

  8) 统计数据不真实

  9) CBO 计算走索引花费过大的情况。其实也包含了上面的情况,这里指的是表占有的 block 要比索引小。

  10)隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.

    由于表的字段 tel_num 定义为 varchar2(20),但在查询时把该字段作为 number 类型以 where 条件传给数据库,这样会导致索引失效.

      错误的例子:select * from test where tel_nume=13333333333;

      正确的例子:select * from test where tel_nume=’13333333333′;

  11) 注意使用的特殊符号

        1,<>  ,!=

        2,单独的>,<,(有时会用到,有时不会)

  12)like “%_” 百分号在前.

      select * from t1  where name like ‘linux 培训%’;

  13) not in ,not exist.

  14)  in  尽量改成 union 。

  15)当变量采用的是 times 变量,而表的字段采用的是 date 变量时.或相反情况。

  16)B-tree 索引 is null 不会走,is not null 会走,位图索引 is null,is not null 都会走 。

  17)联合索引 is not null 只要在建立的索引列(不分先后)都会走,

  in null 时 必须要和建立索引第一列一起使用,当建立索引第一位置条件是 is null 时,其他建立索引的列可以是 is null(但必须在所有列 都满足 is null 的时候),或者=一个值;

  当建立索引的第一位置是=一个值时,其他索引列可以是任何情况(包括 is null =一个值),以上两种情况索引都会走。其他情况不会走。

1.6.2 需要注意的一些

1)    MyISAM 存储引擎索引键长度总和不能超过 1000 字节;
2)    BLOB 和 TEXT 类型的列只能创建前缀索引;
3)    MySQL 目前不支持函数索引;
4)    使用不等于(!= 或者<>)的时候 MySQL 无法使用索引;
5)    过滤字段使用了函数运算后(如 abs(column)),MySQL 无法使用索引;
6)    Join 语句中 Join 条件字段类型不一致的时候 MySQL 无法使用索引;
7)    使用 LIKE 操作的时候如果条件以通配符开始( '%abc...')MySQL 无法使用索引;
8)    使用非等值查询的时候 MySQL 无法使用 Hash 索引;
9)    在我们使用索引的时候,需要注意上面的这些限制,尤其是要注意无法使用索引的情况,因为这很容易让我们因为疏忽而造成极大的性能隐患。

1.7 数据库索引的设计原则

  为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引。

1.7.1 那么索引设计原则又是怎样的

1.选择唯一性索引

  唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。

  例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。

2.为经常需要排序、分组和联合操作的字段建立索引

  经常需要 ORDER BY、GROUP BY、DISTINCT 和 UNION 等操作的字段,排序操作会浪费很多时间。

如果为其建立索引,可以有效地避免排序操作。

3.为常作为查询条件的字段建立索引

  如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,

  为这样的字段建立索引,可以提高整个表的查询速度。

4.限制索引的数目

  索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。

5.尽量使用数据量少的索引

  如果索引的值很长,那么查询的速度会受到影响。例如,对一个 CHAR(100)类型的字段进行全文检索需要的时间肯定要比对 CHAR(10)类型的字段需要的时间要多。

6.尽量使用前缀来索引

  如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT 和 BLOG 类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。

7.删除不再使用或者很少使用的索引

  表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

8.小表不应建立索引

  包含大量的列并且不需要搜索非空值的时候可以考虑不建索引

1.8 参考文献

https://baike.baidu.com/item/数据库索引/8751686?fr=aladdin
https://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html
http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx
http://blog.csdn.net/woshiqjs/article/details/24135495

 

 

本文转载自 MySQL 索引管理与执行计划


蜷缩的蜗牛 , 版权所有丨如未注明 , 均为原创丨 转载请注明MySQL 索引管理与执行计划
喜欢 (0)
[]
分享 (0)