谈谈MySql中的索引及其失效的情况

2023年 11月 4日 71.6k 0

索引的定义与作用

索引是数据库管理系统中用于提高查询速度的一种数据结构。在MySQL中,索引可以看作是一种特殊的表,其中包含了对数据表中特定列的值及其在数据表中的位置信息。

它就像是一本书的目录或字典,允许我们在很短的时间内找到所需的信息。 在数据库环境中,索引可以大大加快数据检索速度,减少处理时间和磁盘I/O次数。此外,它还可以确保对数据库记录的操作(如INSERT、UPDATE和DELETE)的完整性。因此,在设计数据库应用程序时,应充分考虑并合理利用索引技术。

索引的创建

建表时创建

 create table users(
     id        int unsigned auto_increment primary key,
     username  varchar(50)  not null,
     password  varchar(11)  not null
 )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在创建表时,在表中定义一个primary key约束时,MySQL会自动为我们创建一个相应的主键索引。这也是所谓的隐式创建,可以通过如下语句来进行查看。

 show index from users;

image-20231102145644808

建表后添加

当然,除了在创建表的时候定义,你也可以在已经定义好的表中进行添加。

 create index username_index on users(username);

以上语句就为users表又添加了一个普通索引(单列索引)

image-20231102151416370

或者通过alter语句进行索引的创建。

 alter table users add index indexName(password);
 # ALTER TABLE `tableName` ADD PRIMARY KEY ( `column` ) 
 # 添加主键索引
 # ALTER TABLE `tableName` ADD UNIQUE (`column`)
 # 添加唯一索引
 # ALTER TABLE `tableName` ADD INDEX index_name (`column1`,`column2`)
 # 添加组合索引

以上就又创建了一个名为indexName的普通索引,同时列举了主键索引跟唯一索引的创建方式。

在创建索引方面,ALTER TABLE 和 CREATE INDEX 都可以达到相同的效果,但它们之间存在一些微妙的区别。例如,ALTER TABLE 可以同时执行多个更改,而 CREATE INDEX 则只能用于创建单个索引。

删除索引

在优化表时,如果遇到需要删除索引时,MySql也是支持的,可通过如下语句实现:

 # 删除索引
 # DROP INDEX index_name ON table_name;
 DROP INDEX username_index ON users;

这里我们就将刚刚创建的单列索引给删除了,只剩下最初建表时创建的主键索引。

image-20231102145644808

被动创建

那如果我们在创建表的时候不设置索引,在后续也不给表添加索引,那该表是否就没有索引呢,它的查询效率是否就会很低?

MySql 8.0.30开始,MySQL支持为没有显式主键的InnoDB表生成不可见的主键。当sql_generate_invisible_primary_key服务器系统变量设置为ON时,MySQL服务器会自动将生成的不可见主键(GIPK)添加到任何这样的表中。

  • 查询对应设置
 SELECT @@sql_generate_invisible_primary_key;

该属性的默认值为:0。不开启,也就是说默认的情况下,MySql不会为没有创建的表创建主键(索引)。

 create table demos(
     username  varchar(50)  not null,
     password  varchar(11)  not null
 )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 # show index from demos;

未将sql_generate_invisible_primary_key的值设为on的情况下,是不会为其添加主键的。

接下来我们将其设置为on,再重新创建表看看。

 SET sql_generate_invisible_primary_key=ON;

| @@sql_generate_invisible_primary_key | ------------------------------------ 0 |

 create table demos(
     username  varchar(50)  not null,
     password  varchar(11)  not null
 )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 # show index from demos;

此时,就可以看到该表在创建成功后,自动有了一个名为PRIMARY的主键

image-20231102162759335

相关链接:

MySql-官网文档

通过索引定位数据

MySQL常用的InnoDB引擎中,还是使用 B+树索引比较多。以下是MySql使用B+树进行数据存储的一个简略结构,

image-20231102182906743

真实场景中最底层中每个节点(叶子节点)都有若干条数据,并且用其中的最小值作为一个标识符,而中间的节点则是存储的最底层中每个节点的最大值(当然其中的数据也有若干条),并用其中最大值作为标识符。而最上层的节点就是根节点,它存储的就是第二层中的子节点的标识符跟其他一些信息。

查找过程

比如我们去找43这个数,

  • 首先在跟节点进行比较,发现应该去最大的那个子节点图中为0033去查找
  • 接着在0033节点中跟内部元素进行比较,发现需要去最大的叶子节点中查找
  • 最后在叶子节点0033-0042中查找,经过一番比较过,没有找到,于是返回结果:没有该元素

以上就是如何通过主键索引查询数据的过程,每个数据节点存储的数据具体有些哪些这里就不展开了。

提示:使用InnoDB引擎创建主键索引,叶子节点存储了我们完整的用户记录

聚簇索引跟非聚簇索引

使用InnoDB 引擎创建的主键索引也叫聚簇索引(Clustered Index)。聚簇索引是指:将整个表的数据都存储在一个 B+树结构中的索引。该索引组织了表中的数据,使它们按照主键值排序并存储在一个 B+树中。聚簇索引的特点:是可以快速访问指定范围内的记录,因为不需要多次查找就可以检索到所需要的数据。此外还有聚簇索引的叶子节点存储的是表中的数据

此外还有非聚簇索引,在MySql中,非聚簇索引是指在 InnoDB 引擎中除了主键索引以外的所有其他索引类型,如:唯一索引,组合索引...非聚簇索引不同于聚簇索引,它不包含表的实际数据,而是指向数据的指针。当需要从非聚簇索引中检索数据时,MySQL 需要执行一次回表操作,以便访问聚簇索引并将数据返回给客户端。非聚簇索引也叫二级索引,或者叫辅助索引。它叶子结点存储的则是主键值索引列

使用非聚簇索引查找数据的过程一般情况如下:

  • 首先,MySQL 会在非聚簇索引的 B+ 树上找到匹配的索引项,并读取指向实际数据行的指针;
  • 然后,MySQL 会根据这些指针从聚簇索引中检索出相关行,然后将其返回给客户端。
  • 从非聚簇索引转到聚簇索引的这个过程就叫做回表

    注意:通过非聚簇索引查询不是一定会发生回表,如果查询完全符合非聚簇索引的条件,则可以直接从非聚簇索引中检索出所需的记录。在这种情况下,无需进一步回表即可完成查询。

    索引的一些类型

    • 主键索引:设定为主键后,数据库自动建立索引,InnoDB为聚簇索引,主键索引列值不能为空(Null)
    • 唯一索引:索引列的值必须唯一,但允许有空值(Null),但只允许有一个空值(Null)
    • 复合索引:一个索引可以包含多个列,多个列共同构成一个复合索引
    • 全文索引:全文索引是一种特殊类型的索引,用于在文本数据上进行高效的全文搜索。
    • 空间索引: MySQL 提供的一种用于索引地理位置数据的技术,它使用 R 树结构来提高查询性能。

    索引失效的情况

    查看一个语句是否走索引,可以通过如下sql进行查看

     #explain select * from tableName where 条件
     explain select * from users where password="222" 
    

    image-20231102231228979

    若对应的key有值,则为查询时走了索引。

    不满足最左匹配原则

    使用联合索引时,比如我们建立了name, age, phone 三个字段的联合索引。

     alter table users add index uni_index(name,age,phone);
     # 不走索引
     explain select * from users where age=12 and phone= 1298672
    

    因为最左优先原则:会先比较name属性,再接着age,最后phone,如没有,则无法匹配

    所以如果where age = 12,则不会生效,而where age = 12 and name="test"则会生效。或者说只有(a),(a,b),(a,b,c)三种查询可以生效。

    使用不等于(!= 、<>)

     explain select * from user where age != 20;
    

    通常情况来说,使用不等于是不走索引的,但也有另外,MySQL中有一个叫做优化器的东西,他会对每一条查询sql做成本分析,然后根据分析结果选择是否使用索引或者全表扫描。

    这里举2种情况,如果不等于20的记录跟等于20的记录,数量都比较多,则成本很高,不会走索引。但如果本身记录很少,比如一共只有几条,则会走索引,或者不等于2的仅有10条,也会走索引。

    总结:使用不等于的情况下,不一定不走索引,而是看成本,如果走索引成本不大,也会走索引,否则不会。

    “OR”前后存在非索引列,导致索引失效

    当where语句中使用or操作符并且两边的条件至少涉及两个字段,且字段中存在非索引列时,MySQL无法使用索引,会转向全表扫描。

    index_merge: OR前后的两个条件中的列都是索引时,查询中才使用索引,索引类型是“index_merge”,将两个索引字段分别扫描,然后合并。

     explain select * from diy.users where age =88 or hobby ='看电影';
     # 使用了索引
     explain select * from diy.users where age =88 or hobby ='看电影' or password='123'
     # 以上语句 则没有使用索引
    

    在上述两个语句中,因为age,hobby都使用了索引,所以第一个sql走了索引,而password没有索引,所以第二个sql不会走索引。

    like中用通配符开头('%xxx')

     # 在一个表中创建一个索引
     alter table diy.users add index h_index(hobby);
     # 以下语句 使用了索引
     explain select * from diy.users where hobby like '看%'
     # 未使用索引 花费时间更久
     explain select * from diy.users where hobby like '%看'
    
  • %通配符:表示任意字符,可以匹配任意的字符数量。
  • _通配符:表示任意单个字符。
  • 使用Order By

     explain select * from diy.users order by id,age;
    

    以上sql语句,order后跟了2个字段,其中id是主键,但age没建立索引,则在查询时不会走索引。这是因为:ORDER BY子句中的列与索引列不完全匹配(即索引列包含部分数据而非完整列),所以MySQL可能无法有效地利用索引进行排序。

    通常来说,order by后跟的列只要是索引列就会走索引,但有时即使order by后跟的列是索引列,也会发生索引失效的情况,这是因为优化器的作用,可能是由于查询优化器的估算成本较高,或者使用文件排序的开销较小等原因。

    在索引列上进行计算、函数

    现有一个需求,找出name为ty开头的字段信息:

     create index idx_name on demos(name);
     # 对索引列执行函数计算
     explain select * from demos where left(name,2)='ty'
    

    字段使用函数会让MySql中的优化器无法生效,因为函数的结构可能和B+树中的值不匹配,所以不会走索引。字段能不用函数就不用函数。

    Select *

     select * from tableName
    

    使用select * 查询,不加任何条件,也不会走索引,直接返回全部结果,没有用到任何索引,查询效率很低

    还有其它的一些索引失效的情况,这里只列举了一些常用的。

    优缺点与建议

    优点:它类似于书的目录,降低了数据库IO成本,大大提高了数据查询效率

    缺点:索引也是数据,因此会占据一些磁盘空间,同时由于索引跟数据存在一定关联关系,在改动数据时,也需要同时对它进行更新,一定程度上减低了更新表的效率(每次对表进行增删改查,MySQL不仅要保存数据,还有保存或者更新对应的索引文件)。

    总结:索引确实能提高查询效率,但并非每次查询都会奏效,因为它独特的数据结构,有时也会存在失效的情况。索引其实就是用空间换取时间的典型例子。

    数据库表中不是索引越多越好,而是根据情况为那些常用的搜索字段建立索引,效果才是最佳!

    一些扩展资料

    菜鸟教程-MySql索引

    数据结构可视化网站

    相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论