MySQL 索引
-
- 索引是什么?有什么好处?
- 讲讲索引的分类情况
- 哈希索引的使用场景是什么?却又为啥不代替redis?
- MySQL聚簇索引和非聚簇索引的区别是什么?
- 如果聚簇索引的数据更新,他的存储要不要变化?
- MySQL主键是聚簇索引吗?
- 什么字段适合当做主键?
- 性别字段能加索引吗?为啥?
- 表中十个字段,你主键用自增ID,还是UUID,为什么?
- 为什么自增ID更快一些,UUID不快吗,它在B+树里面存储是有序的吗?
- Mysql中的索引是怎么实现的?
- 查询数据时,到了B+树的叶子节点,之后的查找数据是如何做的?
- B+树的特性是什么?且与B树的区别
- MySQL为什么用B+树结构?和其他结构比的优点?
- 为什么MySQL不用跳表?
- 联合索引的实现原理
- 创建联合索引时,需要注意什么?
- [联合索引(a,b,c),现在有个执行语句是 A=XXX and C<XXX,索引怎么走](#联合索引(a,b,c),现在有个执行语句是 A=XXX and C<XXX,索引怎么走)
- [联合索引(a,b,c),查询条件where b>xxx and a = xxx and c=xxx,索引怎么走](#联合索引(a,b,c),查询条件where b>xxx and a = xxx and c=xxx,索引怎么走)
- 索引失效有哪些?
- 什么是覆盖索引?
- 如果一个列即是单列索引,又是联合索引,单独查它的话先走哪个?
- 索引的优缺点?
- 怎么决定建立哪些索引?
- 索引优化详细讲讲
索引是什么?有什么好处?
索引类似书籍的目录,是数据库额外维护的数据结构,可以减少查询时遍历的数据量,从而提升查询效率。
- 如果不走索引,查询的时候就会变成全表扫描,查询的效率为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、尽量迎合最左索引优化建立索引。把区分度高的字段,往前放。