索引是用来快速检索出具有特定值的记录,它可以大大提高查询速度。如果没有索引,数据库就必须从第一条记录开始进行全表扫描来找到相关的行。数据越多,成本就越高,检索时如果表的列存在索引,那么MySQL就能快速到达指定位置去搜索数据文件,而不必查看所有数据。
索引是存储引擎快速找到记录的一种数据结构,例如 MyISAM 引擎和 Innodb 引擎都使用 B+ Tree 作为索引结构。 ——《高性能MySQL》
MySQL中索引的存储类型有两种:BTREE和HASH,具体和表的存储引擎相关;MyISAM 和 InnoDB存储引擎只支持 BTREE 索引,MEMORY/HEAP 存储引擎可以支持HASH和BTREE索引。
以下示例展示如何在不同存储引擎中创建 BTREE 和 HASH 索引:
sqlCREATE TABLE myisam_table (
id INT AUTO_INCREMENT,
name VARCHAR(100),
PRIMARY KEY (id),
INDEX idx_name (name)
) ENGINE=MyISAM;
CREATE TABLE innodb_table (
id INT AUTO_INCREMENT,
name VARCHAR(100),
PRIMARY KEY (id),
INDEX idx_name (name)
) ENGINE=InnoDB;
磁盘读取数据,靠的是机械运动,每次读取数据花费的时间可以分成3个部分:
一次磁盘I/O的时间大约在 9ms,听起来还不错,但相比CPU指令执行时间是非常慢的。比如,计算机每秒可以执行 5 亿条指令,执行一次I/O的时间大概可以执行 40 万条指令,数据库百万级甚至千万级的数据,每次IO都用 9ms 的时间,显然和耗时。
以下是一些常见硬件操作的延迟时间对比:
从上面的数据可以看出,磁盘 I/O 的延迟时间远远高于其他硬件操作的延迟时间。
上面提到到磁盘I/O是非常高昂代价的操作,计算机系统对此也做了一些优化,当发生一次I/O时,不仅会把当前磁盘地址的数据读取到内存中,也会把相邻的数据也读取到内存缓冲区中,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快访问到。这样可以减少后续对相邻数据的磁盘 I/O 操作,提高整体性能。
每一次I/O读取的数据我们称之为一页(Page)。页(Page)是操作系统管理内存和磁盘之间数据交换的基本单位。具体一页的数据有多大,和操作系统有关,一般为4K或8K,也就是我们读取一页数据的时候,实际上才发生了一次I/O。
除此之外,数据库对数据的读取并不是以行为单位进行的,无论是读取一行还是多行,都会将该行或者多行所在的页全部加载进来,然后再读取对应的数据记录,也就是说,读取所耗费的时间与行数无关,只与页数有关。
MySQL 中所有的数据其实都是以文件的形式存储在磁盘上的,而从磁盘上随机访问对应的数据非常耗时,所以数据库程序和操作系统提供了缓冲池和内存以提高数据的访问速度。
MySQL 的基本存储结构是「页」,行记录是存储在页中的。
哈希索引通过将键值(key)通过哈希函数转换为哈希值(hash value),然后根据这个哈希值直接定位到存储位置,从而实现快速查找。其时间复杂度为 O(1)。 哈希函数是哈希索引的核心,它将输入的键值映射到一个固定范围内的哈希值。一个好的哈希函数应该具有以下特性:
哈希索引的优点
哈希索引的缺点
哈希索引的应用场景
二叉查找树是一种二叉树,满足以下性质:
优点: 因为二叉查找树是有序的,左孩子都比父节点小,右孩子都比父节点大,所以其查找效率很高,类似于二分查找可以达到O(logN). 缺点: 当二叉查找树的结构变成了下图右边这种结构之后,时间复杂度就会变成O(N)
操作
B树呢,又叫平衡多路查找树。如果每个节点最多有m个孩子,那么这样的树就是m阶B树。咱们可以看到,该图就是一个三阶B树的样子。
特征
B+树是B树的变体,其定义基本与B树相同。二者最大的不同是B+树的非叶子结点不保存数据,非叶子节点只用于索引,所有数据/记录都保存在叶子结点中。B+Tree的检索都是从根节点开始,叶子节点结束.
1)B+树包含2种类型的结点:内部结点(也称索引结点)和叶子结点。根结点本身即可以是内部结点,也可以是叶子结点。根结点的关键字个数最少可以只有1个。
2)B+树与B树最大的不同是内部结点不保存数据,只用于索引,所有数据(或者说记录)都保存在叶子结点中。
3) m阶B+树表示了内部结点最多有m-1个关键字(或者说内部结点最多有m个子树),阶数m同时限制了叶子结点最多存储m-1个记录。
4)内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
5)每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接
特征
之所以B+树的非叶子节点选择只存储索引,叶子节点存储数据的原因是在数据库中页的大小是相对固定的(InnoDB 中页的默认大小是 16KB),如果不存储数据,那么就会存储更多的键值和指针,相应的树的阶数就会更大,树的高低会更低,我们查找数据进行磁盘的 IO 次数会再次减少,数据查询的效率也会更快。比如,假设,有一个B+ 树单节点可以存储 1000 个键值,3 层的 B+ 树可以存储 1000 × 1000 × 1000 = 10 亿 个数据。一般根节点是常驻内存的,所以一般我们查找 10 亿数据,只需要 2 次磁盘 IO。
综上,B+树的磁盘IO成本更低,因为B+树的非叶子节点只存放索引的信息,不存放数据,可以指向更多的数据,一次性读取更多的要查找的关键字到内存中,因此相对来说IO的次数,也就降低了;B+树的查询效率更稳定,以任何数据的查找都是从根节点到叶子节点;B+Tree更利于对数据库的扫描,可以直接只在叶子节点里根据链表指针做扫描,不用每次都遍历整棵树
举个例子
举个例子
树的高度直接影响了查询的性能,一般树的高度维持在 3~4 层。
磁盘IO次数取决于B+树的高度 h。假设当前数据表的数据为 N,每个磁盘块的数据项的数量是 m,则有B+树的高度h为 h = log(m + 1)N 当数据量 N 一定的情况下,m 越大,h 越小。
密集索引是一种索引结构,其中索引的每个键值都直接对应一条数据记录的物理位置。叶子节点不仅存储键值,还存储与该键值相关的完整数据记录。由于密集索引决定了表的物理排列顺序,一个表只能有一个物理排列顺序,所以一个表只能创建一个密集索引。在密集索引中,数据记录按索引键值的顺序存储在磁盘上。叶子节点包含了实际的数据记录或指向数据记录的指针。
稀疏索引只为某些键值建立,而不是每个键值都建立索引项。在稀疏索引中,只有部分数据记录有对应的索引项,索引项之间的间隔可以是固定的或者根据需要调整。叶子节点包含键值和指向数据记录的地址或主键。查找数据时,首先通过稀疏索引定位到最近的索引项,然后通过该索引项中的地址或主键进一步查找实际的数据记录。由于不是每个数据记录都有索引项,查找过程可能需要额外的步骤来定位具体的数据记录。
InnoDB必须有且仅有一个密集索引,这个密集索引的选取规则如下:
InnoDB会有一个密集索引,将主键组织到一颗B+树中,而行数据就存储在叶子节点上,因为InnoDB的主键索引和对应的数据是保存在同一个文件当中的,在检索的时候,加载叶子节点的主键进入内存的同时,也加载了对应的数据。即若使用 where id =xx 这样的条件查询主键,则按照B+树的检索算法即可查找到对应的叶子节点,并获得对应的行数据。若对稀疏索引进行条件筛选,则需要经历两个步骤,第一步,在稀疏索引的B+树中检索该键定位到主键信息,获取主键信息后还要经过第二步,第二步就是使用主键 where id =xx 在密集索引的B+树中再执行一次检索操作,最终再到达叶子节点,获取整行的数据。
本文作者:sora
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!