Mysql索引详解

Mysql索引

要求:看到目录可以有话说

一阶段:对索引定义,索引分类,索引使用情形有所了解

二阶段:sql优化,

三阶段:索引数据结构

  1. 什么是索引
  2. 索引有什么用
  3. 如何创建索引
  4. 如何查看索引
  5. 索引的分类
  6. 索引的数据结构浅析
  7. 什么情况下需要索引
  8. 什么情况下不会走索引
  9. 索引相关补充
  10. 索引常见使用情形
  11. 慢查询日志

一、什么是索引

索引是存储引擎用于快速查找记录的一种数据结构,通过合理的使用数据库索引可以大大提高系统的访问性能。

理解:

mysql属于关系型数据库(二维数据库,其实就是以行和列的行时存储数据),我们一般查询行的数据,按照列的条件。当条件越来越多,关系越来越复杂时,效率会极低,索引(这种数据结构)帮助我们快速定位我们需要的数据,或者优化查询的方式。

二、索引有什么用

  • 大大减轻了服务器需要扫描的数据量,从而提高了数据的检索速度
  • 帮助服务器避免排序和临时表
  • 可以将随机I/O变为顺序I/O

三、如何创建索引

1. 建表后创建索引

创建普通索引:

CREATE INDEX indexName ON tableName (columnName(length));

创建唯一索引:

create unique index index_name on 表名(列名);

创建全文索引:

create fulltext index index_name  on 表名(列名);

创建多列索引:

create index index_name_no on 表名(列名,列名)

以修改表的方式创建索引:可以在 INDEX 前面添加限定条件,使其成为普通索引,唯一索引,全文索引等。

ALTER TABLE tableName ADD INDEX indexName(columnName);
2. 建表时创建索引
CREATE TABLE tableName(  
  id INT NOT NULL,   
  columnName  columnType,
  INDEX [indexName] (columnName(length))  
);

可以在 第四行 INDEX 前面添加限定条件,使其成为普通索引,唯一索引,全文索引等。

四、如何查看索引

1. 查看索引
show index from tableName;

相关参数:

Table表名Non_unique0:索引不能包含重复此1:可以包含重复此
Key_name索引名称Seq_In_index索引中的列序列号
Column_name列名称Collation列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)
Cardinality索引中唯一值的数目的估计值。Sub_part如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。
Packed指示关键字如何被压缩。如果没有被压缩,则为NULL。Null如果列含有NULL,则含有YES。如果没有,则该列含有NO。
Index_type用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。Comment多种评注。
2. 如何查看sql语句是否使用索引

在sql语句前面 加上 EXPLAIN

理解:

key就是我们所使用的索引的第一个字段,如果有值,则走了索引。

解析:

  • select_type

可以看id的执行实例,总共有以下几种类型:

SIMPLE: 表示此查询不包含 UNION 查询或子查询

PRIMARY: 表示此查询是最外层的查询

SUBQUERY: 子查询中的第一个 SELECT

UNION: 表示此查询是 UNION 的第二或随后的查询

DEPENDENT UNION: UNION 中的第二个或后面的查询语句, 取决于外面的查询

UNION RESULT, UNION 的结果

DEPENDENT SUBQUERY: 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果.

DERIVED:衍生,表示导出表的SELECT(FROM子句的子查询)

需要注意一个最重要的的 type 的信息很明显地体现出是否用到了索引:

  • type 结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到 range 级别,最好能达到 ref 级别,否则就可能出现性能问题。possible_keys: SQL查询时用到的索引。

  • possible_keys: SQL查询时用到的索引。

可以看到,没加索引时,possible_keys 的值为 NULL,加了索引后的值为 address,即用到了索引address(索引默认为(column_list)中的第一个列的名字).

  • key 显示SQL实际决定查询结果使用的键(索引)。如果没有使用索引,值为NULL

可以看到,没加索引时,key 的值为 NULL,加了索引后的值为 address,即决定查询结果用到了索引address

  • rows 显示MySQL认为它执行查询时必须检查的行数

可以看到,没加索引时,rows 的值为17,即数据表student中所有数据,说明没加索引时的SQL查询是全表扫描;

五、索引的分类

索引根据底层实现可分为B-Tree索引和哈希索引,大部分时候我们使用的都是B-Tree索引,因为它良好的性能和特性更适合于构建高并发系统。

根据索引的存储方式来划分,索引可以分为聚簇索引非聚簇索引。聚簇索引的特点是叶子节点包含了完整的记录行,而非聚簇索引的叶子节点只有所以字段和主键ID。

根据聚簇索引和非聚簇索引还能继续下分还能分为普通索引、覆盖索引、唯一索引以及联合索引等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qC3lV1ZZ-1659713383499)(Mysql%E7%B4%A2%E5%BC%95.assets/image-20220803214649573.png)]

‘’

1. 普通索引

最基本的索引,它没有任何限制,用于加速查询。

2. 唯一索引

索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

3. 主键索引

是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。

4. 组合索引

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

最左前缀原则:
mysql建立多列索引(联合索引)有最左前缀的原则,即最左优先,如:

如果有一个2列的索引(col1,col2),则已经对(col1)、(col1,col2)上建立了索引;
如果有一个3列索引(col1,col2,col3),则已经对(col1)、(col1,col2)、(col1,col2,col3)上建立了索引;

最左前缀匹配原则

当对多列创建索引后,并不是只要包含了创建索引的列就能使用索引,索引的使用要遵循最左前缀匹配原则。

假设对列(A, B, C)创建索引,那么只有以下场景能使用索引:

  1. 对列(A, B, C)/(A, C)或者(A, B)进行查询会匹配索引,对(C, A)或者(B, C)来说不能使用索引。
  2. 通配符只能使用LIKE 'val%'形式,不能使用LIKE '%VAL%',后者会导致全表扫描。
  3. 索引列不能进行运算,例如WHERE A + 1 = 5这种场景会导致索引失效。
  4. 索引列不能包含范围值查询,如LIKE/BETWEEN/>/<等都会导致后面的列无法匹配索引。
  5. 索引列不能包含有NULL值。
5. 全文索引

主要用来查找文本中的关键字,而不是直接与索引中的值相比较。

(在MySQL 5.6版本以前,只有MyISAM存储引擎支持全文引擎。在5.6版本中,InnoDB加入了对全文索引的支持,但是不支持中文全文索引.在5.7.6版本,MySQL内置了ngram全文解析器,用来支持亚洲语种的分词。主要用来利用关键词查询文本,不是MySQL的主要面向场景,使用较少,这里就不展开讨论了。)

fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。

fulltext索引配合match against操作使用,而不是一般的where语句加like。

它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。

全文索引允许在索引列中插入重复值和空值。

关于match against:

mysql 4.x以上提供了全⽂检索⽀持 MATCH ……AGAINST 模式(不区分⼤⼩写)

建⽴全⽂索引的表的存储引擎类型必须为MyISAM

问题是match against对中⽂模糊搜索⽀持不是太好

SELECT * FROM articles WHERE MATCH (title,body) AGAINST (‘database’);

注意 MATCH (title,body) ⾥⾯的值必须是前⾯建⽴全⽂索引的两个字段不能少。

mysql 默认⽀持全⽂检索的字符长度是4,可以⽤SHOW VARIABLES LIKE ‘ft_min_word_len’ 来查看指定的字符长度,也可以在mysql配置⽂件my.ini 更改最⼩字符长度,⽅法是在my.ini 增加⼀⾏⽐如:ft_min_word_len = 2,改完后重启mysql即可。

6. 聚簇索引

聚簇索引也叫聚集索引,它实际上并不是一种单独的索引类型,而是一种数据存储方式,聚簇索引的叶子节点保存了一行记录的所有列信息。也就是说,聚簇索引的叶子节点中,包含了一个完整的记录行。

非聚簇索引也叫辅助索引、普通索引,它的叶子节点只包含一个主键值,通过非聚簇索引查找记录要先找到主键,然后通过主键再到聚簇索引中找到对应的记录行,这个过程被称为回表

聚集索引决定数据在物理磁盘上的物理排序,一个表只能有一个聚集索引,如果定义了主键,那么InnoDB会通过主键来聚集数据,如果没有定义主键,InnoDB会选择一个唯一的非空索引代替,如果没有唯一的非空索引,InnoDB会隐式定义一个主键来作为聚集索引。

聚集索引可以很大程度的提高访问速度,因为聚集索引将索引和行数据保存在了同一个B-Tree中,所以找到了索引也就相应的找到了对应的行数据,但在使用聚集索引的时候需注意避免随机的聚集索引(一般指主键值不连续,且分布范围不均匀),如使用UUID来作为聚集索引性能会很差,因为UUID值的不连续会导致增加很多的索引碎片和随机I/O,最终导致查询的性能急剧下降。

理解:

在Innodb引擎中,主键就是聚簇索引,其他索引一般为非聚簇索引。当使用非聚簇索引时,非聚簇索引指向聚簇索引的主键,相当于查询了两次索引,效率较低。

7. 覆盖索引

非聚簇索引中因为不含有完整的数据信息,查找完整的数据记录需要回表,所以一次查询操作实际上要做两次索引查询。而如果所有的索引查询都要经过两次才能查到,那么肯定会引起效率下降,毕竟能少查一次就少查一次。

8. 空间索引

空间索引是对空间数据类型GEOMETRY、POINT、LINESTRING和POLYGON字段上建立的索引,只有MyISAM上可以定义

9. 前缀索引

前缀索引顾名思义,定义字符串的⼀部分当做索引,⽽不是把整个字符串当做索引。默认地,如果你创建索引的语句不指定前缀长度,那么索引就会包含整个字符串。

1.创建email列的普通索引应该是:alter table T add index idx_email1(email)

2.前缀索引的创建规则为:alter table table T add index idx_email2(email(6))

补充:下表是 MySQL 常见的存储引擎 InnoDB,MyISAM 和 Memory 分别支持的索引类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ont6YRVF-1659713383499)(Mysql%E7%B4%A2%E5%BC%95.assets/image-20220803221317259.png)]

索引InnoDB引擎MyISAM引擎Memory引擎
B TREE索引支持支持支持
HASH索引不支持不支持支持
R-tree索引不支持支持不支持
Full-text5.6版本后支持支持不支持
10.其他索引

https://mp.weixin.qq.com/s?src=11&timestamp=1659533384&ver=3960&signature=Qco3YE4R9dmRxZe5nKaogPB4ptAiO24fyNJilcV8YpaHWs6sIU-9M0j*39A4wazXbs5ykyNFSoJfOmYxGduzIyXITso4HI27RDxbwPb-tWXGbTpNejKSWUU4M8DcBL&new=1

倒叙索引

函数索引

表达式索引

不可见索引

六、索引的数据结构浅析

本节参考:https://blog.csdn.net/weixin_35794878/article/details/122609218

1. B+tree

二叉树具有以下性质:左子树的键值小于根的键值,右子树的键值大于根的键值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXv4xtED-1659713383500)(Mysql%E7%B4%A2%E5%BC%95.assets/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAamVycnlfZHl5,size_13,color_FFFFFF,t_70,g_se,x_16-16595361762455.png)]

平衡二叉树(AVL树):

平衡二叉树(AVL树)在符合二叉查找树的条件下,还满足任何节点的两个子树的高度最大差为1,就达到了所谓的平衡。需要通过一系列复杂的旋转达到平衡。

B-tree 平衡多路查找树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49j7T7hi-1659713383500)(Mysql%E7%B4%A2%E5%BC%95.assets/image-20220803222013601.png)]

模拟查找关键字29的过程:

根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
比较关键字29在区间(17,35),找到磁盘块1的指针P2。
根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
比较关键字29在区间(26,30),找到磁盘块3的指针P2。
根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
在磁盘块8中的关键字列表中找到关键字29。

B+tree

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kLArnhr-1659713383500)(Mysql%E7%B4%A2%E5%BC%95.assets/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAamVycnlfZHl5,size_20,color_FFFFFF,t_70,g_se,x_16.png)]

在B-Tree的基础上优化了:

1、节点上只存储键值,不存储数据,这样一来,在有限的节点空间(页空间)内就可以存放更多的键值、指针;

2、所有数据都放在叶子节点中,所有叶子节点之间有链指针(双向循环列表),便于范围查找,也便于排序。

数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondary index)。上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。

七、什么情况需要索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8lq6PW3R-1659713383501)(Mysql%E7%B4%A2%E5%BC%95.assets/image-20220803214110527.png)]

索引设计原则

  • 对查询次数频次较高,且数据量较大的表建立索引;
  • 索引字段的选择,最佳候选列应当从where子句的条件中提取,如果where子句中的组合比较多,那么应当挑选最常用、过滤效果最好的列的组合;
  • 使用唯一索引,区分度高、使用效率越高;
  • 索引可以有效的提升查询数据的效率 , 但索引数量并不是多多益善 , 索引越多 , 维护索引的代价自然也就水涨船高 . 对于插入, 更新, 删除 等 DML 操作比较繁琐的表来说 , 索引过多 , 会引入相当高的维护代价 , 降低 DML 操作的效率 , 增加相应操作的时间消耗 , 另外索引过多的话 , MySQL也会犯 选择困难症 , 虽然最终仍然会找到一个可用的索引 , 但无疑提高了索引的代价 .
  • 使用段索引 , 索引创建之后也是使用硬盘来存储的 , 因此提高索引访问的 I/O 效率 , 也可以跳高总体的访问效率 . 假如构成索引的字段 总长度比较短 , 那么在给定大小的存储块内 , 可以存储更多的索引值 , 相应的可以有效地提升MySQL访问索引的 I/O 效率.
  • 利用最左前缀的原则 , N个列组合而成的组合索引 , 那么相当于是创建了N 个索引 。如果查询时where 子句使用了组成该索引的前几个字段 , 那么这条查询SQL可以利用组合索引来提升查询效率

八、什么情况不会走索引

1、条件字段原因

  • 单字段有索引,WHERE条件使用多字段(含带索引的字段),例如 SELECT * FROM student WHERE name =‘张三’ AND addr = '北京市’语句,如果name有索引而addr没索引,那么SQL语句不会使用索引。
  • 多字段索引,违反最佳左前缀原则。例如,student表如果建立了(name,addr,age)这样的索引,WHERE后的第一个查询条件一定要是name,索引才会生效。(第二个)

2、<>、NOT、in、not exists

当查询条件为等值或范围查询时,索引可以根据查询条件去找对应的条目。否则,索引定位困难(结合我们查字典的例子去理解),执行计划此时可能更倾向于全表扫描,这类的查询条件有:<>、NOT、in、not exists

3、查询条件中使用OR

如果条件中有or,即使其中有条件带索引也不会使用(因此SQL语句中要尽量避免使用OR)。要想使用OR,又想让索引生效,只能将OR条件中的每个列都加上索引。

4、查询条件使用LIKE通配符

SQL语句中,使用后置通配符会走索引,例如查询姓张的学生(SELECT * FROM student WHERE name LIKE ‘张%’),而前置通配符(SELECT * FROM student WHERE name LIKE ‘%东’)会导致索引失效而进行全表扫描。

如果where查询条件中有某个列的范围查询,则其右边的所有列都无法使用索引优化查询,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YTyBGroQ-1659713383501)(Mysql%E7%B4%A2%E5%BC%95.assets/image-20220803224610796.png)]

5、索引列上做操作(计算,函数,(自动或者手动)类型转换)

有以下几种例子:

  • 在索引列上使用函数:例如select * from student where upper(name)=‘ZHANGFEI’;会导致索引失效,而select * from student where name=upper(‘ZHANGFEI’);是会使用索引的。
  • 在索引列上计算:例如select * from student where age-1=17;

6、在索引列上使用mysql的内置函数,索引失效

例如,SELECT * FROM student WHERE create_time

7、索引列数据类型不匹配

例如,如果age字段有索引且类型为字符串(一般不会这么定义,此处只是举例)但条件值为非字符串,索引失效,例如SELECT * FROM student WHERE age=18会导致索引失效。

字段类型是字符串,where时一定用引号括起来,否则索引失效

8、索引列使用IS NOT NULL或者IS NULL可能会导致无法使用索引

B-tree索引IS NULL不会使用索引,IS NOT NULL会使用,位图索引IS NULL、IS NOT NULL都会使用索引。

九、索引相关补充

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

注:索引不是万能的!索引可以加快数据检索操作,但会使数据修改操作变慢。每修改数据记录,索引就必须刷新一次。为了在某种程序上弥补这一缺陷,许 多SQL命令都有一个DELAY_KEY_WRITE项。这个选项的作用是暂时制止MySQL在该命令每插入一条新记录和每修改一条现有之后立刻对索引进 行刷新,对索引的刷新将等到全部记录插入/修改完毕之后再进行。在需要把许多新记录插入某个数据表的场合,DELAY_KEY_WRITE选项的作用将非 常明显。[2]另外,索引还会在硬盘上占用相当大的空间。因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内 容,为它建立索引就没有太大的实际效果。

1. 我们是否可以无限制的添加索引?

从理论上讲,完全可以为数据表里的每个字段分别建一个索引,但MySQL把同一个数据表里的索引总数限制为16个。

扩展:为什么限制为16个?

2. 索引有什么弊端
  • 创建和维护索引组要耗费时间,并且随着数据量的增加所耗费的时间也会增加。
  • 索引需要占磁盘空间,除了数据表占数据空间以外,每一个索引还要占一定的物理空间。
  • 当对表中的数据进行增加、删除和修改的时候,索引也要动态维护,这样就降低了数据的维护速度。
3. 如何使用索引来排序

1、ORDER BY子句后的列顺序要与组合索引的列顺序一致,且所有排序列的排序方向(正序/倒序)需一致

2、所查询的字段值需要包含在索引列中,及满足覆盖索引

4. 索引使用时需要注意什么
全值匹配

全值匹配指的是和索引中的所有列进行匹配,如:以上面创建的索引为例,在where条件后可同时查询(user_name,city,age)为条件的数据。

注:与where后查询条件的顺序无关,这里是很多同学容易误解的一个地方

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pstXrzN4-1659713383502)(Mysql%E7%B4%A2%E5%BC%95.assets/640.png)]

匹配最左前缀

匹配最左前缀是指优先匹配最左索引列,如:上面创建的索引可用于查询条件为:(user_name )、(user_name, city)、(user_name , city , age)

注:满足最左前缀查询条件的顺序与索引列的顺序无关,如:(city, user_name)、(age, city, user_name)

匹配列前

指匹配列值的开头部分,如:查询用户名以feinik开头的所有用户

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N7f7nuSf-1659713383503)(Mysql%E7%B4%A2%E5%BC%95.assets/640-16595377451099.png)]

匹配范围值

如:查询用户名以feinik开头的所有用户,这里使用了索引的第一列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCRPoeTU-1659713383503)(Mysql%E7%B4%A2%E5%BC%95.assets/640-165953774510910.png)]

5. 为什么MySQL使用B+树?

一般来说,索引本身也很大,所以不会存储在内存中,往往是以索引文件的形式存储在磁盘上的,因此索引在查找的过程中也是需要到IO消耗的。相较于B-树,

  • 首先B+树的空间利用率更高(非叶节点没有data域),可减少IO次数,磁盘读写所耗费的代价更低;
  • B+树的查询效率更加地稳定,B树搜索在非叶子节点还是叶子节点结束都有可能,约靠近根节点,查找效率越快;而B+树无论查找的是什么数据,最终都需要从根节点一直走向叶节点,所有查找所经过的次数都是一样的;
  • B+树能同时支持随机检索和顺序检索,而B树只适合随机检索,顺序检索的效率比B+树低;
  • 增删文件时,B+树的效率更高,因为所有的data都在叶子节点中,而B树删减节点时还需要分裂,中间节点向上等操作;
6. 简单介绍一下最左匹配原则?是什么和应用场景

参考:https://blog.csdn.net/weixin_47162914/article/details/123793589

最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。

如果不连续,则只有前面连续的字段能够匹配索引

所以联合索引最左列是绝对有序的,其他字段无序。

探究:
  1. 组合索引中使用范围查询,在满足最左前缀的原则下是否会走索引

  2. 使用select * 是否会走索引

  3. 如果是组合索引,条件中的字段顺序必须和索引顺序保持一致吗

  4. 如果a字段是一个普通索引,同时也存在于(a,b,c)联合索引中,这时如果查询条件中存在(c,a)字段,会走索引吗,如果走,会走哪个索引?

7. 误区总结
  • 组合索引(联合索引),遵守最左匹配原则,但与where条件无关。其实就是where条件中要包含最左条件(索引的第一个字段)。
  • 索引失效的条件:
    1. 不符合最左匹配原则,即查询条件中不包含组合索引中的第一个字段
    2. 上述条件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值