MySQL 索引一篇讲透:原理、分类、优化与面试总结

MySQL 索引

索引是什么?有什么好处?

索引类似书籍的目录,是数据库额外维护的数据结构,可以减少查询时遍历的数据量,从而提升查询效率。

  • 如果不走索引,查询的时候就会变成全表扫描,查询的效率为O(N)。
  • 走了索引 ,那之后查询就可以基于二分查找算法进行快速查询,MySQL底层用的是B+树,所以查询速度可以达到O(LogbN)。其中b是B+树的阶数,代表每个非叶子节点可以指向多少个子节点。

讲讲索引的分类情况

1、数据结构分类:

如果按照数据结构分类,分为三种类型:B+树索引、Hash索引、Full-Text索引

  • B+树,是一种专门优化磁盘查询的平衡多路查找树。Innodb索引的底层默认使用B+树结构。
  • Hash索引,底层采用的是哈希表,他的查询速度比B+树还快,但是范围查询表现比较差。
  • Full-Text全文索引,底层采用倒排索引,会对传进来的数据进行分词,并做一个映射表。像Elasticsearch就用的倒排索引。

而,Innodb引擎支持B+树索引,与Full-Text全文索引,不支持Hash索引

2、物理存储分类:

分为 聚簇索引 (主键索引) 与 非聚簇索引 (二级索引、辅助索引)

他们两个都是把 某种字段 作为键 B+树 的key值。

一张表,只能有一个聚簇索引

他是取目标表的主键作为索引,建立B+树。

若该表没有设立主键,则取其不含NULL值的唯一索引,建立B+树。

若连符合条件的唯一索引也没有的话,则自建一个隐藏列,作为索引列。

非聚簇索引 ,可以有很多个,他的作用就是,加速查询数据效率。

几乎所有字段,都可以被用来建立非聚簇索引。

但是对于像 text/blob 这种字段,不建议建立索引。因为有了跟没有,几乎没啥区别。

此外,聚簇索引的叶子节点,都是存的整行完整数据。故其还有一个索引即数据的称呼。

而非聚簇索引的叶子节点只会存储主键数值。

所以,如果你走了非聚簇索引,并且要查询的值,不是主键值,那就必须去先在非聚簇索引中得到目标主键,然后再去聚簇索引中查找目标字段。这个过程被成为回表。但若你查的数据在非聚簇索引中就能找到,不在需要回表,这种行为又被称为覆盖索引。

3、按字段特性分类

有主键索引、唯一索引、普通索引、前缀索引。

  • 主键索引:以主键字段,作为建立索引的键值。通常一张表只能有一个,在innodb中又叫聚簇索引。
  • 唯一索引:以unique字段,作为建立索引的键值,允许为空,一张表可以有多个。
  • 普通索引:不指定建立索引的字段的有其他特性。可以有多个。
  • 前缀索引:不直接使用一整个字段的数据,通常取前k个字符建立索引。如char、varchar类型。

4、按字段个数分类

通常有单列索引 ,仅有一个字段(如主键)。

另一个是联合索引,可以有多个字段结合在一起,共同作用在一起。

哈希索引的使用场景是什么?却又为啥不代替redis?

哈希索引,底层用的是哈希表,通常由mysql中的memeory引擎支持,可以快速查询。

但是功能单一,不适合范围查询、没有过期策略等功能,只是作为关系型数据库的附属产品。同时又因为存在内存中,关机即丢失。所以大家往往选择redis,因为他更加专业,且支持持久化。

MySQL聚簇索引和非聚簇索引的区别是什么?

数据存储不同 :虽然两者都采用的B+树进行存储,但聚簇索引的叶子节点内存的是键值+对应行的所有数据。而非聚簇索引只存了目标行的主键值。
唯一性 :聚簇索引只能有一个,非聚簇索引可以有多个
效率问题:通过聚簇索引,可以获取目标行的所有数据,可以不用在做其他多余寻址操作。但是非聚簇索引,在不发生覆盖索引的情况下,就必须要回表。

如果聚簇索引的数据更新,他的存储要不要变化?

分为两种场景。

1、修改的其他字段: 如果修改的字段是在叶子节点存储的业务数据,由于不涉及主键,则不会对B+树造成重排、页分裂的影响。

2、修改的主键: 如果修改的字段是维护聚簇索引的主键,则为了保持B+树的特性,B+树可能会触发重排、页分裂。

MySQL主键是聚簇索引吗?

只要有主键,就一定是聚簇索引。通常一张表只会有一个聚簇索引。

  • 最初就有主键时,则会将主键作为聚簇索引的索引。
  • 若表建立初期,没有主键索引,则会寻找一个无null值得,唯一索引做聚簇索引。
  • 既无主键,有无符合条件得唯一索引,则Innodb会自动建隐藏一个自增的row_id,作为聚簇索引。若后期一点手动增加了主键,Innodb引擎则会立马对聚簇索引进行重建。

什么字段适合当做主键?

  • 适合的:不可重复,且最好能连续递增,这样可以更好的按顺序插入,而不必担心频繁的页分裂。
  • 不适合的:对于以后可能会重复的业务字段,则不适合当主键(像学号、会员id这些,不知道后期是否可重复),在分布式的场景下,默认自增的也不一定行,如果表数据合并时,可能会造成重复。

性别字段能加索引吗?为啥?

可以加索引,但是不建议 加索引。

假设有100w个行数据,则男/女分别有50w,他的区分度很低不说,因为是非聚簇索引,查询的时候最坏的情况是造成50w次回表

所以优化器,可能压根都不会走这种方案。

可以通过建立(联合索引、覆盖索引)进行优化。

表中十个字段,你主键用自增ID,还是UUID,为什么?

自增ID。
选择自增ID的原因:

  • 自增ID,可以使加入的数据,紧挨着上条数据插入目标页,使磁盘利用率更高。
  • 由于自增ID是按照顺序插入的,所以大大减少了页分裂的频率。

不选择UUID的原因:

  • 由于uuid的无序性,每次插入时,可能都是无序插入。频繁插入时,所需目标页可能刚被刷到磁盘上,也能压根还没被加载到磁盘上,加载就需要花费额外时间。
  • 且它的随机插入,不仅会导致页分裂更频繁,而且由于随机性,也会导致有大量磁盘碎片,资源利用率不高。
  • 同时因为UUID,占用36个字符。因为体积较大的原因,会导致一个数据页能存的索引数量减少,会造成B+树的层级更高,从而导致一次查询需要多次磁盘IO。且作比较的时候,需要从字符串的头部遍历到尾部,耗费的时间也会更长。

为什么自增ID更快一些,UUID不快吗,它在B+树里面存储是有序的吗?

回答如上一条。

Mysql中的索引是怎么实现的?

Mysql 中 Innodb引擎 默认把 B+树 作为索引的数据结构。

从上向下来说,

  • 上层非叶子节点只会存储用于排序的主键,不存完整的数据,只是用来快速定位。
  • 叶子节点则存了所有的字段信息,包括主键。
  • 并且各个叶子节点之间,会存储额外的指针,形成一个双向链表,方便范围查询。

    并且在Mysql中,每个节点,都是一个数据页(16KB大小),所以千万级别的数据,一般也就3~4层,所以一次插叙的磁盘IO通常也就3 ~ 4次。

查询数据时,到了B+树的叶子节点,之后的查找数据是如何做的?

1、叶子节点本质上就是一个数据页,而数据页内会的每条数据都是通过单链表连接。

为了优化查询效率。

2、Innodb引擎为每个数据页都维护了一个页目录 ,这个页目录将内部的所有数据分组放置 。其中组内数据都是按照主键大小进行顺序放置。

3、并且会为每一组,都会维护一个数据槽(slot)。其中数据槽内存的都是每组数据的最后一个数据的偏移量。

之后查询的话,会先基于数据槽进行二分查找,之后在对找到的目标组进行顺序遍历。

B+树的特性是什么?且与B树的区别

  • 所有叶子节点都在同一层 :B+树的所有叶子节点都在同一层,并且各个叶子节点之间以双链表形式连接,方便范围查询。也正以内所有的叶子节点存储在同一层且只在 叶子节点存储详细数据,所以每次磁盘IO次数都会非常稳定。而B树并非在同一层,叶子节点间也无连接,并且每个节点都会存有详细信息。
  • 非叶子节点存储键值:B+树的非叶子节点只存主键索引,与其子节点的指针,所以可以存储更多数据。所以相较于B树会更矮,磁盘IO会更少。
  • 自平衡 :B+树是严格的自平衡。不论插入 /删除 操作时会通过分裂与合并,来严格保持叶子节点在同一层。虽然B树也是自平衡,但不会像B+树那样非常严格。

MySQL为什么用B+树结构?和其他结构比的优点?

  • B+树与B树:上一段已经讲解的很清楚了。
  • B+树与二叉树:二叉树只有两个分叉,B+树却是多路平衡,如果选则二叉树的话会造成
  • B+树与哈希表:哈希表虽然查询的速度极快O(1),但是范围查询的效率远远低于B+树。

为什么MySQL不用跳表?

跳表是 多层级的有序链表 ,通过多层链表的加速查询,近似二分查找。

最底层是一条有序的链表,存着所有数据。上方是逐层精简的加速链表

查询时,方便向下快速定位跳转,俗称跳表

跳表更适合内存查询,若用到MySQL中,因为指针的无序性,可能会触发多次磁盘IO。

而B+树,恰巧不仅能解决大量随机磁盘IO,并且对千万级别的存储量数据进行查询时,通常也就只需要3~4次磁盘IO。

只有在内存中,跳表的优势才能完全发挥出来。而对于磁盘来说,指针随机跳转劣势将会无限放大,还是B+树更适合它。

go 复制代码
第3层:      1 ----------- 7
第2层:      1 ----- 5 ---- 9
第1层:      1 -- 3 -- 5 -- 7 -- 9
第0层:      1 -> 3 -> 5 -> 7 -> 9 -> 11

联合索引的实现原理

是什么 :其实就是多个字段共同组成一个索引,这就叫做联合索引。
例子 :就像商品表中的 product_id商品ID 与 name商品名称,这两个字段,共同组成一个索引 (product_id,name)。他们两者在 B+树 中被一同存在一起,建立索引时,会先用第一个 id字段 排序,如果 id 相同,再用 第二个 name 字段进行排序。
特性 :它遵循最左匹配原则 。就拿上方举出的例子来说,他会先对比 product_id,在对比 name

虽然索引是由两个字段组成。可是你查询时,在where后面接 name=...,则不会触发联合索引。

但若你用第一个字段 product_id,就能走这条索引。这就是最左匹配!

创建联合索引时,需要注意什么?

因为最左匹配原则的特性,所以联合索引会优先匹配最前方的字段。
要点一:区分度

因此,要把区分度最高字段的放到最前方,就像UUID这种,放到最前方。

如果像 性别 这种,是不建议添加索引,若非要放到联合索引中,那是越往后放越好。

因为查询优化器的存在,如果索引的区相似度太高(通常大于30%)可能会直接不走索引,转身进行全表扫描。
要点二:覆盖索引

在做业务的时候,建立联合索引的时,可权衡所需字段尽量都放到联合索引中,这样可以避免回表。

联合索引(a,b,c),现在有个执行语句是 A=XXX and C<XXX,索引怎么走

因为最左匹配原则,A可以走联合索引,C不行,C只能对A过滤之后的数据,进行全数据扫描。当然现在索引下推可以对其进行优化。

联合索引(a,b,c),查询条件where b>xxx and a = xxx and c=xxx,索引怎么走

因为最左匹配原则,两者都会走联合索引。但是c不行。

索引失效有哪些?

1、不符合最左匹配原则,跳过最开头的字段,则无法进行范围查询。

2、出现范围查询后,下一个字段就无法进行联合查询。

3、左模糊匹配,或者左右模糊匹配都不行。

4、带有函数运算数学操作类型转换等,都不可以。

5、or 连接中,任何一个字段不在 联合索引中,也会失效。

什么是覆盖索引?

所需的字段,在非聚簇索引中就能获取,这种行为叫做覆盖索引

如果没有找到,而只能拿着存在叶子节点的主键,再去聚簇节点中查询,这就叫做回表

注:联合索引,有就非常适合覆盖索引。

如果一个列即是单列索引,又是联合索引,单独查它的话先走哪个?

这个mysql查询优化器,会通过成本判断,谁低走谁。

索引的优缺点?

优点 是加速查询过程,降低磁盘IO次数。
坏处

1、索引的建立,需要消耗额外的磁盘空间。

2、需要花费额外的时间,对B+树的索引进行维持。

3、会降低增删改的效率,因为每次操作,都要额外花费时间对B+树进行动态维护

怎么决定建立哪些索引?

建立哪些索引,就意味着这些索引,值不值得建立。

需要建立:

1、需要频繁的进行where查询。

2、经常需要用到 order by / gourp by 这种顺序范围的查询,因为B+树内存的数据本身就是有序的。

不需要建立:

1、如果数据量很少,压根就用不到。

2、如果where / order by / group by 这种查询,几乎不会使用的字段,也不用。

3、如果 增/删/改 的字段,也不建议,因为频繁的更改,会导致维护B+树的成本增大。

4、区分度记低的也不建议。

索引优化详细讲讲

1、覆盖索引优化,尽量使所需值,在非聚簇索引中就有,避免回表。

2、前缀索引优化,前缀所以只取所需字段的前几个字符,既可以降低磁盘存储,又可以加快匹配速度,从而提升查询速度。

3、主键的选值,尽量可以自增,从而避免UUID这种,频繁插入导致磁盘IO过多页分裂加剧

4、尽量迎合最左索引优化建立索引。把区分度高的字段,往前放。

相关推荐
M ? A2 小时前
Vue Transition 组件转 React:VuReact 怎么处理?
前端·javascript·vue.js·经验分享·react.js·面试·vureact
小江的记录本2 小时前
【分布式】分布式一致性协议:2PC/3PC、Paxos、Raft、ZAB 核心原理、区别(2026必考Raft)
java·前端·分布式·后端·安全·面试·系统架构
熙客2 小时前
MySQL数据库压力测试:Sysbanch
数据库·mysql·压力测试
HalvmånEver2 小时前
MySQL数据库操作
linux·数据库·学习·mysql
_Evan_Yao11 小时前
技术成长周记06|面试中看清差距,新项目点燃热情
面试·职场和发展
数据库小组12 小时前
MySQL 删库后怎么恢复?binlog2sql 之外,NineData 还能做什么
数据库·sql·mysql·安全·数据·ninedata·删库
haina201915 小时前
《品牌观察》专访海纳AI:引领AI面试测评新时代
人工智能·面试·职场和发展
kyriewen15 小时前
你的首屏慢得像蜗牛?这6招让页面“秒开”
前端·面试·性能优化
Raink老师16 小时前
【AI面试临阵磨枪】什么是 MCP(Model Control Protocol)、A2A(Agent-to-Agent)协议?
人工智能·面试·职场和发展·ai 面试