八股学习(三)---MySQL

一、MySQL中的回表是什么?

我的回答:

MySQL回表指的是在查询使用非聚簇索引也就是二级索引时,叶子节点只存储了索引列的值和主键Id,若要查询其他字段,就要根据主键去聚簇索引查询完整的数据。这个过程就是回表。比如用name的二级索引查age,要先通过name找到主键id,再用这个主键id查询age。回表会增加 IO ,所以可以建立覆盖索引来包含所需要的字段,避免回表。我练过用联合索引包含其他字段,减少了回表,查询快了不少~

回答重点(官方答案):

"回表" 是指在使用二级索引(非聚簇索引)作为条件进行查询时,由于二级索引中只存储了索引字段的值和对应的主键值,无法得到其它数据。如果要查询数据行中的其它数据,需要根据主键去聚簇索引查找实际的数据行,这个过程被称为回表。

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

我的回答:

MySQL中使用索引不一定有效,比如索引字段上有函数运算、使用了like '%xxx'这类前缀模糊匹配查询、类型不匹配,或者统计信息不准确等, 都可能导致索引失效。

排查索引效果,我们可以使用explain分析SQL执行计划,重点看type字段,(如all表示全表扫描)、key字段(是否为null),以及rows估算值

再通过show status like 'Handler%'; 查看索引使用次数,对比查询前后的Handler_read_key和Handler_read_rnd_next等指标。之前我在练习时,发现一条SQL有索引,但是执行慢,用explain发现是因为对索引字段做了函数操作,调整后索引生效,查询效率显著提升,所以现在写SQL后都会习惯性用这些方法检验索引效果,避免出校"有索引但不生效"的情况。

回答重点(官方答案):

索引不一定有效。

例如查询条件中不包含索引列、低基数列索引效果不佳,或查询条件复杂且不匹配索引的顺序。

对于一些小表,MySQL 可能选择全表扫描而非使用索引,因为全表扫描的开销可能更小。

最终是否用上索引是根据 MySQL 成本计算决定的,评估 CPU 和 I/O 成本最终选择用辅助索引还是全表扫描。有时候确实是全表扫描成本低所以没用上索引。但有时候由于一些统计数据的不准确,导致成本计算误判,而没用上索引。

排查索引效果的方法:使用 EXPLAIN 命令,通过在查询前加上 EXPLAIN ,可以查看 MySQL 选择的执行计划,了解是否使用了索引、使用了哪个索引、估算的行数等信息。

主要观察 EXPLAIN 结果以下几点:

  • type(访问类型):这个属性显示了查询使用的访问方法,例如 ALL 、index 、range 等。当查询使用索引时,这个属性通常会显示为 index 或 range ,表示查询使用了索引访问。如果这个值是 ALL ,则表示查询执行了全表扫描,没有使用索引。
  • key(使用的索引):这个属性显示了查询使用的索引,如果查询使用了索引,则会显示索引的名称。如果这个值是 NULL ,则表示查询没有使用索引。
  • rows(扫描的行数):这个属性显示了查询扫描的行数,需要评估下扫描量。

扩展知识

确定索引真的生效了吗?

索引失效的场景有很多,也是面试官经常喜欢问的,可以根据具体场景进行排查,典型场景可以分为以下几点(实际索引的选择会根据 mysql 优化器的成本评定,答案最后会提到):

  1. 使用了联合索引却不符合最左前缀

    举个例子:小鱼对 user 表建立了一个联合索引为 name_age_id 的联合索引。

    他使用以下 SQL 查询 select * from user where age = 10 and id = 1;

    这样的写法恰恰不满足最左前缀原则,索引就失效啦。

  2. 索引中使用了运算

    例如这个 SQL select * from user where id + 3 = 8 。这样会导致全表扫描计算 id 的值再进行比较,使得索引失效。

  3. 索引上使用了函数也会失效

    例如:select * from user where LOWER (name) like 'cong%'; 。

    这样也会导致索引失效,索引参与了函数处理,会导致去全表扫描。

  4. like 的随意使用

    例如:select * from user where name like '% cong%'; 因为索引是从左到右来进行排序查找的,占位符直接放在了最左边开头,可能会导致直接全表扫描,这种情况就会导致索引失效。

  5. or 的随意使用

    user 当前只有一个索引 name 。此时执行以下 SQL:

    select * from user where name = 'cong' or age = 18; 这可能也会导致索引失效,因为 age 没有索引。

  6. 随意的字段类型使用

    不小心将 varchar 类型的 name 条件匹配了 int 类型字段。SQL 是这样的 select * from user where name = 1; ,在代码中涉及隐式转换!等于 select * from user where CAST (name AS signed int) = 1; ,这就变成了第三条索引上使用了函数,导致索引失效。

    除此之外还有隐式字符编码转换的问题,即联表查询的时候,如果不同表之间的关联字段字符编码不一致,也会导致隐式转换编码,等于变相用上了函数,使得索引失效。

  7. 不同的参数也会导致索引失效

    这个就是我在回答重点里面说的 "是否用上索引是根据 MySQL 成本计算决定的"。不同的参数 MySQL 评估成本不一致,有时候会选择使用索引,有时候会选择全表扫描,特别是在复杂查询(联表、子查询、需要回表等)的情况下。

比如根据商品从订单表查询,收集商品对应的所有买家的订单信息。如果传入的商品 id 是个热点商品,占据这家店铺 80% 的销量,那么本次查询对订单表很可能是全表查询,如果是冷门商品,则很可能是走索引查询。

  1. 表中两个不同字段进行比较

例如这样的 SQL : select * from user where id > age; ,将 id 跟 age 字段做了比较,索引失效。

  1. 使用了 order by

当 order by 后面跟的不是主键或者不是覆盖索引会导致不走索引。

为什么索引生效了反而查询变慢了呢?

确认是否选对了索引!MySQL 根据优化器会评估成本选择对应的索引,但有时候 MySQL 因为估计值不准确,导致选错了索引,因此查询速度反而更慢。

三、在MySQL中建立索引时需要注意哪些事项?

我的回答:

我会注意:

1.按需创建索引:根据实际查询需求,在where,join,order by等子句的字段上建索引,避免冗余。

2.会优先给唯一性高的,如id、手机号建立索引,像性别低选择性字段建立索引意义不大。

3.若是联合索引,遵循最左前缀原则,把选择性高的字段放在前面,比如(a,b,c)的索引能支持a、a+b、a+b+c的查询

4.避免失效场景,不在字段上使用函数,避免使用like %xxx、保证类型匹配,防止索引失效

5.权衡性能:索引会加快查询,但是会降低增删改的速度,还会占用磁盘空间,因此写入频繁的表要控制索引数量。

我练习的时候,给订单表的user_id 和 create_time建立联合索引,发现遵循最左原则的查询能命中索引,但是加了低选择性的status字段后反而失效,删除后性能恢复了,这让我明白索引设计要结合实际场景,不能盲目添加。

类型匹配解释:

加了低选择性的status字段,索引失效解释:

重点回答(官方答案)

  1. 不能盲目建立索引,索引并不是越多越好,索引会占用空间,且每次修改的时候可能都需要维护索引的数据,消耗资源。

  2. 对于字段的值有大量重复的不要建立索引。比如说:性别字段,在这种重复比例很大的数据行中,建立索引也不能提高检索速度。但是也不绝对,例如定时任务的场景,大部分任务都是成功,少部分任务状态是失败的,这时候通过失败状态去查询任务,实际上能过滤大部分成功的任务,效率还是可以的。

  3. 对于一些长字段不应该建立索引。比如 text、longtext 这种类型字段不应该建立索引。因为占据的内存大,扫描的时候大量加载至内存中还耗时,使得提升的性能可能不明显,甚至可能还会降低整体的性能,因为别的缓存数据可能因为它被踢出内存,下次查询还需要从磁盘中获取。

  4. 当数据表的修改频率远大于查询频率时,应该好好考虑是否需要建立索引。因为建立索引会减慢修改的效率,如果很少的查询较多的修改,则得不偿失。

  5. 对于需要频繁作为条件查询的字段应该建立索引。在 where 关键词后经常查询的字段,建立索引能提高查询的效率,如果有多个条件经常一起查询,则可以考虑联合索引,减少索引数量。

  6. 对经常在 order by、group by、distinct 后面的字段建立索引。这些操作通常需要对结果进行排序、分组或者去重,而索引可以帮助加快这些操作的速度。

扩展知识

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

回答重点

MySQL 索引的最左前缀匹配原则指的是在使用联合索引时,

查询条件必须从索引的最左侧开始匹配。如果一个联合索引包含多个列,查询条件必须包含第一个列的条件,然后是第二个列,以此类推。
底层原理: 因为联合索引在 B+ 树中的排列方式遵循 "从左到右" 的顺序,例如联合索引 (first_name, last_name, age) 会按照 (first_name, last_name, age) 的顺序在 B+ 树中进行排序。

MySQL 在查找时会优先使用 first_name 作为匹配依据,然后依次使用 last_name 和 age 。因此,组合索引能够从左到右依次高效匹配,跳过最左侧字段会导致无法利用该索引。

按照 (first_name, last_name, age) 的顺序在 B+ 树中的排列方式 (大致的示意图) 如下

相关推荐
永远不会的CC3 分钟前
浙江华昱欣实习(4月23日~ 4月19日)
后端·学习
二哈赛车手3 分钟前
新人笔记---实现简易版的rag的bm25检索(利用ES),以及RAG上传时的ES与向量数据库双写
java·数据库·笔记·spring·elasticsearch·ai
xxjj998a7 分钟前
Laravel4.x核心特性全解析
android·mysql·laravel
爱上好庆祝10 分钟前
学习js的第五天
前端·css·学习·html·css3·js
何中应13 分钟前
CentOS 7安装、卸载MySQL数据库(二)
数据库·mysql·centos
qiaozhangchi21 分钟前
求解器学习笔记
笔记·python·学习
KmSH8umpK23 分钟前
Redis分布式锁从原生手写到Redisson高阶落地,附线上死锁复盘优化方案进阶第六篇
数据库·redis·分布式
梁萌1 小时前
mysql使用事件做日志表数据转移
数据库·mysql
lThE ANDE1 小时前
MySQL中的TRUNCATE TABLE命令
数据库·mysql
kexnjdcncnxjs1 小时前
Redis如何记录每一次写操作_开启AOF持久化机制实现命令级追加记录
jvm·数据库·python