MySQL常见八股:索引

MySQL索引的最左前缀匹配原则是什么?

一句话概括:

当MySQL在使用联合索引时,查询条件必须从索引的最左列开始匹配。

这是因为联合索引在B+树中的排列方式是"从左到右"的顺序。比如联合索引(first_name,last_name,age)会先按 first_name 排序,first_name 相同再按last_name 排序,last_name 相同再按 age 排序。MySQL 查找时会优先用first_name作为匹配依据,然后依次用last_name和 age。跳过最左侧字段,后面的列在B+树中是无序的,压根没法利用索引快速定位。

特例:范围查询

当范围查询是:>,<这种,会停止匹配

sql 复制代码
where a > 1 and b = 2 and c = 3;

只有a能用上联合索引,因为a经过查询筛选后,不同a值之间的b和c是无序的。无法走索引。

相反,当遇到>=,<=,前缀 like xx%时,不会停止匹配。因为他们之中包含等值判断

脏读/不可重复读/幻读分别代表什么意思

这三个都是并发事务带来的数据一致性问题,严重程度递减

  • 脏读:指数据库中一个事务查询了一个还没提交的数据,万一那个数据回滚了,读到的数据不存在
  • 不可重复读:指一个事务查询两次同一行数据,两次获得的数据不一样。因为中间有别是事务修改并提交了这行数据。强调数据的内容改变
  • 幻读:指一个事务执行两次同样的范围查询,返回的数据行数不一样。因为中间有别是事务执行了插入或删除操作。强调数据的行数变化。

不同隔离级别下会出现的问题

安全程度:读未提交>读已提交>可重复读>串行化

性能依次递减,InnoDB存储引擎默认的就是可重复读。

如何解决的

  • 脏读:MVCC天然解决,读的是快照,看不到未提交的数据
  • 不可重复读:在可重复读隔离级别下,ReadView在事务一开始就固定了,后续复查读取的是同一个ReadView。
  • 幻读:快照读走MVCC,看不到新插入行,当前读用间隙锁所著范围,别的事务插不进来。当快照读和当前都混用时,还会出现幻读。

MySQL的存储引擎有哪些,他们之间有什么区别

MySQL的存储引擎是可插拔的,不同引擎辅助数据的存储与读取。实际场景中95%都是使用的InnoDB,面试主要讲InnoDB与MyISAM进行区分就行了

MySQL8.4版本一共提供了10个引擎,常见的有这几个:

1)InnoDB:MySQL5.5 之后的默认引擎,支持事务、行级锁、外键,MVCC也有,适合高并发的OLTP场景。数据按聚簇索引组织,主键查询贼快。

2)MyISAM:老版本的默认引擎,不支持事务,只有表级锁,但读性能不错。适合那种写少读多、对一致性要求不高的场景,比如早年的一些报表系统。

3)MEMORY:数据全放内存里,速度快但MySQL重启数据就没了。一般拿来做时表或者会话级缓存。4)Archive:专门存归档数据的,只支持INSERT 和 SELECT,不支持索引I,但压缩率高。日志归档、历史订单这种场景用得上。

5)NDB:MySQLCluster用的引擎,支持分布式和高可用,数据自动分片,适合电信级别的大规模集群。

MySQL的覆盖索引

覆盖索引指的是查询内容包含在了二级索引内部,查询时直接从索引里拿数据,无需再次执行回表操作。

本质是:索引中有的数据直接拿,减少回表操作。

优点:

  • 减少I/O操作:查询直接从索引中拿数据,避免访问主键索引的数据页。
  • 查询更快:索引页小,结构紧凑,缓存命中率更高
  • 减少内存占用:Buffer Poll只需加载索引页,不用加载更大的数据页

设计覆盖索引的哲学

主要分四点来设计:

  • 高频查询优先:分析业务哪些查询频率高,针对这些设计覆盖索引
  • 权衡读写比列:覆盖索引不是越多越好。索引列越多,索引越大,写入维护成本高。
  • 利用联合索引:把where条件的列与select的列创建为一个联合索引。例如:select a,b from t where c=1,可以键(c,a,b)索引

覆盖索引和索引下推的区别

覆盖索引是完全不回表,索引里有查询需要的所有数据。索引下推是减少回表次数,把过滤条件下推到引擎层提前过滤,但最终还是要回表拿完整数据。两种解决的问题不一样

索引类型有哪些

MySQL索引可以从三个维度来分类:数据结构,存储方式,索引性质。我们先从数据结构来切入,重点讲解B+树,然后展开讲解聚簇索引和非聚簇索引的区别。
数据结构

  • B+树索引:叶子节点存储数据,叶子节点通过链表串起来:保证了快速定位单条记录和高效做出范围扫描。
  • 哈希索引:通过哈希函数直接算出数据位置,等值查询O(1),不支持范围查询和排序。
  • 全文索引
  • 空间索引
    存储方式
  • 聚簇索引:又叫主键索引,叶子节点世界存放完整的行数据,数据按主键顺序物理存储,一张表只能由一个主键索引
  • 非聚簇索引:也叫二级索引,叶子节点只存储索引字段值和主键值。查完二级索引还得拿着主键取主键索引获取具体数据。
    索引性质
    1)主键索引:唯一且非空,每张表只能有一个。InnoDB里主键索引就是聚簇索引。2)唯一索引:保证列值不重复,但允许有NULL,可以有多个NULL。
    3)普通索引:没有唯一约束,纯粹为了加速查询。
    4)联合索引:多列组合成一个索引,遵循最左前缀原则,列顺序很重要。5)全文索引:文本搜索用。
    6)空间索引:GIS数据用。

衍生

B+树厉害在哪

数据库查询由两大类构成:等职查询和范围查询。哈希索引等值查询速度快,但是无法进行范围查询。B+树两个都行,并且叶子节点用双向链表连接,范围扫描的时候顺着链表走,不用回溯。

另外,B+树特点是树矮,一个三层的B+树就能存储2000万左右的数据。查询一条数据最多3次I/O。

索引下推

索引下推是MySQL 5.6之后引入的技术。核心思路是把部分查询条件从Service层下推到存储引擎层,在引擎层就把不符合查询条件的数据过滤,不用在将这些数据进行回表操作。作用是:减少回表操作,提升查询效率。

没有索引下推的流程是:引擎层用索引定位数据,返回主键给Service层,Service层回表拿到完整数据,再用剩余条件过滤。有了索引下推,引擎层能直接用索引里的列来过滤,不符合条件的不会回表,减少了I/O

如何判断索引是否生效

在SQL语句的前面加上EXPLAIN,如果显示Usingindexcondition,说明用上了索引下推。

sql 复制代码
EXPLAIN SELECT * FROM people
  WHERE zipcode='95054'
  AND lastname LIKE '%etrunia%';

Extra 显示Usingindex condition就表示 lastname 条件被下推到引擎层了。`

关于联合索引,覆盖索引,索引下推的区分

联合索引是基础,有了联合索引才有覆盖索引和索引下推。

索引下推和覆盖索引是技术。

这两个优化都能减少I/O,但原理不同:

特性 索引下推 覆盖索引
解决的问题 减少回表次数 完全避免回表
是否回表 需要回表,但次数少 不需要回表
EXPLAIN 标识 Using index condition Using index
适用场景 select * 且有额外过滤条件 select 的列都在索引里

两者可以同时存在吗?不能。如果走了覆盖索引,压根不回表,索引下推就没有用武之地了。

InnoDB存储引擎中聚簇索引和非聚簇索引有什么区别

在 InnoDB 引擎中,聚簇索引的叶子节点直接存储完整的数据行,一张表只能有一个聚簇索引I,默认就是主键索引。非聚簇索引的叶子节点只存储索引列的值和主键,要拿到完整数据得先查主键再回表,一张表可以有多个非聚簇索引。

核心区别就是叶子节点存的东西不一样:聚簇索引存完整数据,非聚簇索引存主键值。这导致聚簇索引查询能一步到位拿到数据,非聚簇索引可能要多一次回表操作。

MySQL中使用索引一定有效吗?如何排查索引效果

索引不一定生效 。建了索引不低啊表查询会用,用来索引不代表查询就快。

MySQL最终是否走索引,靠的是优化器的成本计算。优化器会评估走索引和全表扫描各自的I/O成本和cpu成本。优势全表扫描确实成本低,比如表就几百行数据,走索引不如直接扫;有时统计信息不准,导致优化器算错账。

用EXPLAIN命令来排查索引。在SQL语句前面加上EXPLAIN,就能看到MySQL选择的执行计划,重点看:

  • type:访问类型,all是全表扫描,range是范围扫描,ref是等值匹配,index是全索引扫描。
  • key:实际用的索引名称,NULL就是没用上索引
  • rows:预估扫描行数,这个数字越大说明查询代价越高。

索引失效常见场景

基本原因分为两类:1.查询条件导致索引树的快速查找能力用不上 2.优化器觉得不划算

1)联合索引不符合最左前缀

假设有个联合索引idx_name_age_id,查询写成WHERE age= 10 AND id = 1,跳过了 name字段,最左缀匹配失败,索引树的有序性利用不上,退化成全索引扫描甚至全表扫描。

2)索引列上做了运算

WHEREid+3=8这种写法,MySQL得把每行的id 拿出来算一遍,没法直接在索引|树上定位,只能全录扫描。

3)索引列上用了函数

WHERELOWER(name)='cong',对索引列套了函数,索引树里存的是原始值,函数处理后的值根本不在树里,还是得全表扫。MySQL8.0 可以建函数索引来解决这个问题。

4)LIKE左侧带通配符

WHEREnameLIKE%cong%'这种写法,索引是按字符顺序排的,左边不确定就没法定位起始位置,只能全扫。LIKE'cong%'是可以走索引的。

5)OR连接了非索引字段

WHEREname='cong'ORage= 18,如果age没索引l,MySQL没法通过索引l快速过滤 age 条件,整个查询可能退化成全表扫描。

6)隐式类型转换

name是Varchar 类型,查询写成 WHERE name = 1,MySQL会做隐式转换,相当于 WHERE CAST(nameAsignedint)=1,索引列上套了函数,索引失效。联表查询时两边字段编码不一致也有同样的问题。7)优化器认为全表扫描更划算

同一个索引,查热点数据可能走全表,查冷门数据走索引。比如订单表按商品ID 查,热门商品占了80%的订单,走索引反而要回表几十万次,不如直接全表扫描。

8)ORDER BY没配合好

ORDER BY 后面的字段不是主键,也不是覆盖索引,MySQL 可能选择全表扫描再排序,而不是走索引。

相关推荐
希望永不加班2 小时前
SpringBoot 缓存一致性:缓存与数据库双写策略
数据库·spring boot·后端·缓存·oracle
2401_883600252 小时前
Electron 中正确实现主进程异步操作的 Renderer 端回调机制
jvm·数据库·python
草莓熊Lotso2 小时前
手搓工业级 C++ 线程安全日志系统:基于策略模式解耦,兼容 glog 使用风格
linux·运维·服务器·数据库·c++·安全·策略模式
2402_854808372 小时前
c++ RAII机制详解 c++如何利用RAII管理资源
jvm·数据库·python
吕源林2 小时前
CSS如何使用Less的Merge功能合并多个属性值_通过逗号或空格组织css参数
jvm·数据库·python
qq_330037992 小时前
Go语言如何写负载均衡器_Go语言负载均衡器实战教程【完整】
jvm·数据库·python
2501_914245932 小时前
如何验证SQL删除操作的影响行数_通过ROW_COUNT获取反馈
jvm·数据库·python
2301_816660212 小时前
如何处理DG Broker的ORA-16664错误_主备库网络通信与TNS配置排查
jvm·数据库·python
2401_835956812 小时前
mysql如何配置用户只读权限_授予SELECT权限与限制操作
jvm·数据库·python