mysql七:索引原理与慢查询优化(待完整)

1、介绍

  索引在MySQL中也叫作“”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能很是关键,尤为是当表中的数据量愈来愈大时,索引对于性能的影响愈发重要。
  索引优化应该是对查询性能优化最有效的手段了。索引可以轻易将查询性能提升好几个数量级。索引至关于字典的音序表,若是要查某个字,若是不使用音序表,则须要从几百页中逐页去查。mysql

为何要有索引?ios

  通常的应用系统,读写比例在10:1左右,并且插入操做和通常的更新操做不多出现性能问题,在生产环境中,咱们遇到最多的,也是最容易出问题的,仍是一些复杂的查询操做,所以对查询语句的优化显然是重中之重。提及加速查询,就不得不提到索引了。sql

是否对索引存在误解?数据库

  索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能相当重要。一些开发人员老是在过后才想起添加索引----我一直认为,这源于一种错误的开发模式。若是知道数据的使用,从一开始就应该在须要处添加索引。开发人员每每对数据库的使用停留在应用的层面,好比编写SQL语句、存储过程之类,他们甚至可能不知道索引的存在,或认为过后让相关DBA加上便可。DBA每每不够了解业务的数据流,而添加索引须要经过监控大量的SQL语句进而从中找到问题,这个步骤所需的时间确定是远大于初始添加索引所需的时间,而且可能会遗漏一部分的索引。固然索引也并非越多越好,我曾经遇到过这样一个问题:某台MySQL服务器iostat显示磁盘使用率一直处于100%,通过分析后发现是因为开发人员添加了太多的索引,在删除一些没必要要的索引以后,磁盘使用率立刻降低为20%。可见索引的添加也是很是有技术含量的。性能优化

2、索引原理

一、索引原理

  经过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,咱们能够老是用同一种查找方式来锁定数据。服务器

二、磁盘IO与预读

  考虑到磁盘IO是很是高昂的操做,计算机操做系统作了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,由于局部预读性原理告诉咱们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据咱们称之为一页(page)。具体一页有多大数据跟操做系统有关,通常为4k或8k,也就是咱们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计很是有帮助。数据结构

3、索引的数据结构

  前面讲了索引的基本原理,数据库的复杂性,又讲了操做系统的相关知识,目的就是让你们了解,任何一种数据结构都不是凭空产生的,必定会有它的背景和使用场景,咱们如今总结一下,咱们须要这种数据结构可以作些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么咱们就想到若是一个高度可控的多路搜索树是否能知足需求呢?就这样,b+树应运而生(B+树是经过二叉查找树,再由平衡二叉树,B树演化而来)。ide

  如上图,是一颗b+树,关于b+树的定义能够参见B+树,这里只说一些重点,浅蓝色的块咱们称之为一个磁盘块,能够看到每一个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P一、P二、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如1七、35并不真实存在于数据表中。性能

一、b+树的查找过程

  如图所示,若是要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找肯定29在17和35之间,锁定磁盘块1的P2指针,内存时间由于很是短(相比磁盘的IO)能够忽略不计,经过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,经过指针加载磁盘块8到内存,发生第三次IO,同时内存中作二分查找找到29,结束查询,总计三次IO。真实的状况是,3层的b+树能够表示上百万的数据,若是上百万的数据查找只须要三次IO,性能提升将是巨大的,若是没有索引,每一个数据项都要发生一次IO,那么总共须要百万次的IO,显然成本很是很是高。测试

二、b+树性质

  1.索引字段要尽可能的小:经过上面的分析,咱们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每一个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N必定的状况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,若是数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为何每一个数据项,即索引字段要尽可能的小,好比int占4字节,要比bigint8字节少一半。这也是为何b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度降低,致使树增高。当数据项等于1时将会退化成线性表。

  2.索引的最左匹配特性:当b+树的数据项是复合的数据结构,好比(name,age,sex)的时候,b+数是按照从左到右的顺序来创建搜索树的,好比当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来肯定下一步的所搜方向,若是name相同再依次比较age和sex,最后获得检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪一个节点,由于创建搜索树的时候name就是第一个比较因子,必需要先根据name来搜索才能知道下一步去哪里查询。好比当(张三,F)这样的数据来检索时,b+树能够用name来指定搜索方向,但下一个字段age的缺失,因此只能把名字等于张三的数据都找到,而后再匹配性别是F的数据了, 这个是很是重要的性质,即索引的最左匹配特性。

4、汇集索引与辅助索引

  在数据库中,B+树的高度通常都在2~4层,这也就是说查找某一个键值的行记录时最多只须要2到4次IO,这倒不错。由于当前通常的机械硬盘每秒至少能够作100次IO,2~4次的IO意味着查询时间只须要0.02~0.04秒。

  数据库中的B+树索引能够分为汇集索引(clustered index)和辅助索引(secondary index),

  汇集索引与辅助索引相同的是:无论是汇集索引仍是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着全部的数据。

  汇集索引与辅助索引不一样的是:叶子结点存放的是不是一整行的信息

一、汇集索引

  InnoDB存储引擎表示索引组织表,即表中数据按照主键顺序存放。而汇集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据,也将汇集索引的叶子结点称为数据页。汇集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构同样,每一个数据页都经过一个双向链表来进行连接。

  若是未定义主键,MySQL取第一个惟一索引(unique)并且只含非空列(NOT NULL)做为主键,InnoDB使用它做为聚簇索引。

  若是没有这样的列,InnoDB就本身产生一个这样的ID值,它有六个字节,并且是隐藏的(没法用来帮忙加速查询),使其做为聚簇索引。

  因为实际的数据页只能按照一棵B+树进行排序,所以每张表只能拥有一个汇集索引。在多少状况下,查询优化器倾向于采用汇集索引。由于汇集索引可以在B+树索引的叶子节点上直接找到数据。此外因为定义了数据的逻辑顺序,汇集索引可以特别快地访问针对范围值得查询。

汇集索引的好处之一:它对主键的排序查找和范围查找速度很是快,叶子节点的数据就是用户所要查询的数据。如用户须要查找一张表,查询最后的10位用户信息,因为B+树索引是双向链表,因此用户能够快速找到最后一个数据页,并取出10条记录

#参照第六小结测试索引的准备阶段来建立出表s1
mysql> desc s1; #最开始没有主键
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id     | int(11)     | NO   |     | NULL    |       |
| name   | varchar(20) | YES  |     | NULL    |       |
| gender | char(6)     | YES  |     | NULL    |       |
| email  | varchar(50) | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

mysql> explain select * from s1 order by id desc limit 10; #Using filesort,须要二次排序
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra          |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+----------------+
|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 2633472 |   100.00 | Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+----------------+
1 row in set, 1 warning (0.11 sec)

mysql> alter table s1 add primary key(id); #添加主键
Query OK, 0 rows affected (13.37 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from s1 order by id desc limit 10; #基于主键的汇集索引在建立完毕后就已经完成了排序,无需二次排序
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------+
|  1 | SIMPLE      | s1    | NULL       | index | NULL          | PRIMARY | 4       | NULL |   10 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.04 sec)
示例

汇集索引的好处之二:范围查询(range query),即若是要查找主键某一范围内的数据,经过叶子节点的上层中间节点就能够获得页的范围,以后直接读取数据页便可

mysql> alter table s1 drop primary key;
Query OK, 2699998 rows affected (24.23 sec)
Records: 2699998  Duplicates: 0  Warnings: 0

mysql> desc s1;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id     | int(11)     | NO   |     | NULL    |       |
| name   | varchar(20) | YES  |     | NULL    |       |
| gender | char(6)     | YES  |     | NULL    |       |
| email  | varchar(50) | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.12 sec)

mysql> explain select * from s1 where id > 1 and id < 1000000; #没有汇集索引,预估须要检索的rows数以下
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 2690100 |    11.11 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> alter table s1 add primary key(id);
Query OK, 0 rows affected (16.25 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from s1 where id > 1 and id < 1000000; #有汇集索引,预估须要检索的rows数以下
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | s1    | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 1343355 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.09 sec)
示例

二、辅助索引

  表中除了汇集索引外其余索引都是辅助索引(Secondary Index,也称为非汇集索引),与汇集索引的区别是:辅助索引的叶子节点不包含行记录的所有数据。辅助索引包含一个辅助索引对应字段自己的值、还包含一个对id字段的引用

  叶子节点除了包含键值之外,每一个叶子节点中的索引行中还包含一个书签(bookmark)。该书签用来告诉InnoDB存储引擎去哪里能够找到与索引相对应的行数据。

  因为InnoDB存储引擎是索引组织表,所以InnoDB存储引擎的辅助索引的书签就是相应行数据的汇集索引键。以下图

  辅助索引的存在并不影响数据在汇集索引中的组织,所以每张表上能够有多个辅助索引,但只能有一个汇集索引。当经过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并经过叶子级别的指针得到只想主键索引的主键,而后再经过主键索引来找到一个完整的行记录。

  举例来讲,若是在一棵高度为3的辅助索引树种查找数据,那须要对这个辅助索引树遍历3次找到指定主键,若是汇集索引树的高度一样为3,那么还须要对汇集索引树进行3次查找,最终找到一个完整的行数据所在的页,所以一共须要6次逻辑IO访问才能获得最终的一个数据页。

5、MySQL索引管理

一、功能

  1. 索引的功能就是加速查找

  2. mysql中的primary key,unique,联合惟一也都是索引,这些索引除了加速查找之外,还有约束的功能

二、MySQL经常使用的索引

普通索引INDEX:加速查找

惟一索引:

  -主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)

  -惟一索引UNIQUE:加速查找+约束(不能重复)

联合索引:

  -PRIMARY KEY(id,name):联合主键索引

  -UNIQUE(id,name):联合惟一索引

  -INDEX(id,name):联合普通索引

  各个索引的应用场景:

举个例子来讲,好比你在为某商场作一个会员卡的系统。

这个系统有一个会员表
有下列字段:
会员编号 INT
会员姓名 VARCHAR(10)
会员身份证号码 VARCHAR(18)
会员电话 VARCHAR(10)
会员住址 VARCHAR(50)
会员备注信息 TEXT

那么这个 会员编号,做为主键,使用 PRIMARY
会员姓名 若是要建索引的话,那么就是普通的 INDEX
会员身份证号码 若是要建索引的话,那么能够选择 UNIQUE (惟一的,不容许重复)

#除此以外还有全文索引,即FULLTEXT
会员备注信息 , 若是须要建索引的话,能够选择全文搜索。
用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,若是就一两行字的,普通的 INDEX 也能够。
但其实对于全文搜索,咱们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来作全文搜索。

#其余的如空间索引SPATIAL,了解便可,几乎不用
各个索引应用场景

三、索引的两大类型hash与btree

咱们能够在建立上述索引的时候,为其指定索引类型,分两类

  • hash类型的索引:查询单条快,范围查询慢
  • btree类型的索引:b+树,层数越多,数据量指数级增加(咱们就用它,由于innodb默认支持它)

不一样的存储引擎支持的索引类型也不同

  • InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  • MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  • Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
  • NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
  • Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;

四、建立/删除索引的语法

#方法一:建立表时
      CREATE TABLE 表名 (
                字段名1  数据类型 [完整性约束条件…],
                字段名2  数据类型 [完整性约束条件…],
                [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                [索引名]  (字段名[(长度)]  [ASC |DESC]) 
                );


#方法二:CREATE在已存在的表上建立索引
        CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名 
                     ON 表名 (字段名[(长度)]  [ASC |DESC]) ;


#方法三:ALTER TABLE在已存在的表上建立索引
        ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                             索引名 (字段名[(长度)]  [ASC |DESC]) ;

#删除索引:DROP INDEX 索引名 ON 表名字;
#方式一
create table t1(
    id int,
    name char,
    age int,
    sex enum('male','female'),
    unique key uni_id(id),
    index ix_name(name) #index没有key
);


#方式二
create index ix_age on t1(age);

#方式三
alter table t1 add index ix_sex(sex);

#查看
mysql> show create table t1;
| t1    | CREATE TABLE `t1` (
  `id` int(11) DEFAULT NULL,
  `name` char(1) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` enum('male','female') DEFAULT NULL,
  UNIQUE KEY `uni_id` (`id`),
  KEY `ix_name` (`name`),
  KEY `ix_age` (`age`),
  KEY `ix_sex` (`sex`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
示例
相关文章
相关标签/搜索