MySQL-索引

发布于:2025-03-19 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

索引概述

介绍

演示

优缺点

索引结构

主要的索引结构

B+Tree

Hash

思考

索引的分类

索引语法 

查看索引

创建索引 

删除索引

SQL性能分析

查看执行频次

慢查询日志

profile详情

查看每一条SQL语句的耗时情况

查看指定query_id的SQL语句每个阶段的耗时情况

查看指定query_id的SQL语句的CPU的使用情况

explain执行计划

查看执行计划

执行计划各字段义

ID

SELECT_Type

TYPE

possible_key

KEY

KEY_len

rows

filtered

Extra

索引使用

最左前缀法则

索引失效情况

范围索引

索引列运算

字符串索引

模糊匹配

or连接的条件

数据分布影响

SQL提示

覆盖索引

前缀索引

语法

索引的选择性

查询流程

思考题 

索引设计原则

​编辑


索引概述

介绍

索引是一种能够帮数据库高效查询数据数据结构。数据库不仅仅需要维护,还需要维护满足特定查找算法的数据结构

演示

select *from user where age = 45;

使用约束函数进行查找时,当找到第一个45时,查询并不会停止,因为数据库并不知道,表中是不只有一个45,所以他要进行全表查询,效率极低

所以数据库确实需要一种数据结构来进行高效的查询,现在我们试着使用数据结构中的二叉树来储存数据,并且比较一下查找效率:

优缺点

优点:

  1. 使数据的查询更加高效,降低的数据库的IO成本
  2. 通过索引进行排序,降低了排序的成本,降低了CPU的消耗

缺点:

  1. 索引列是要占用空间的
  2. 降低了更新表的速度,要进行INSERT、UPDATE等(也就是不太方便维护)

索引结构

索引是在储存引擎层实现的,不同的储存引擎有不同的结构。

主要的索引结构

  • BTree索引:最常见的索引,大部分的储存引擎都支持B+Tree索引
  • Hash索引:他的底层数据结构使用Hash实现的,但是只有精确匹配的索引列才能查询有效,不支持范围查询
  • R+Tree索引(空间索引):是MyISAMD的特有的索引结构,通常用于地理数据类型,不经常使用
  • Full-Text(全文索引):是引种建立倒排索引,快速快速匹配文档的方式

 在分析索引结构之前,我们先回顾一下数据结构中的二叉树:

如图是一个二叉排序树,他确实能提高查询的速率,但是我们知道,当插入的数据有序时,二叉树就会退化成为链表:

 不难看出,当输入数据有序时,二叉树会退化成链表,导致查找效率大大降低;并且,当数据量特别大时,二叉树的层数会很深。

所以才有了后面的红黑树(RBT):

红黑树可以解决退化成链表的情况,但是还是不能避免数据量大时,层数太深的问题。

BTree

BTree又称多路平衡查找树:

每个节点可以有多个子节点(事先规定好最大度数)

假设max-degree=5,就说明每个节点最多存4个key,每个节点会有5个指针

五个指针分别指向,值分别在该节点4个key之间的区间,大家可以使用这个数据结构可视化网站进行体验一下Btree的构建过程。

B+Tree

B+Tree与BTree优有点像,但是不同点在于当一个节点值满后,向上分裂时,B+Tree的节点始终会保留在叶子结点上,最终叶子结点会形成一个链表。

B+Tree

数据库索引中的B+Tree在经典数据结构的B+Tree的基础上进行了优化,加上一个指向相邻叶子结点的指针,从而形成了带有顺序指针的B+Tree,提高了访问速率。

非叶子结点其实并不储存数据,只是起到索引的作用

Hash

Hash的逻辑和经典数据结构中的处理相似,通过Hasn算法,将键值映射成Hash值,然后储存到Hash表对应的槽位中,而当多个键值对应一个槽位时,就需要进行Hash冲突处理,也就是后面接链表解决

Hash的特点:

  • Hash只能进行对等比较(=、IN),不能进行范围查询(<、>、BETWEEN)
  • 不能通过索引完成排序操作
  • 能够提高查询效率

一般来说,Memory储存引擎是支持Hasn索引结构的,但是Innodb储存引擎,会根据B+Tree的索引在指定条件下转换成为Hash索引。 

思考

为什么InnoDB要使用B+Tree索引结构呢?而不是红黑树或者BTree又或是Hasn?

  1. 因为B+Tree相较于红黑树而言,层级更少,查找效率更高
  2. 相较于BTree而言,B+Tree只有叶子结点储存数据,非叶子结点不储存数据,只是起到索引作用。而我们知道,每一个结点在数据库中使用块/页储存的,这样不储存数据的话,能够存放的结点和指针就会更多,能够减少层数,提高查询效率 

索引的分类

在InnoDB储存引擎中,根据索引的储存形式,将索引分为了:

注意:

  • 当表中有主键时,主键索引就是聚焦索引
  • 当没有主键时,将第一个唯一索引作为聚焦索引
  • 没有主键,切没有合适的唯一索引时,InnoDB会自动生成ROWID作为隐藏的聚焦索引

下面使用一个例子来比较清晰地解释下聚焦索引和二级索引:

这张表中有三个字段,其中id是主键,主键将会作为聚焦索引:

聚焦索引的叶子结点是一行数据(包括所有字段),而不难想到,二级索引的叶子结点肯定就不是存放行数据的所有字段,因为这样就会有数据冗余。下面是二级索引:

它存放的是id号,所以使用二级索引查找信息时,应该是先通过name,找到id号,然后在通过聚集索引查找这一行所有字段的数据,这个过程叫做回表查询

索引语法 

查看索引

show index from users;

创建索引 

CREATR  [ 约束 ]  INDEX  索引名  ON  表名(字段名);

create unique index idx_user_contact on users(user_contact);

删除索引

drop index idx_user_name on users;

SQL性能分析

查看执行频次

查询数据库增、删、改、查命令的频次:

show global status like 'Com_______';

根据系统的命令执行频次,来选择对那些操作进行优化。 

慢查询日志

查看是否开启慢查询日志

show variables like 'slow_query_log';

如果没有开启,需要在mysql的配置文件中添加配置(注意是在Linux中输入语句,不是mysql)

vim /etc/my.cnf

 然后在配置文件中添加下面两条语句:

#开启mysql慢查询日志开关
slow_query_log=1

#设置慢查询的时间为2秒
long_query_time=2

然后我们可以看一下,日志中的信息:

先进入文件所在位置

cd /var/lib/mysql

然后查看一下文件内容 

cat localhost-slow.log

显示信息如下 

这里要注意的是,在配置文件时,明确了慢查询的时间,也就是说只有查询时间大于这个值才会被日志记录下来,而相反小于这个时间的查询就不会记录下来。

profile详情

查询当前数据库是否支持profile详情

select @@have_profiling;

查看profile是否打开

select @@profiling;

使用set语句,将profile打开

set profiling = 1;

查看每一条SQL语句的耗时情况

show profiles ;

查看指定query_id的SQL语句每个阶段的耗时情况

show profile for query query_id;

查看指定query_id的SQL语句的CPU的使用情况

show profile cpu for query query_if;

explain执行计划

查看执行计划

explain select *form users;

将会出现一系列信息:

执行计划各字段义

  • ID

select查询的序列号(ID相同,执行计划从上到下;ID不同,值越大越先执行)

  • SELECT_Type

表示select的类型,常见的有:SIMP(简单表,即不使用表连接或者子查询),PRIMARY(主查询,即外层的查询),UNION(union中的第二个查询语句或者后面的查询语句),SUBQUERY(select或where后面有子查询)等

  • TYPE

连接性能从好到坏:NULL、system、const(根据主键或者唯一索引查询时)、eq_ref、ref、range、index、all

  • possible_key

查询可能出现的索引,可以是一个,可以是多个

  • KEY

实际用到的索引,如果没有索引就显示NULL

  • KEY_len

表示索引的字节数,是索引的最大可能长度,所以可能与实际的数据不同(在不会减少精确度的情况下,长度越短越好)

  • rows

是MySQL认为必须要查询的行数,这一般是一个估计值

  • filtered

表示返回行数占实际读取行数的百分比(当然是百分比越大越好) 

  • Extra

在查询过程中没有出现的字段,会显示在Extra中

索引使用

最左前缀法则

当要使用联合索引时,必须满足最左前缀法则,在查询时必须包含联合索引最左边的列(不包含的话,索引全部失效),如果跳过联合索引的某列就会导致部分索引失效(后面的索引失效)

 假设有一个联合索引,它包含了name、age、status三个字段,此时我进行查询:

select *from user where name='张三' and age='24' and status='0';

是能够按照索引进行查询的,可以通过执行计划中的select_type查看,select_type是联合索引

但是如果不包含最左边的列,那么select_type就会是ALL(也就是全表查询):

select *from user where age='24' and status='0';

此时我们还可以验证一下,如果 跳过了某个索引,如下就是跳过了字段age:

select *from user where name='张三' and status='0';

导致的结果就是,虽然依旧是使用联合索引查询,但是会导致跳过的索引age后面的索引status失效,也就是说在匹配最后一个索引的条件时并没有使用索引查询。

索引失效情况

范围索引

当联合索引中出现了范围索引( < 或者 > ),范围索引右边的所有索引失效(索引一般情况下,使用 >= 或者 <= 比较好)

索引列运算

不要对索引列进行运算操作,否则索引列会失效

字符串索引

如果在使用字符串索引列时没有使用引号,虽然能够查询成功,但是索引是失效的

模糊匹配

如果是模糊匹配后面部分,能够使用索引查询;但如果是模糊匹配前面部分,无法使用索引

explain select *from users where user_contact like '13%';

explain select *from users where user_contact like '%00000000';

or连接的条件

在条件中,如果or前面的条件有索引,or后面的字段没有索引,那么所有涉及的索引都不会被用到

数据分布影响

如果MySQL评价使用索引查询比比全表还慢,那么就不会使用索引

SQL提示

如果有多个索引存在,可以加入人为的提示,告诉MySQL进行一定的优化

常见的索引SQL提示:

  • use是建议MySQL使用你给的索引,但是MySQL还需要评估是不是最优的
  • ignore是限制MySQL不使用某个索引
  • force是强制MySQL使用某个索引

覆盖索引

Extra的信息提示:

using index condition(使用了索引,但是需要回表查询)

using where ;using index(同样使用了索引,并且数据全在索引列中,不需要回表查询)

前缀索引

当字段类型是字符串时,建立索引时,可能出现索引太长的情况。这时就可以使用前缀索引,也就是截取该字段数据的部分前缀,建立索引。能够大大减少索引空间,提高索引效率。

语法

create index idx_xxxx on 表名(字段名(n))
--n表示截取的字段前缀长度

索引的选择性

选择性表示,所建立的不重复的索引值与数据表的记录总数的比值,这个比值越大代表索引的选择性越好,主键的选择性为1,性能是最好的

查询流程

如上表,邮件字段是字符串类型,并且比较长,所以在建立索引时会比较占用空间,我们可以考虑使用前缀索引,比如我想实现下面的查询:

select *from users where email = 'lvbu666@163.com';

我们发现其实只需要email的前5位就可以将需要的信息查询出来,并且选择性为1,所以查询流程就会是下面这样:

建立一个前缀索引作为辅助索引,然后找到匹配的数据,然后找到数据的行号,在通过row进行回表查询:

思考题 

最优的方案应该是,建立一个username和password的联合索引,zhegelianhesuoyin是一个二级索引,而二级索引的B+Tree的叶子结点下面挂的是id

索引设计原则