Mysql系列-索引类型

一 、索引类型别

根据叶子节点的内容分类的索引类型

InnoDB 使用B+ tree 索引模型,根据叶子节点是否存储数(根据叶子节点的内容)分为主键索引和非主键索引;非主键索引包括:普通索引、唯一索引、组合索引主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询,也称为回表查询。使用主键索引查询会一次性查询出数据。

1.普通索引

MySQL中最基本的索引类型,没有限制,允许重复,允许NULL

sql 复制代码
CREATE INDEX index_code ON tb_student(code);

2.唯一索引

唯一索引跟普通索引类似,唯一索引列的值必须唯一,允许有空值。

如果是组合索引,则列值的组合必须唯一。

创建唯一索引通常使用 UNIQUE 关键字,例如电话号码必须唯一,创建唯一索引

sql 复制代码
CREATE INDEX index_phone_number ON tb_student(phone_number);

3.主键索引

主键索引其实也是唯一索引的一种,但是不允许值重复或者值为NULL。必须使用PRIMARY KEY,在创建表结构的时候创建,不能单独用CREATE 的方式创建,主键一般都是自增ID

4.全文索引

全文索引主要用来查找文本中的关键字,只能在 CHAR、VARCHAR 或 TEXT 类型的列上创建。在 MySQL 中只有 MyISAM 存储引擎支持全文索引。

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

不过对于大容量的数据表,生成全文索引非常消耗时间和硬盘空间。

创建全文索引使用 FULLTEXT 关键字。

sql 复制代码
CREATE FULLTEXT INDEX index_name ON tb_student(name);

根据索引和数据存储方式分类存储方式

都属于B + tree的所有结构,那么他们的区别是什么呢?最大的区别就是存储方式的不同

聚簇索引

聚簇索引:将数据和索引放在一起存储,并且是按照顺序,数据的物理存放顺序和索引顺序是一直的,找到索引,就能找到数据

非聚簇索引

非聚簇索引:叶子节点不存数据,存储的是数据对应的地址,也就是说先要查询到地址,然后在去磁盘中找到对应的数据,因为数据和索引是分开的,举个例子,我们要找一本书中的某一个知识点,那么就需要先去目录中查询对应的章节页码,然后通过页码找到对应的那一页,这个目录就相当于索引,而那一页就是对应的数据

优点

聚簇索引查询可以直接获取数据,相比于非聚簇索引需要二次查询效率更高

聚簇索引的范围查询效率更高,因为是有序排列的
缺点

维护成本较高,特别是插入数据,会导致分页(page split),当数据插入一个已满的页中时,存储引擎就会把这一页分页,这样就导致占用更多磁盘空间

更新数据代价高,需要把更新后的行移动到新的位置

二 索引的规则

最左匹配原则

例如给日志表创建了3个联合索引,分别是

operate_type操作类型,

operate_user操作人,

operate_status操作状态

sql 复制代码
CREATE INDEX index_type_user_status ON eom_log(operate_type,operate_user,operate_status);

下面我们就来例如explain来看看是否命中索引

sql 复制代码
explain select * from eom_log where operate_type < 10

先来查一个字段,operate_type,结果如下,命中索引

再加一个字段,执行的结果也是命中索引

sql 复制代码
explain select * from eom_log where operate_type < 10 and operate_user < 10

那么如果我们查最右边的那个字段,会发生什么呢?很遗憾,没有走索引,

同意的道理,只查询中间字段,也是不走索引的

sql 复制代码
explain select * from eom_log where operate_user < 10

所以在多个字段组成的联合索引中,where条件中必须要从最左边的第一个字段开始匹配,如果没有就无法命中索引

那么问题又来了,如果只用第一个和第三个 字段当条件能命中索引吗?我们来试试

sql 复制代码
explain select * from eom_log where operate_type < 10 and operate_status  < 10


结果显示命中索引,所以只要where条件中包含最左边的字段,那么一定是命中

那么问题又来了,如果把这3个字段的顺序颠倒一下,能命中索引吗?

sql 复制代码
explain select * from eom_log where operate_user < 10 and operate_status  < 10 and operate_type < 10


结果显示命中索引,这里可能会有小伙伴问,不是最左匹配吗,其实这是mysql的优化器的功劳,优化器会判断纠正这条sql语句该以什么样的顺序执行效率最高,他会排列where的查询顺序,使他能够命中索引,最后才生成真正的执行计划。所以mysql查询优化器会最终以这种顺序进行查询执行。

覆盖索引

覆盖索引就是SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。说白了,就是你的查询走到了索引,并且需要返回的数据刚好是索引的组成字段,不需要回表就能得到想要的结果;

简单粗暴理解:如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫做覆盖索引,如根据主键查询走主键索引,或者如下情况

看看例子,有个学生表student,里面有个联合索引学号和电话号码index_code_phone

sql 复制代码
SELECT code,phone from tb_user where code = 'a' and phone = 'b'

那么这个要查询的code 和phone 正好索引,那么查询的时候就直接能够返回数据,不需要走二次查询,这就是覆盖索引
覆盖索引的优点

  • 减少了磁盘I/O,提高了查询速度

  • 减少了内存占用,节省了资源

  • 减少了锁的竞争,提高了并发能力,它可以避免回表查询原始数据,从而减少了对数据行的访问和锁定。如果使用索引覆盖,那么只需要对索引记录加锁,而不需要对数据记录加锁。这样就可以减少锁的范围和冲突,提高并发能力

覆盖索引的缺点

  • 增加了索引的大小,占用了更多的磁盘空间
  • 增加了数据更新的开销,因为每次更新数据都需要更新索引
    因此,在创建覆盖索引时,我们需要权衡利弊,选择合适的字段和顺序,避免创建过多或过大的索引。

索引下推

索引下推(Index Condition Pushdown,简称ICP),是MySQL5.6版本的新特性,它能减少回表查询次数,提高查询效率。索引下推是指在使用联合索引时,将过滤条件下推到索引层,从而减少回表的次数。索引下推的下推其实就是指将部分上层(服务层)负责的事情,交给了下层(引擎层)去处理。索引下推的目的是为了减少回表次数,也就是要减少IO操作。对于的聚簇索引来说,数据和索引是在一起的,不存在回表这一说。

索引下推原理

我们来具体看一下,在没有使用ICP的情况下 ,MySQL的查询:

存储引擎读取索引记录;

根据索引中的主键值,定位并读取完整的行记录;

存储引擎把记录交给Server层去检测该记录是否满足WHERE条件。

使用ICP的情况下,查询过程

存储引擎读取索引记录(不是完整的行记录);

判断条件部分能否用索引中的列来做检查,条件不满足,则处理下一行索引记录;

条件满足,使用索引中的主键去定位并读取完整的行记录(就是所谓的回表);

存储引擎把记录交给层,层检测该记录是否满足条件的其余部分。

例如,假设我们有一个订单表,包含了订单的id,用户id,商品id,价格等字段,我们想要查询所有用户id为1且价格大于1000的订单的商品id和价格,那么我们可以创建一个包含了用户id,价格和商品id的联合索引,这样在扫描索引时就可以先过滤掉不满足用户id为1或价格大于1000的记录,而不需要回表查询订单表。

与联合索引不同的是,联合索引只能在查询条件中包含索引的前缀部分时才能生效,而索引下推可以在查询条件中包含任意部分的索引时生效。 例如,如果一个联合索引是(a,b,c),那么只有当查询条件中包含a或者a和b时才能使用该索引,而如果查询条件中只包含b或者c时则不能使用该索引。 但是如果使用了索引下推,那么即使查询条件中只包含b或者c,也可以将这些条件下推到索引层面上进行过滤,从而减少回表的次数和范

索引下推的具体实践

理论比较抽象,我们来上一个实践。使用一张用户表tuser,表里创建联合索引(name, age)

如果现在有一个需求:检索出表中名字第一个字是张,而且年龄是10岁的所有用户。那么,SQL语句是这么写的:

sql 复制代码
select * from tuser where name like '张%' and age=10;

假如你了解索引最左匹配原则,那么就知道这个语句在搜索索引树的时候,只能用 张,找到的第一个满足条件的记录id为1

那接下来的步骤是什么呢?

没有使用ICP

在MySQL 5.6之前,存储引擎根据通过联合索引找到name likelike '张%' 的主键id(1、4),逐一进行回表扫描,去聚簇索引找到完整的行记录,server层再对数据根据age=10进行筛选。

我们看一下示意图:

可以看到需要回表两次,把我们联合索引的另一个字段age浪费了。

使用ICP

而MySQL 5.6 以后, 存储引擎根据(name,age)联合索引,找到,由于联合索引中包含列,所以存储引擎直接再联合索引里按照age=10过滤。按照过滤后的数据再一一进行回表扫描。

我们看一下示意图:

可以看到只回表了一次。

除此之外我们还可以看一下执行计划,看到Extra一列里 Using index condition,这就是用到了索引下推。

索引下推使用条件

只能用于range、 ref、 eq_ref、ref_or_null访问方法;

只能用于InnoDB和 MyISAM存储引擎及其分区表;

对存储引擎来说,索引下推只适用于二级索引(也叫辅助索引);

索引下推的目的是为了减少回表次数,也就是要减少IO操作。对于的聚簇索引来说,数据和索引是在一起的,不存在回表这一说。

引用了子查询的条件不能下推;

引用了存储函数的条件不能下推,因为存储引擎无法调用存储函数。

索引下推优缺点

索引下推的优点

  • 减少了回表的次数和范围,提高了查询速度
  • 减少了数据传输量和网络开销
    索引下推的缺点
  • 增加了索引扫描的复杂度和开销
  • 可能会导致误判或漏判,因为索引层无法处理一些复杂或模糊的过滤条件

因此,在使用索引下推时,我们需要注意一些限制和条件,比如:

  • 索引下推只适用于联合索引
  • 索引下推只适用于部分数据类型和操作符
  • 索引下推可能会受到优化器的影响

相关系统参数

索引条件下推默认是开启的,可以使用系统参数optimizer_switch来控制器是否开启。

查看默认状态:

sql 复制代码
mysql> select @@optimizer_switch\G;
*************************** 1\. row
: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on
1 row in set (0.00 sec)

切换状态:

sql 复制代码
set ="index_condition_pushdown=off";
set ="index_condition_pushdown=on";
相关推荐
PGCCC34 分钟前
【PGCCC】Postgresql 存储设计
数据库·postgresql
PcVue China2 小时前
PcVue + SQL Grid : 释放数据的无限潜力
大数据·服务器·数据库·sql·科技·安全·oracle
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
锐策4 小时前
〔 MySQL 〕数据库基础
数据库·mysql
远歌已逝5 小时前
管理Oracle实例(二)
数据库·oracle
日月星宿~6 小时前
【MySQL】summary
数据库·mysql
爱吃土豆的程序员6 小时前
在oracle官网下载资源显示400 Bad Request Request Header Or Cookie Too Large 解决办法
java·数据库·oracle·cookie
睿思达DBA_WGX6 小时前
Oracle 11g rac 集群节点的修复过程
数据库·oracle
尘浮生6 小时前
Java项目实战II基于微信小程序的移动学习平台的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·学习·微信小程序·小程序
希忘auto7 小时前
详解MySQL安装
java·mysql