MySQL索引(九)

1、索引介绍

1.1 什么是索引mysql

索引就比如一本书的目录,它会让你更快的找到内容。linux

让获取的数据更有目的性,从而提升数据库检索数据的性能。web

分为如下四种:redis

  • BTREE:B+树索引(基本上都是使用此索引)
  • HASH:HASH索引
  • FULLTEXT:全文索引
  • RTREE:R树索引

树形结构(B树:B树、B+树、B*树),算法

B树索引由多个层次构成:‘根’,‘枝’,‘叶’,它创建在表的列上sql

stu(id, name, age)数据库

假如说,在id列上建索引服务器

  A. 对id列的值,进行自动排序,把这些值有规律的存放到各个叶子节点运维

  B. 而且叶子节点还会存储整行数据的指针信息elasticsearch

  C. 生成上层枝节点,存储每一个对应叶子节点最小值和叶子节点指针

  D. 生成根节点,存储每一个枝节点的最小值以及对应的存储指针

  以上是B树索引的基本构成

  E. 对于B+树索引结构,对于范围查询有了更好的优化,叶子节点还会记录相邻叶子节点指针

  F. 对于B*树索引结构,枝节点还会记录相领枝节点的指针状况

B+树图:

1.2 主键和索引的区别

索引:索引比如是一本书的目录,能够快速的经过页码找到你须要的那一页。唯一地标识一行。
主键:作为数据库表惟一行标识,做为一个能够被外键有效引用的对象。
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里全部记录的引用指针。索引能够大大提升MySQL的检索速度。
数据库有两种查询方式,一个全表扫描,条件匹配。一个是索引。
主键是特殊的索引,主键是索引,索引不必定是主键,索引能够是多列,主键只能是一列。

 2、索引执行计划管理

基于特色的一些分类:

  1. 主键索引(汇集索引 cluseter indexes):parmary key(建立主键后自动生成的,最符合B+ 树的)
  2. 惟一键:惟一键索引(都是惟一值的列)
  3. 普通键:辅助索引(sec indexex)

优先使用主键索引,查询的时候还要基于主键索引进行查询。

3、添加、查询、删除索引

-- 添加一张表
mysql> create table stu (id int not null auto_increment primary key,name varchar(20),age tinyint,gender enum('m','f'),telnum varchar(12),qq varchar(20));
Query OK, 0 rows affected (0.02 sec)

-- 把name列设置为普通索引,idx_name为key的名字
mysql> alter table stu add index idx_name(name);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

-- 查询索引
mysql> desc stu;
+--------+---------------+------+-----+---------+----------------+
| Field  | Type          | Null | Key | Default | Extra          |
+--------+---------------+------+-----+---------+----------------+
| id     | int(11)       | NO   | PRI | NULL    | auto_increment |
| name   | varchar(20)   | YES  | MUL | NULL    |                |
| age    | tinyint(4)    | YES  |     | NULL    |                |
| gender | enum('m','f') | YES  |     | NULL    |                |
| telnum | varchar(12)   | YES  |     | NULL    |                |
| qq     | varchar(20)   | YES  |     | NULL    |                |
+--------+---------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

mysql> show index from stu;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| stu   |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| stu   |          1 | idx_name |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)

mysql> show index from stu\G
*************************** 1. row ***************************
        Table: stu
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 2. row ***************************
        Table: stu
   Non_unique: 1
     Key_name: idx_name
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
2 rows in set (0.00 sec)

-- 删除索引
mysql> alter table stu drop key idx_name;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show index from stu;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| stu   |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

-- key就是索引的意思,PRI就是主键,MUL就是普通的索引,UNQ、UNI 是惟一键

4、建立表的时候建立自增主键

CREATE TABLE `stu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` tinyint(4) DEFAULT NULL,
  `gender` enum('m','f') DEFAULT NULL,
  `telnum` varchar(12) DEFAULT NULL,
  `qq` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

若是当时没有建立,后面能够增长

mysql> CREATE TABLE `stu_test` (
    ->   `id` int(11) NOT NULL,
    ->   `name` varchar(20) DEFAULT NULL
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.23 sec)

mysql> desc stu_test;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql> alter table stu_test change id id int(11) primary key not null auto_increment;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc stu_test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

5、惟一索引

内容惟一,但不是主键

能够统计一下有没有重复值,用去重后的行数,和总行数作个比较,若是不同,说明有重复的值。

-- 添加telnum为惟一键索引
alter table stu add UNIQUE key uni_tel(telnum);

-- 统计总行数
select count(*) from webdb.t1;
-- telnum列去重以后还剩多少行 SELECT count(distinct telnum) from webdb.t1;

还能够判断是否是惟一索引,最简单的方法是建一建试试,若是建不上 说明有重复的。

 6、前缀索引和联合索引

若是字符较长的时候,可使用前缀索引

-- 根据字段的前10个字符创建索引,名称为index_note
alter table stu add note varchar(200);
alter table stu add index index_note(note(10));

联合索引

多个字段创建一个索引

条件:a(女生) and b(身高165) and c(身材好)

Index(a,b,c)

特色:前缀生效特性。

a,ab,abc,ac  能够走索引或者部分走索引

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

mysql> alter table stu add money int;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> alter table stu add index idx_dup(gender,age,money);
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> show index from stu;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| stu   |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| stu   |          0 | uni_tel  |            1 | telnum      | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| stu   |          1 | idx_dup  |            1 | gender      | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| stu   |          1 | idx_dup  |            2 | age         | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| stu   |          1 | idx_dup  |            3 | money       | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.00 sec)

7、explain 命令分析

MySQL中的执行计划,只分为两种。都是优化器决定的

全表扫描:

  通常在线上业务系统,要避免全表扫描

索引扫描:

  将要获取的数据,变得更有目的性。

经过explain命令来 获取优化器选择后的执行计划,并不输出后面的语句结果。

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

-- type 表示的是使用的是全表扫描仍是索引扫描
-- type 类型以下:ALL、index、range、ref、eq_ref、const、system、Null
-- 从左到右,性能愈来愈好,咱们在使用索引是,最底应达到range
-- key_len值越小越好
-- rows值越小越好

ALL 全表扫描

index:Full index scan,index与ALL区别为index类型只遍历索引树

range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。显而易见的索引范围扫描是带有between或者where子句里带有<,>查询。

where条件后 > < >= <= in or between and   like 'm%'

 不等因而不走索引的!= 、<>、like '%m%'

 此句性能略差

可改写为

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

 eq_ref:相似ref,区别就在使用的索引是惟一索引,对于每一个索引键值,表中只有一条记录匹配,简单来讲,就是多表链接中使用primary key或者unique key做为关键条件。

A join B

on A.sid=b.sid

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

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

NULL:MySQL在优化过程当中分解语句,执行时甚至不用访问表过索引。

例如:从一个索引列里选取最小值可经过单独索引查找完成

若是出现以上附加信息,请检查order by,group by,distince,join条件列上有没有合理的索引。(联合索引)

单列索引也不会避免filesort的出现

若是想优化,必须建立联合索引。

会发现,下面有两个索引,最后走的新建立的dup_codepogo

可是基于countcode有两个索引,须要删除一个,不然会影响优化器的算法。

Possible_key只有一个了,里面的extra正常了,只要不是filesort就正常。

 8、创建索引的原则(运维规范)

数据库索引的设计原则:

为了使索引的使用效率更高,在建立索引时,必须考虑在哪些字段上建立索引和建立什么类型的索引,那么索引设计缘由又是怎样的呢?

  1. 选择惟一性索引
  • 惟一性索引的值是惟一的,能够更快速的经过该索引来肯定某条记录
  • 例如:学生表中学号是具备惟一性的字段。为该字段创建惟一性索引能够很快的肯定某个学生的信息,若是使用姓名的话,可能存在同名现象,从而下降查询速度。
  • 主键索引和惟一索引,在查询中使用的效率最高的。

  注意:若是重复值较多,能够考虑采用联合索引

   2.为常常须要排序、分组和联合操做的字段创建索引

  • 常常须要order by、group by、distinct和union等操做的字段,排序操做会浪费不少时间。
  • 若是为其创建索引,能够有效地避免排序操做。

   3.为常做为查询条件的字段创建索引

  • 若是某个字段常常用来作查询条件,那么该字段查询的速度会影响整个表的查询速度。所以为这样的字段创建索引,能够提升整个表的查询速度。
  • 常常查询
  • 列值的重复值少

   4.尽可能使用前缀来索引

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

――――以上重点关注――――如下是能保护则保证的―――――

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

   2.删除再也不使用,或者不多使用索引

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

――――不走索引的状况---------(开发规范)

重点关注:

  1.没有查询条件,或者查询条件没有创建索引

-- 全表扫描
select * from t1;

-- 工具生成,和全表扫描是同样的
select * from t1 where 1=1; 

  在线上业务数据库中,特别是数据量比较大的表,是没有全表扫描这种需求的。

  A.对用户查看是很是痛苦的。

  B.对服务器来说是毁灭性的

  例外:数据处理分析的业务,通常也不用mysql了

select * from t1;  
-- SQL改写成如下语句
-- 须要在price列上创建索引
select * from t1 ORDER BY price limit 10;

  2.查询结果集是原表中的大部分数据,应该是25%以上。

  查询的结果集,超过了总数行数25%,优化器以为不必走索引了。

  若是业务容许,可使用limit控制

  怎么改写?

  结合业务判断,有没有更好的方式。若是没有更好的改写方案,尽可能不要在mysql存放这个数据了,放到redis中。

  3.索引自己失效,统计数据不真实

  索引有自我维护能力。

  对于表内容变化比较频繁的状况下,有可能会出现索引失败。

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

-- 错误的
select * from test where id-1=9;

--正确的
select * from test where id=10;

  5.隐式转换致使索引失效,这一点应当引发重视,也是开发中常犯的错误。

  这样会导航不索引失效,错误的例子

 

 

 

   隐式的把数字转换成字符串

 

  6.<>  、not in 不走索引

 

  7.like '%a' 百分号在最前面不走索引

 

  %linux%类的搜索需求,可使用elasticsearch

  8. 单独引用复合索引里非第一位置的索引列。

  复合索引index(a,b,c)

  where a

  where a b

  where a b c

  保会走a的部分索引

  where a c

  where a c b

  不走索引的:

  任何where条件列a不在第一条件列的状况不走索引

相关文章
相关标签/搜索