索引是一种用于快速查询和检索数据的数据结构。 可以将其类比于书的目录。
索引底层的数据结构存在很多种类型,常见的索引结构有: B 树, B+树 和 Hash、红黑树。在 MySQL 中,无论是 Innodb 还是 MyIsam,都使用了 B+树作为索引结构。
优点:
缺点:
(我们思考这样一个问题:使用索引一定能提高查询性能吗?
大多数情况下,索引查询都是比全表扫描要快的。但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。)
Mysql默认使用的是Innodb引擎,当然MySQL也支持其他的存储引擎如MyISAM等,无论是Innodb还是MyISAM都是使用了B+树作为索引结构,下面我们来分析下索引底层数据结构选型,为什么使用B+树做索引?
哈希表存储的元素是键值对,通过键(key)即可快速取出对应的值(value),因此哈希表可以快速检索数据(接近 O(1))。
既然哈希表这么快,为什么 MySQL 没有使用其作为索引的数据结构呢?
主要是因为 Hash 索引不支持顺序和范围查询。假如我们要对表中的数据进行排序或者进行范围查询,那 Hash 索引可就不行了。并且,每次 IO 只能取一个。
(试想一种情况:
SELECT * FROM tb1 WHERE id < 1000;
在这种范围查询中,优势非常大,直接遍历比 1000 小的叶子节点就够了。而 Hash 索引是根据 hash 算法来定位的,难不成还要把 1 - 999 的数据,每个都进行一次 hash 计算来定位吗?这就是 Hash 最大的缺点了。
)
二叉查找树(Binary Search Tree)是一种基于二叉树的数据结构,它具有以下特点:
当二叉查找树是平衡的时候,也就是树的每个节点的左右子树深度相差不超过 1 的时候,查询的时间复杂度为 O(log2(N)),具有比较高的效率。然而,当二叉查找树不平衡时,例如在最坏情况下(有序插入节点),树会退化成线性链表(也被称为斜树),导致查询效率急剧下降,时间复杂退化为 O(N)。
也就是说,二叉查找树的性能非常依赖于它的平衡程度,这就导致其不适合作为 MySQL 底层索引的数据结构。
平衡二叉树(AVL 树)是计算机科学中最早被发明的自平衡二叉查找树。AVL 树的特点是保证任何节点的左右子树高度之差不超过 1,因此也被称为高度平衡二叉树,它的查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(logn)。
那为什么不使用AVL树作为索引呢?原因有如下两点:
红黑树是一种自平衡二叉查找树,通过在插入和删除节点时进行颜色变换和旋转操作,使得树始终保持平衡状态。和 AVL 树不同的是,红黑树并不追求严格的平衡,而是大致的平衡。
红黑树的应用还是比较广泛的,例如 JDK1.8 的 HashMap 底层用到了红黑树。对于数据在内存中的这种情况来说,红黑树的表现是非常优异的。
但为什么不会用红黑树来作为索引呢?
原因:
在使用红黑树时,每个树节点仅存储一个数据,而每次进行磁盘 IO 时只能读取一个节点的数据,磁盘 IO 是一项耗时的操作,而我们在设计数据库时需要最大限度地减少磁盘 IO 操作的次数。
B 树也称 B-树,全称为 多路平衡查找树 。B+树是B树的一种变化。
目前大部分数据库系统及文件系统都采用 B-Tree 或其变种 B+Tree 作为索引结构。
概述:
详细分析:
综上,B+树与B树相比,其查找过程IO次数更少,且具备更稳定的查询效率和范围查询这些优势。
(附加:
在MySQL中,InnoDB引擎和MyISAM引擎都是使用B+树作为索引结构,但两者的实现方式不太一样:
聚簇索引是索引和数据一起存放的索引。即聚簇索引的每个非叶子节点存储的是索引,而每个叶子节点存储的是索引和索引对应的数据。
主键索引其就是聚簇索引
优点:
缺点:
非聚簇索引是索引和数据分开存放的。即非聚簇索引的每个非叶子节点存放的是也是索引,但叶子节点存放的却是数据的指针或是主键。(当查询到索引对应的指针或是主键后,还需要根据指针或是主键再到数据文件或是表中进行查询)
非主键索引(二级索引即辅助索引)就是非聚簇索引。
优点:
缺点:
数据表中主键使用的就是主键索引。
一张数据表有只能有一个主键,并且主键不能为 null,不能重复。
在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在 null 值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。
主键索引就属于聚簇索引,其叶子节点data存放的是主键及主键对于的完整的数据。
二级索引(Secondary Index)又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。(然后通过回表进而查询到索引对应的数据)
我们前面说了二级索引,非聚簇索引的叶子节点存储的是主键的值,通过索引可以定位主键的位置。(然后通过回表进而查询到索引对应的数据)。那非聚簇索引一定回表查询吗(覆盖索引)?
非聚簇索引不一定回表查询。
试想一种情况,用户准备使用 SQL 查询用户名,而用户名字段正好建立了索引。
SELECT name FROM table WHERE name='AAA';
那么这个索引的 key 本身就是 name,查到对应的 name 直接返回就行了,无需回表查询。
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为 覆盖索引(Covering Index)
覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。
(注意:
覆盖索引是索引优化的一种常用手段(重点)。
由于覆盖索引的索引即为所要查询的字段的数据,无需进行回表查询,减少了树的搜索次数,显著提高了查询性能,所以使用覆盖索引是索引优化的一种常用手段
)
联合索引,即使用表中的多个字段创建索引,就是 联合索引
以 score 和 name 两个字段建立联合索引:
ALTER TABLE `Student_Grade` ADD INDEX id_score_name(score, name);
最左前缀匹配原则指的是,在使用联合索引时,MySQL 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,
如果最左边的字段不能完全匹配,那么会依次向右移动,对下一个字段进行匹配,直到所有的字段都被匹配或者无法找到匹配项为止。
所以,我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。
索引下推:是MySQL5.6版本的新特性,用于优化数据查询。当存储引擎通过索引检索到数据并返回给MySQL服务器时,服务器会判断数据是否符合条件。在使用索引条件下推优化时,如果存在某些被索引的列的判断条件,MySQL服务器将这一部分判断条件传递给存储引擎,由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器。
这种优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储引擎接收数据的次数,从而提高了查询效率。简单来说,索引下推就是将原本由上层(服务层)负责的部分任务交给了下层(引擎层)去处理。