高性能MySQL05-索引原理

一、简介

索引(在MySQL中也叫做“键(key)”),是存储引擎用于快速找到记录的一种数据结构。

索引类似于书籍的目录,要想找到一本书的某个特定主题,需要先查找书的目录,定位对应的页码。

存储引擎使用类似的方式进行数据查询,先去索引当中找到对应的值,然后根据匹配的索引找到对应的数据行。一般来说,在WHERE和JOIN中出现的列需要建立索引,但也不完全如此,因为MySQL只对<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE才会使用索引。

索引有很多种类型,都是实现在存储引擎层的。Mysql主要支持如下几种索引:

1)普通索引
2)唯一索引
3)主键索引
4)组合索引
5)全文索引

二、语法

CREATE TABLE table_name[col_name data type]
[unique|fulltext][index|key][index_name](col_name[length])[asc|desc]

1)unique|fulltext为可选参数,分别表示唯一索引、全文索引
2)index和key为同义词,两者作用相同,用来指定创建索引
3)col_name为需要创建索引的字段列,该列必须从数据表中该定义的多个列中选择
4)index_name指定索引的名称,为可选参数,如果不指定,默认col_name为索引值
5)length为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度
6)asc或desc指定升序或降序的索引值存储

例 :

# 1)添加INDEX(普通索引) 
    mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) 
# 2)添加UNIQUE(唯一索引) 
    mysql>ALTER TABLE `table_name` ADD UNIQUE ( `column`) 
# 3)添加PRIMARY KEY(主键索引) 
    mysql>ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 
# 4)添加组合索引 
    mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`)
# 5)添加FULLTEXT(全文索引) 
    mysql>ALTER TABLE `table_name` ADD FULLTEXT ( `column`) 

相关操作:

# 查看表结构
    desc table_name;
# 查看生成表的SQL
    show create table table_name;
# 查看索引
    show index from  table_name;
    show keys from table_name;
# 查看执行时间
    set profiling = 1;
    SQL...
    show profiles;
# 查看索引的使用情况
    show status like '%Handler_read%' ;
# 使用explain分析

三、索引的机制

传统的查询方法,是按照表的顺序遍历的,不论查询几条数据,mysql需要将表的数据从头到尾遍历一遍。在我们添加完索引之后,mysql一般通过BTREE算法生成一个索引文件,在查询数据库时,找到索引文件进行遍历(折半查找大幅查询效率),找到相应的键从而获取数据。

1、索引对性能的影响:
1)创建索引是为产生索引文件的,占用磁盘空间
2)大大减少服务器需要扫描的数据量
3)帮助服务器避免排序和临时表
4)将随机I/O变为顺序I/O
5)虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行insert、update和delete。因为更新表时,不仅要保存数据,还要保存一下索引文件(因为dml操作同样也会对索引文件进行修改)。

2、在哪些column上使用索引:
1)较频繁的作为查询条件字段应该创建索引
2)唯一性太差的字段不适合创建索引,尽管频繁作为查询条件,例如gender性别字段
3)更新非常频繁的字段不适合作为索引
4)不会出现在where子句中的字段不该创建索引

3、索引的创建原则:
1)最适合索引的列是出现在WHERE子句中的列,或连接子句中的列而不是出现在SELECT关键字后面的列。
2)索引列的基数越大,索引的效果越好。
3)对字符串进行索引,应该制定一个前缀长度,可以节省大量的索引空间
4)根据情况创建复合索引,复合索引可以提高查询效率
5)避免创建过多索引,索引会额外占用磁盘空间,降低写操作效率
6)主键尽可能选择比较短的数据类型,可以有效减少索引的磁盘占用,提高查询效率。

4、索引的使用场景

1)对于非常小的表,大部分情况下全表扫描效率更高
2)中到大型表,索引非常有效
3)特大型的表,建立和使用索引的代价将随之增长,可以使用分区技术来解决

四、索引类型

1、普通索引
是最基本的索引,它没有任何限制。它有以下几种创建方式:
1)直接创建索引

CREATE INDEX index_name ON `table` (`column`(length));

2)修改表结构的方式添加索引

ALTER TABLE `table_name` ADD INDEX index_name (`column`(length))

3)创建表的时候同时创建索引

CREATE TABLE `c` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) NOT NULL ,
    `content` text NULL ,
    PRIMARY KEY (`id`),
    INDEX title (`title`(5))
);

4)删除索引

DROP INDEX index_name ON `table`;

2、唯一索引
与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
1)创建唯一索引

CREATE UNIQUE INDEX index_name ON `table` (`column`(length));

2)修改表结构

ALTER TABLE `table_name` ADD UNIQUE index_name (`column`(length));

3)创建表的时候直接指定

CREATE TABLE `d` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) NOT NULL ,
    `content` text NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    UNIQUE title (`title`(5))
);

3、主键索引
是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。主键可以与外键构成参照完整性约束,防止数据不一致。

如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。

一般是在建表的时候同时创建主键索引:

CREATE TABLE `e` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) NOT NULL ,
    PRIMARY KEY (`id`)
);

4、组合索引
将多个列组合在一起创建索引。
只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。

ALTER TABLE `table` ADD INDEX name_city_age (`name`,`city`,`age`); 

5、全文索引
MySQL自带的全文索引只能用于MyISAM,对文本的内容进行分词,进行搜索。

主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。

1)创建表的时候添加全文索引

CREATE TABLE `article` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) NOT NULL ,
    `content` text NULL ,
    `time` int(10) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    FULLTEXT (content)
);

2)修改表结构添加全文索引

ALTER TABLE `article` ADD FULLTEXT index_content (`content`);

3)直接创建索引

CREATE FULLTEXT INDEX index_content ON `article` (`content`);

6、外键
用于建立和加强两个表数据之间的链接的一列或多列。外键约束主要用来维护两个表之间数据的一致性。简言之,表的外键就是另一表的主键,外键将两表联系起来。一般情况下,要删除一张表中的主键必须首先要确保其它表中的没有相同外键。比如,A表中的一个字段,是B表的主键,那他就可以是A表的外键。

外键的使用条件:
① 两个表必须是InnoDB表,MyISAM表暂时不支持外键
② 外键列必须建立了索引,MySQL 4.1.2以后的版本在建立外键时会自动创建索引,但如果在较早的版本则需要显式建立;
③ 外键关系的两个表的列必须是数据类型相似,也就是可以相互转换类型的列,比如int和tinyint可以,而int和char则不可以;

五、覆盖索引

索引确实是一种查找数据的高效方式,但是MySQL也可以使用索引来直接获取列的数据,这样就不再需要读取数据行。如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。

不是所有类型的索引都可以成为覆盖索引。覆盖索引必须要存储索引列的值,而哈希索引、空间索引和全文索引等都不存储索引列的值,所以MySQL只能使用B-Tree索引做覆盖索引。

六、注意事项

1、复合索引遵循前缀原则

# 如复合索引:KEY(a,b,c)
# 如下查询方式索引有效:
WHERE a = 1 and b = 2 and c = 3
WHERE a = 1 and b = 2
WHERE a = 1
# 如下方式索引无效:
WHERE b = 2 and c = 3
WHERE a = 1 and c = 3

2、以通配符%和_开头作查询时,MySQL不会使用索引。
3、column is null 可以使用索引
4、如果MySQL估计使用索引比全表扫描更慢,会放弃使用索引
5、如果or前的条件中的列有索引,后面的没有,索引都不会被用到
6、列类型是字符串,查询时一定要给值加引号,否则索引失效
7、不要在索引列上进行运算,这将导致索引失效,如:

SELECT * FROM table_name WHERE YEAR(column_name) < 2017;  # 索引失效
SELECT * FROM table_name WHERE id + 1 = 5;  # 索引失效

8、如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。如:

# 建立索引 key(last_name, first_name, dob)
WHERE last_name = 'Chan' and first_name Like 'J%' AND dob = '1234-12-21'
# 这个查询只能使用索引的前两列,因为这里LIKE是一个范围条件。

9、MySQL无法使用前缀索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖扫描。

七、索引底层的种类

1、B-tree索引

B-Tree是最常见的索引类型,所有值(被索引的列)都是排过序的,每个叶节点到跟节点距离相等。所以B-Tree适合用来查找某一范围内的数据,而且可以直接支持数据排序(ORDER BY)。

B-Tree在MyISAM里的形式和Innodb稍有不同:
MyISAM表数据文件和索引文件是分离的,索引文件仅保存数据记录的磁盘地址。InnoDB表数据文件本身就是主索引,叶节点data域保存了完整的数据记录。

可以使用B-Tree索引的查询类型如下:
1)全值匹配
2)匹配最左前缀
3)匹配列前缀
4)匹配范围值
5)精确匹配某一列并范围匹配另外一列
6)只访问索引的查询

因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的ORDER BY操作(按顺序查找)。一般来说,如果B-Tree可以按照某种方式查询到值,那么也可以按照这种方式用于排序。所以,如果ORDER BY子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。

2、Hash索引

在MySQL中,只有Memory引擎显式支持哈希索引。这也是Memory引擎表的默认索引类型,Memory引擎同时也支持B-Tree索引。

注意事项:
1)仅支持”=”,”IN”和”<=>”精确查询,不能使用范围查询:
由于Hash索引比较的是进行Hash运算之后的Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的Hash算法处理之后的Hash
2)不支持排序:
由于Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算
3)在任何时候都不能避免表扫描:
由于Hash索引比较的是进行Hash运算之后的Hash值,所以即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果
4)检索效率高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以Hash索引的查询效率要远高于B-Tree索引
5)只有Memory引擎支持显式的Hash索引,但是它的Hash是nonunique的,冲突太多时也会影响查找性能。Memory引擎默认的索引类型即是Hash索引,虽然它也支持B-Tree索引

3、R-Tree索引(空间数据索引)

R-Tree在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。

4、全文索引

全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。

相同的列上同时创建全文索引和基于值的B-tree索引不会有冲突,全文索引适用于MATCH AGAINST操作,而不是普通的WHERE条件操作。

八、经典实例

1、简单描述MySQL中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响?

[答案参考本文]

2、创建MySQl复合索引应该注意哪些事项?

[答案参考本文]

3、在空表News中,字段ID为自增主键,批量插入17条记录之后,发现最后三条数据有误,删除此三条记录后重启Mysql数据库,再重新插入一条记录,请问最后一条记录的ID值是多少?(5分)

一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysql的情况下),这条记录的id是18;但是如果重启(文中提到的)MySQL的话,这条记录的ID是15。因为InnoDB表只把自增主键的最大ID记录到内存中,所以重启数据库或者对表OPTIMIZE操作,都会使最大ID丢失。

但是,如果我们使用表的类型是MylSAM,那么这条记录的ID就是18。因为MylSAM表会把自增主键的最大ID记录到数据文件里面,重启MYSQL后,自增主键的最大ID也不会丢失。

参考

1、《高性能MySQL》 [美]Baron Scbwartz, Peter Zaitsev, Vadim Tkacbenko 著
2、 蹲厕所的熊MySQL索引原理以及慢查询优化

    原文作者:Nosee123
    原文地址: https://www.jianshu.com/p/f22fdd11d1cd
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞