MySQL 索引

MySQL 索引

索引的概述

MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。如下面的示意图所示 :
图1没有建立索引
在这里插入图片描述
图2左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。
为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找快速获取到相应数据。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。索引是数据库中用来提高性能的最常用的工具。

索引优势劣势

优势

  • 类似于书籍的目录索引,提高数据检索的效率,降低数据库的IO成本。
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

劣势

  • 实际上索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的。
  • 虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE。因为更新表时,MySQL 不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

索引类型

索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL目前提供了以下4种索引:

  • BTREE 索引 : 最常见的索引类型,大部分索引都支持 B 树索引。
  • HASH 索引:只有Memory引擎支持 , 使用场景简单 。
  • R-tree 索引(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少,不做特别介绍。
  • Full-text (全文索引) :全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从Mysql5.6版本开始支持全文索引。

MyISAM、InnoDB、Memory三种存储引擎对各种索引类型的支持

索引InnoDB引擎MyISAM引擎Memory引擎
BTREE索引支持支持支持
HASH 索引不支持不支持支持
R-tree 索引不支持支持不支持
Full-text5.6版本之后支持支持不支持

我们平常所说的索引,如果没有特别指明,都是指B+树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引、复合索引、前缀索引、唯一索引默认都是使用 B+tree 索引,统称为 索引。

索引结构

图1没有建立索引
如图1有一个2列7行的数据表,这时要查着张表

select * from t where t.col2 = 89;

这段 SQL 语句要逐行把 col2 字段拿出来和 89 进行比较,要查找很多次才能查找到 89 的数据。
数据存储在磁盘中,每一次的比较就相当于做一次磁盘的 IO ,磁盘的 IO 是最慢的,如果数据少性能还可以,当数据量大的情况下性能将特别的慢,系统中 IO 就会被占满。

二叉树

在这里插入图片描述

如果这时候用到数据库的索引性能将大幅的提高(如图2)。
把 col2 字段存到二叉树(早起的 mysql 索引用过二叉树)中,在二叉树每一个节点上存 2 个东西,一个 key 另一个 value ,key 是 col2 字段的具体值,value 是数据所在磁盘中的文件地址(数据表的数据分布在磁盘中并不是按着进行存储的,大部分情况下是中间有间隔的),所以每次查找都相当于和磁盘做一次 IO 操作,所以效率是不高的。
如果把数据放到二叉树中,一般从根节点开始查找,大于根节点的在右面,小于根节点的在左面,所以 89 和 34 比较,看可以知道应该去右面的子节点进行查询,直接就可以查到 89,这个时候可以拿到这条数据在磁盘中的地址可以直接去磁盘中定位这条记录。
这种方式要比直接全部扫描快很多,只用了2次的磁盘 IO,减少了很多次的 IO。

二叉树所存在的问题

二叉树在某些场景下不是特别好的能满足索引高性能查找的要求。
比如说这个场景下,把图2 中的 col1 维护在二叉树中会出现什么效果?
在这里插入图片描述
插入 7 条数据,对应二叉树就维护了 7 个索引的记录,但是这时可以发现根本就不是一个树,已经退化成了一个类似于链表的数据结构了。
这个时候我们来查询刚才的 SQL 语句,这时查找 col1

select * from t where t.col1 = 6;

这时可以发现扫描了 6 次,对应做了 6次 的 IO,跟没创建索引一样,也是全部扫描一遍。
二叉树最为索引的话用对于这种数据二叉树把它搞成了类似链表结构,没有起到快速查询减少 IO 次数的效果。
所以可以发现二叉树处理单边增长的数据不太适合作为索引的是数据结构

红黑树(二叉平衡树)

用红黑树把 col1 插入效果。

插入1
在这里插入图片描述

插入2
在这里插入图片描述

插入3
在这里插入图片描述

插入4
在这里插入图片描述

插入5
在这里插入图片描述

插入6
在这里插入图片描述

插入7
在这里插入图片描述

这个时候我们来查询刚才的 SQL 语句,这时查找 col1

select * from t where t.col1 = 6;

这时可以发现扫描了 3 次,对应做了 3次 的 IO,比刚才的二叉树要好上一些。

红黑树树所存在的问题

如果数据量特别大的情况下,比如 500W 条记录,树的高度假设是 20 层,这个时候红黑树弊端就会体现出来,500W 条记录都插入树里的话就会有层数太高的情况,对应的就是查找的时候层数也高,这时 IO 的次数就也会多,如果这时候恰好查询的记录在叶子节点上,就得消耗 20 次的 IO,才可以查询的到。
总结就是红黑树的高度太高了,查找层数太高,IO次数多。

B+Tree(多路平衡树)

现在可以针对红黑树优化一下
在这里插入图片描述

  • 在根节点中多放一些索引
  • 在每一个索引之间存放下一个节点的地址
  • 对应的下一个节点中也可以存放很多的节点,以此类推…
    同样是 500W 的数据,高度 <= 4 ,向红黑树的一个横向节点更多的存放元素,就可以减少红黑树的高度。
    我们上面所说的数据结构就是这次要介绍的BTree结构
    在这里插入图片描述
    这个BTree就是把单节点改为一个大的节点,大节点内有多个元素,元素和元素之前存放的是下一个节点的地址。
B+Tree特点

但是 MySQL 并不是完全用BTree,而是用B+Tree,如下图。
在这里插入图片描述

  • 把整张表的所有索引全部存放在叶子节点上
  • 叶子节点上都有都有数据
  • 非叶子节点是每一个下面大节点的第一个索引元素,实际上就是一些冗余索引
  • 所有节点元素放的顺序都是从小到大进行排序好了的
B+Tree查找过程

还是查询刚才的 SQL

select * from t where t.col1 = 30;

首先把根节点加载到内存中,在内存中和 30 进行比对,找到 15-56 之前的磁盘地址,接着把15-20-49这大节点加载到内存中,比对找到 20-30 的叶子节点,接着找到 30 的磁盘地址,从主键索引中进行回表查询到 col1 = 30 的全部记录。
mysql 默认会把根节点的数据存放到内存中,这参数是 innodb_page_size 默认是 160384 大小,也就是 16 kb,根节点元素如果是 bigint 类型占 8 byte,存元素的地址 mysql 底层分配 6 byte ,那么计算规则就是 16 kb ÷ (8 byte +6 byte) = 1170,也就是每个大节点可以放 1170 个元素。
叶子节点比较特殊 data 也是占用磁盘空间的,假设 data 占用1 kb(不同存储引擎占用大小不同,1kb 一般情况下错错有余),也就是说每一个叶子节点都是可以放 16kb ÷ 1kb = 16,每个叶子节点就可以放 16 个元素。
B+Tree撑满了高度为 3 的情况下就能存放 1170 × 1170 × 16 = 21,902,400,也是都撑满的情况下,叶子节点就可以放下 2000 多万个索引。
MySQL 会把非叶子节点放到内存中,也就是每次查找大概会走 2-3 次的 IO,这样性能就会大幅度的提高。可以在 2-3 次的 IO 操作内完成千万级别的表操作。

Hash 索引

Hash 索引存储的节点也是 key-value 形式的,key 具体值,value 磁盘地址
在这里插入图片描述

Hash 索引特点
  • 对索引的 key 进行一次 hash 计算就可以定位出数据存储的位置
  • 很多时候 Hash 索引要比 B+Tree 索引更高效
  • 仅能满足 “=”,“IN”,不支持范围查询
  • hash 冲突问题

引擎分类

引擎分为聚集索引(聚簇索引)和非聚集索引
聚集索引:InnoDB
非聚集索引:MyISAM

文件形式

MyISAM

在这里插入图片描述

  • frm:表结构
  • MYD:表内的数据
  • MYI:表中的索引
    在这里插入图片描述
    MyISAM 的查询过程
select * from t where t.col1 = 30;

首先通过 B+Tree 索引(MYI 文件)查询到 col1 = 30 记录在磁盘中的地址,拿到地址后,通过地址去硬盘中(MYD文件)找到具体的数据

InnoDB

在这里插入图片描述

  • frm:表结构
  • idb:表内的索引和数据

主键索引结构(InnoDB 主键索引是聚集索引)
在这里插入图片描述

普通索引结构(InnoDB 普通索引是非聚集索引,叶子节点存的是主键id,查到了具体记录拿到id,需要回表通过主键索引查到指定数据的所有字段,所有查询的时候尽可能的减少回表操作,直接查出想要的信息,减少 select * 的使用)
在这里插入图片描述

索引设计原则

索引的设计可以遵循一些已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引

  • 对查询频次较高,且数据量比较大的表建立索引。

  • 索引字段的选择,最佳候选列应当从where子句的条件中提取,如果where子句中的组合比较多,那么应当挑选最常用、过滤效果最好的列的组合。

  • 使用唯一索引,区分度越高,使用索引的效率越高。

  • 索引可以有效的提升查询数据的效率,但索引数量不是多多益善,索引越多,维护索引的代价自然也就水涨船高。对于插入、更新、删除等DML操作比较频繁的表来说,索引过多,会引入相当高的维护代价,降低DML操作的效率,增加相应操作的时间消耗。另外索引过多的话,MySQL也会犯选择困难病,虽然最终仍然会找到一个可用的索引,但无疑提高了选择的代价。

  • 使用短索引,索引创建之后也是使用硬盘来存储的,因此提升索引访问的I/O效率,也可以提升总体的访问效率。假如构成索引的字段总长度比较短,那么在给定大小的存储块内可以存储更多的索引值,相应的可以有效的提升MySQL访问索引的I/O效率。

  • 利用最左前缀,N个列组合而成的组合索引,那么相当于是创建了N个索引,如果查询时where子句中使用了组成该索引的前几个字段,那么这条查询SQL可以利用组合索引来提升查询效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值