MySQL索引

索引概述

介绍

索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足
特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构
上实现高级查找算法,这种数据结构就是索引。
若无索引的话,查找一个数据就需要全表扫描查找该数据,效率十分低下。

特点

在这里插入图片描述
劣势都可以忽略。磁盘空间占用无所谓,一般情况磁盘都是很便宜的。
增删改会降低效率主要是因为需要维护索引,往树中插入或删除节点都需要耗费时间。这点也可忽略,一般情况增删改的操作都很少,查的操作是最多的。

索引结构

mysql索引是在存储引擎层实现的,不同的存储引擎有不同的索引结构
在这里插入图片描述

在这里插入图片描述
默认都是B+树结构

B树

B-Tree,B树是一种多叉路衡查找树,相对于二叉树,B树每个节点可以有多个分支,即多叉。
以一颗最大度数(max-degree)为5(5阶)的b-tree为例,那这个B树每个节点最多存储4个key,5
个指针:(指针数为key+1,也就是阶数)
在这里插入图片描述
我们可以通过一个数据结构可视化的网站来简单演示一下。https://www.cs.usfca.edu/~gall
es/visualization/BTree.html

特点:

5阶的B树,每一个节点最多存储4个key,对应5个指针。
一旦节点存储的key数量到达5,就会裂变,中间元素向上分裂。
在B树中,非叶子节点和叶子节点都会存放数据。

B+树

B+Tree是B-Tree的变种,我们以一颗最大度数(max-degree)为4(4阶)的b+tree为例,来看一
下其结构示意图:
在这里插入图片描述
绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据。
红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据。

区别

最终我们看到,B+Tree 与 B-Tree相比,主要有以下三点区别:
1、所有的数据都会出现在叶子节点。
2、叶子节点形成一个单向链表。
3、非叶子节点仅仅起到索引数据作用,具体的数据都是在叶子节点存放的。
上述我们所看到的结构是标准的B+Tree的数据结构,接下来,我们再来看看MySQL中优化之后的
B+Tree。
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点
的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能,利于排序。
在这里插入图片描述

Hash

和java中的哈希表一样,解决哈希冲突的方式是用链表形式。
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在
hash表中。
特点
A. Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,< ,…)
B. 无法利用索引完成排序操作
C. 查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,效率通常要高于B+tree索 引
存储引擎支持
在MySQL中,支持hash索引的是Memory存储引擎。 而InnoDB中具有自适应hash功能,hash索引是
InnoDB存储引擎根据B+Tree索引在指定条件下自动构建的。

思考

为什么InnoDB存储引擎选择使用B+tree索引结构?
1、相比较于二叉树,层级更少,搜索效率高;
2、对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储
的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
3、相对Hash索引,B+tree支持范围匹配及排序操作;

分类

在MySQL数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引。
在这里插入图片描述

聚集索引和二级索引

在InnoDB中,索引有可分为两种
在这里插入图片描述
聚集索引选取规则:
1、若存在主键,主键就是聚集索引
2、如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。
3、如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索
引。
聚集索引和二级索引具体结构如下
在这里插入图片描述
聚集索引的叶子节点下挂的是这一行的数据 。
二级索引的叶子节点下挂的是该字段值对应的主键值。

当执行 select * from user where name = ‘Arm’,具体执行操作步骤如下
在这里插入图片描述
1、 由于是根据name字段进行查询,所以先根据name='Arm’到name字段的二级索引中进行匹配查
找。但是在二级索引中只能查找到 Arm 对应的主键值 10。
2、由于查询返回的数据是*,所以此时,还需要根据主键值10,到聚集索引中查找10对应的记录,最
终找到10对应的行row。
3、最终拿到这一行的数据,直接返回即可。

思考

以下两条SQL语句,那个执行效率高? 为什么?
A. select * from user where id = 10 ;
B. select * from user where name = 'Arm' ;
备注: id为主键,name字段创建的有索引;

A 语句的执行性能要高于B 语句。
因为A语句直接走聚集索引,直接返回数据。 而B语句需要先查询name字段的二级索引,然
后再查询聚集索引,也就是需要进行回表查询。

索引语法

创建索引

create (unique| fulltext) INDEX name ON table_name(index_col_name...);

unique 创建唯一索引 fulltext创建全文索引 不加代表常规索引
一个索引可以关联多个字段,若一个索引关联一个字段称为单列索引,关联多个字段称为联合索引或组合索引.

查看索引

show index from table_name;

删除索引

drop index index_name on table_name;

性能分析

SQL执行频率
MySQL 客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信
息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:

SHOW GLOBAL STATUS LIKE 'Com_______';
Com_delete: 删除次数 
Com_insert: 插入次数
Com_select: 查询次数
Com_update: 更新次数

通过上述指令,我们可以查看到当前数据库到底是以查询为主,还是以增删改为主,从而为数据
库优化提供参考依据。 如果是以增删改为主,我们可以考虑不对其进行索引的优化。 如果是以
查询为主,那么就要考虑对数据库的索引进行优化了。

慢查询日志(windows版)

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有
SQL语句的日志。
MySQL的慢查询日志默认没有开启,我们可以查看一下系统变量 slow_query_log。
在这里插入图片描述
打开慢查询日志 show variables like ‘slow_query%’
set global slow_query_log=1;
再次查看
在这里插入图片描述
设置慢查询时间
set global long_query_time=4;
再次查看慢查询时间,注意此时要用命令show global variables like ‘%long_query_time%’;
在这里插入图片描述

profile

能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling
参数,能够看到当前MySQL是否支持profile操作:

SELECT @@have_profiling ;

在这里插入图片描述
在这里插入图片描述
查看每条语句的耗时情况
在这里插入图片描述

explain执行计划

EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行
过程中表如何连接和连接的顺序。

EXPLAIN 或者 DESC select语句; 

在这里插入图片描述
其中各个字段的含义:
在这里插入图片描述

索引失效

1、最左前缀法则

如果索引了多列(联合索引),就要遵守最左前缀法则,否则索引会失效。最左前缀法则指的是:查询从索引最左列开始,不能跳过索引中的某一列,否则索引会部分失效(后面的索引会失效)
查看tb_user表的索引

show index from tb_user;

在这里插入图片描述
表中有联合索引在这里插入图片描述
tb_user中列名排列顺序为在这里插入图片描述

profession 、age和status字段,profession在最左边,使用索引查询时必须要有professsion字段。不用profession字段时,索引将会失效。

explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';

在这里插入图片描述
若无profession字段,可以看到没有使用联合索引进行查询
在这里插入图片描述
同理,如果只使用profession字段和 status字段,跳过了age字段,则status查询将不会使用到索引。
注意:只要查询语句中有对应字段即可,位置可随意排列,profession不必放在第一个查询位置

2、使用范围查询 (<,>),范围查询右侧的列索引失效,使用<=或者>=不会失效。

3、 索引列运算也会失效

  要使用索引查询时,不可以对查询的列进行运算,否则索引失效。

explain select * from tb_user where substring(phone,10,2) = '15';

使用字符串切割函数,phone列的索引就会失效

4、字符串不加引号,索引失效

5、使用头部模糊匹配,索引失效

explain select * from tb_user where profession like '软件%'; 
explain select * from tb_user where profession like '%工程'; 
explain select * from tb_user where profession like '%工%';

第一条语句索引执行,其余两条失效

6、用or连接条件,or两侧的条件中都要有索引,只有前面的有后面没有,索引失效

7、 数据分布影响

  mysql会对查询进行评估,若使用索引比全表查询更慢,则不适用索引。

SQL 提示

  SQL提示是优化数据库的一个重要手段,就是再SQL语句中加入一些人为的提示达到优化的目的。
可以为联合索引中的列再创建单列索引

create index idx_user_pro on tb_user(profession);

  此时,profession列有两个索引(联合索引和单列索引),如果查询profession时,mysql会随机选择一个索引执行。此时可以使用命令建议mysql执行哪个索引。

1use index:建议MYSQL使用哪个索引(只是建议,mysql内部还会进行评,不一定采纳你的建议)
explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';

2ignore index:忽略指定索引
explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工 程';

3force index:强制使用索引
explain select * from tb_user force index(idx_user_pro) where profession = '软件工 程';

覆盖索引

  尽量使用覆盖索引,减少select * 那么什么是覆盖索引呢? 覆盖索引是指 :查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到 。
举例:id为聚集索引,name为二级索引,现在我执行一个语句

select id,name from table where name = 'A';

此时它不会进行回表查询,直接在二级索引中就可以查询需要的全部信息,直接返回即可。不需要先在二级索引中查到id,再根据id去聚集索引中查找对应的所有字段。

explain select id, profession from tb_user where profession = '软件工程' and age = 31 and status = '0' ;
explain select id,profession,age, status from tb_user where profession = '软件工程' and age = 31 and status = '0' ; 
explain select id,profession,age, status, name from tb_user where profession = '软 件工程' and age = 31 and status = '0' ; 
explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';

执行计划中Extra字段中有了值
在这里插入图片描述
在这里插入图片描述
  因为,在tb_user表中有一个联合索引 idx_user_pro_age_sta,该索引关联了三个字段profession、age、status,而这个索引也是一个二级索引,所以叶子节点下面挂的是这一行的主键id。 所以当我们查询返回的数据在 id、profession、age、status 之中,则直接走二级索引直接返回数据了。
  如果超出这个范围,就需要拿到主键id,再去扫描聚集索引,再获取额外的数据了,这个过程就是回表。 而我们如果一直使用select * 查询返回所有字段值,很容易就会造成回表查询(除非是根据主键查询,此时只会扫描聚集索引)。

前缀索引

   当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
   比如为某些文章建立索引,一篇文章的长度可是很长的,不可能用文章所有的字符建立一个索引,是不现实的,多以要截取文章前面一小部分当做索引。
  可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,
索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

比如要为email建立索引,先查看比值
select count(distinct email) / count(*) from tb_user;
然后选择一个合适的比值,截取适当的长度当做索引。 
select count(distinct substring(email,1,5)) / count(*) from tb_user ;

前缀索引的查询流程

在这里插入图片描述

   上面以email的前5个字符建立了索引,当执行查询语句

select * from tb_user where email='1779990@sina.com';

会先截取前五个字符’17799’在索引中查找对应的id
在这里插入图片描述
查找到对应的聚集索引为3,就去聚集索引中查找信息
在这里插入图片描述
查找到以后,对比该email的值与查询语句中的email值是否相同,若对比结果发现不相同,根据B+树的存储结构叶子节点是双向链表,所以就查链表的下一个位置,id为4,再接着去聚集索引中查找id=4的所有信息,对比该email与需要查询的email='1779990@sina.com’是否相同,发现此时相同,即返回改行的所有数据。

单列索引与联合索引

单列索引:索引只包含单个列
联合索引:索引包含了多个列
若查询的条件中,字段都为单列索引,这样会进行回表查询从而影响sql性能。

phone和name都是单列索引
explain select id,phone,name from tb_user where phone = '17799990010' and name = '韩信';

mysql只会根据其中一个索引来进行查询,查询到id后再回表查询另一个字段对比是否相同。
在这里插入图片描述
此时创建phone和name的联合索引

create unique index idx_user_phone_name on tb_user(phone,name);

此时再根据phone和 name 查询则不会进行回表查询(需要指定使用的索引)
在这里插入图片描述
构建联合索引时,B+树的创建默认根据其中一个字段进行排序,若该字段相同则再根据另一个字段排序

索引设计原则

1). 针对于数据量较大,且查询比较频繁的表建立索引。
2). 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索
引。
3). 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
4). 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
5). 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,
避免回表,提高查询效率。
6). 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增
删改的效率。
create unique index idx_user_phone_name on tb_user(phone,name); 1
7). 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含
NULL值时,它可以更好地确定哪个索引最有效地用于查询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值