首页 > 数据库 >MySQL索引失效原因

MySQL索引失效原因

时间:2023-04-23 16:36:10浏览次数:40  
标签:users 查询 索引 MySQL 失效 where 主键 select

索引介绍

MySQL默认的存储引擎是InnoDB,它采用B+树作为索引的数据结构。

InnoDB和MyISAM存储引擎都支持B+树索引,但是它们的数据存储结构实现方式不同。

  • InnoDB:B+树索引的叶子节点保存全部数据
  • MyISAM:B+树索引的叶子节点保存数据的物理地址

在创建表的时候,InnoDB存储引擎默认会创建一个主键索引,也就是聚簇(cù)索引,其他索引都是二级索引。它们的区别在于

  • 主键索引:叶子节点保存了行记录数据
  • 二级索引:叶子节点只保存了主键值和索引列

使用主键索引字段作为查询条件,当主键索引的B+树检索到对应的叶子节点时,因为数据都已经保存在了叶子节点中,可以直接读取要查询的数据。比如下面这个查询语句

select * from users where id = 10;

使用二级索引字段作为查询条件,如果要查询的数据都在主键索引的叶子节点中,那么就需要检索两颗B+树。

先在二级索引中找到对应的叶子节点,获取主键值,再通过主键值去主键索引的B+树上检索对应的叶子节点,然后读取要查询的数据。这个过程就叫回表

select * from users where name = 'cplemom';

在使用二级索引字段作为查询条件的时候,如果要查询的数据都在二级索引的叶子节点中,那么只要在二级索引的B+树中找到对应的叶子节点,然后直接读取要查询的数据。这个过程叫做覆盖索引。比如下面这个查询语句

select id,name from users where name = 'cplemom';

聚簇索引

聚簇索引的优势

  • 数据访问更快:

    • 由于叶子节点保存了行数据,且一个节点(磁盘页)包含多条数据,再次访问的时候可以直接在磁盘中完成,不必再读取硬盘
    • 根据主键值获取数据快
  • 对主键的排序查找和范围查找速度非常快

聚簇索引的缺点

  • 插入速度严重依赖插入顺序,按照主键的顺序插入是最快的,否则会出现页分裂,严重影响性能

    主键的值是顺序的,所每一条记录都存储在上一条记录的后面。当达到页的最大填充因子(InnoDB 默认的最大填充因子是页大小的 15/16,留出部分空间用于以后修改)下一条记录就会被写入到新的页中。

  • 索引维护昂贵,插入数据或者主键更新导致页分裂的时候,严重影响性能。所以一般主键不允许更新

  • 主键过大会导致非叶子节点占用更多物理空间

当对表进行大量的增删改操作的时候,很容易产生一些碎片,这些碎片占据着空间,可以通过OPTIMIZE TABLE重新整理表空间

为什么二级索引使用主键作为“指针”而不是地址值作为指针

减少当出现行移动或者数据页分裂时二级索引的维护。

使用主键值当做指针会让辅助索引占用更多的空间,但是相对的,当数据行的位置(实现中通过16K的Page来定位)随着数据库里数据的修改而发生变化(主键索引的B+树节点分裂以及Page的分裂)时,二级索引不受影响。

为什么主键索引建议使用自增Id

聚簇索引的数据的物理存放顺序与索引顺序是一致的,即只要索引是相邻的,那么对应的数据一定也是相邻的存储在物理磁盘上的

如果主键不是自增Id,随着数据添加,存储引擎需要不停的调整数据地址,分页,如果主键是自增Id,存储引擎只需要一页页的写,磁盘碎片少,效率也更高。

索引失效以及失效原因

索引的本质就是一颗B+树,说白了不能最快速直接通过B+树找到符合条件的数据就不会走索引

对索引列使用左模糊匹配

使用like '%cplemom'或者like '%cplemom%'这两种方式都会引起索引失效。

只有使用like 'cplemom%'的适合才会走索引。

因为索引的 B+ 树是按照「索引值」有序排列存储的,只能根据前缀进行比较

对索引列使用函数

特定场景下,查询时需要对数据进行处理,比如对 name 字段使用 LENGTH 函数就不会走索引了。执行计划也是type=ALL全表扫描。

select * from users where LENGTH(name) = 6;

原因也很简单,索引的B+树存储的是索引字段的原始值,函数计算后的值自然无法比较只能全表扫描了。

从 MySQL 8.0 开始,索引特性增加了函数索引,可以对函数计算后的值建立索引。

对索引列进行表达式计算

select * from users where id + 1 = 10;

原因和对索引列使用函数是一样的

对索引列隐式类型转换

当索引列是字符串类型,where中的查询参数是整型时,索引还是会失效,执行计划还是全表扫描。

select * from users where phone = 12345606324;

当索引列是整型,where中的查询参数是字符串类型时,就可以走索引扫描。

select * from users where age = '18';

MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字进行比较

可以尝试通过select '10' > 9; 的结果来了解MySQL的转换规则。

所以上面两个查询语句也等于加上了CAST函数,自然无法走索引了。

select * from users where  CAST(phone AS SIGNED); = 12345606324;
select * from users where id = CAST("1" AS SIGNED);

联合索引非最左匹配

多个普通字段组合在一起建立的索引就是联合索引,在建立索引的时候一定注意顺序问题。因为正确使用联合索引需要遵循最左匹配原则,按照最左优先的方式进行索引匹配。

比如创建一个联合索引(a, b, c),等于创建了(a)(a, b)(a, b, c) 3个索引,

当符合下面的几个查询条件的时候就可以走索引。

select * from users where a = 1;
select * from users where a = 1 and b = 2;
select * from users where a = 1 and b = 2 and c = 3;

因为查询优化器的存在,where中a字段的位置并不重要,会被自动优化。

下面这几个查询就不会走索引了,因为不符合最左匹配。

select * from users where b = 2;
select * from users where c = 3;
select * from users where b = 2 and c = 3;

还有一种查询条件就是,where a = 1 and c = 3会走索引吗?这个不同版本有不同的处理方式。

MySQL大概架构

这里需要大概了解一下MySQL的架构,Server层负责SQL语句的解析优化,然后生成执行计划,再调用引擎去执行数据的存储和检索。

在MySQL5.6之前,存储引擎根据联合索引找到了符合a = 1的节点,然后根据主键逐一进行回表查询,去主键索引找到完整的行记录,然后返回给Server层对记录根据c = 3进行筛选。

在MySQL5.6之后,存储引擎根据联合索引找到了符合a = 1节点,因为联合索引节点中还包含了C列,所以存储引擎可以直接根据c = 3过滤,然后再返回给Server层。

在索引遍历过程中直接过滤不符合查询条件的数据,减少回表次数就是索引下推优化

WHERE查询语句中使用了OR

在Where语句中,如果OR的一边是索引列,一边是普通列,索引还是会失效的。因为单纯遍历索引并没有办法判别数据是否符合条件,只能全表扫描。

如下面的查询语句,id 是主键,name是普通列,执行计划type=ALL

select * from users where id = 1 or name = 'admin';	

如果给name列也加上索引,执行计划 type=index merge

index merge的意思就是分别对 id 和 name 列进行遍历,然后将查询结果进行合并,从而避免了全表扫描。

总结

本文介绍了索引的数据结构、索引在不同数据库引擎下的区别、聚簇索引(主键索引)和二级索引的区别以及最后列举了几种会导致索引失效的情况及其原因。

标签:users,查询,索引,MySQL,失效,where,主键,select
From: https://www.cnblogs.com/cplemom/p/17151784.html

相关文章

  • mysql select for update + 事务处理数据一致性
    如果SELECT后面若要UPDATE同一个表数据的相关操作,最好使用SELECT...FORUPDATE。一:举例说明假设商品表单test_leyangjun 内有一个存放商品库存的num字段,一个id主键 ,在生成订单前须先确定num>0 ,然后才把数量更新。代码如下(比如现在的库存:num=3对应的id=3,现在生成一个订单......
  • 基于Canal实现MySQL 8.0 数据库数据同步
    前言服务器说明主机名称操作系统说明192.168.11.82Ubuntu22.04主库所在服务器192.168.11.28OracleLinuxServer8.7从库所在服务器版本说明MySQL版本:MySQL_8.0.32Canal版本:Canal_1.1.7//我的canal安装部署在192.168.11.82上,当然你也可以部......
  • mysql 主从,django操作读写分离
    目录mysql主从搭建步骤1.准备两台机器2.创建文件夹,文件(目录映射)3编写mysql配置文件(主,从)重要启动mysql容器,并做端口和目录映射连接主库修改新建用户连接从库修改配置django操作多数据库方法1.配置文件2.手动读写分离3.自动读写分离4.配置文件添加新点mysql主从目的: 主要做读......
  • sqlserver视图 to mysql表
    sqlserver视图tomysql表有两台服务器,是同一个内网。sqlserver这台只能内网访问mysql这台可内可外现在需要将sqlserver的一个视图数据,每天凌晨同步到另外一台服务器的mysql数据库里共参考:安装sqlserverhttps://www.linuxprobe.com/centos7-instal-mssql-2019.html创......
  • 一篇文章教会你进行Mysql数据库和数据表的基本操作
    前言NaicatforMysql使用通常情况下,我们都是通过mysql.exe(客户端),连接mysql服务的。但是有个问题。第一个问题就是太丑了。第二个问题是因为这不会自动提示啊...,早都习惯了各种自动提示工具,在用这么Low的安装下一步下一步就可以了!!!管理员运行PatchNavicat打开之后,游览到安装N......
  • 1 python操作哨兵 、2 python操作集群、3 缓存优化、4 mysql 主从 、5 django使用多数
    目录1python操作哨兵2python操作集群3缓存优化3.1redis缓存更新策略3.2缓存击穿,雪崩,穿透4mysql主从5django使用多数据库做读写分离1python操作哨兵#高可用架构后---》不能直接连某一个主库了---》主库可能会挂掉,后来它就不是主库了#之前学的连接redis的操作,就用不......
  • Mysql查询语句进阶知识集锦
    前言上次咱们简单的学习了一下select的用法,一篇文章教会你进行Mysql数据库和数据表的基本操作,对数据库大概有了一些基本的了解。咱们接着上次继续来看叭!查询数据如下or查询我们在上学时,会听到这样的话,某某某,你把谁谁谁或者谁谁谁叫过来。这样子的话,我们我们要查询的,就是一个或......
  • mysql主从-day1——mysql主从搭建、django中使用多数据库做读写分离
    目录一、mysql主从5django使用多数据库做读写分离一、mysql主从#之前做过redis的主从,很简单#mysql稍微复杂一些,搭建mysql主从的目的是? -读写分离-单个实例并发量低,提高并发量-只在主库写,读数据都去从库#mysql主从原理步骤一:主库db的更新事件......
  • Django配置mysql数据库
    今天跟大家分享Django配置python的mysql数据库链接工具pymysql。系统环境:windows11x64,pycharm专业2022.3版本,python11,Django4.0。第一步:pip安装pymysql库,在当前django的python环境中用pip install pymysql进行安装。如下图: 我已经安装好,所以不用重新安装,这里需要注意的是有......
  • spring事务失效的12种场景
    1.方法访问权限问题,只支持public2.方法用final修饰,动态代理不能代理final方法3.方法内部调用,同一对象内调用没有使用代理,未被aop事务管理器控制4.未被spring管理5.多线程调用,事务管理内部使用threadLocal,不同线程间不在同一事务6.表不支持事务7.未配置事务事务不回滚8.错误的传播......