文章目录
😊 @ 作者:Lion J
💖 @ 主页: https://blog.csdn.net/weixin_69252724
🎉 @ 主题: MySQL__索引)
⏱️ @ 创作时间:2024年04月23日
————————————————
这里写目录标题
- 文章目录
- 索引介绍
- 索引是什么?
- 索引的优缺点
- 索引类型
- 索引的数据结构
- ●索引为什么不是Hash表?
- ●索引为什么不是BST?
- ●索引为什么不是AVL(自平衡树)?
- ●红黑树介绍
- B 树& B+树
- 索引类型总结
- 主键索引(一级索引)
- 二级索引
- 聚簇索引与非聚簇索引
- 覆盖索引与联合索引
- 索引下推
- 原理
索引介绍
索引是什么?
●MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。
● 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。
我们通常所说的索引,聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特别说明,默认都是使用B+树结构组织的索引。
索引的优缺点
优点
- 可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
- 通过创建唯一索引,可以保证数据表的每行数据的唯一性
缺点
- 创建维护需要耗费时间; 对表的数据做增删改的时候, 对应的索引也需要做动态的修改,会降低SQL的执行效率
- 索引要用物理文件存储, 耗费空间
如果按照索引列的顺序进行排序,对应order by语句来说,效率就会提高很多。
索引类型
主键索引
索引列中的值必须是唯一的,不允许有空值。
普通索引
MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值。
唯一索引
索引列中的值必须是唯一的,但是允许为空值。
全文索引
只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。字段长度比较大时,如果创建普通索引,在进行like模糊查询时效率比较低,这时可以创建全文索引。 MyISAM和InnoDB中都可以使用全文索引。
空间索引
MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。
前缀索引
在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。
索引的数据结构
对于数据表中的数据来说
1.存储的数据是非常非常多的
2.并且还不断的动态变化
最终选择B+数来做索引的数据结构
●MySQL索引的底层数据结构是B+树
B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,nnoDB存储引擎就是用B+Tree实现其索引结构。
B-Tree结构图中每个节点中不仅包含数据的key值,还有data值,而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘IO次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
●B+Tree相对于B-Tree有几点不同:
非叶子节点只存储键值信息。
所有叶子节点之间都有一个链指针。
数据记录都存放在叶子节点中
●下面做一个推算:
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为10^3) 。也就是说一个深度为3的B+Tree索引可以维护10^3 *10^3 *10^3= 10亿 条记录。
实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2-4层。MSQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/0操作。
数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondaryindex)。上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。
●索引为什么不是Hash表?
哈希表我们都知道, 他的访问速度是非常快的,由于底层是通过hash的散列算法,可以快速得到对应key的index索引值,从而可以快速地找到对应的value
主要原因:
还是哈希表不支持快速查找和范围性查找, 由于哈希表每次只能IO一次, 那么在此场景下, 效率就会非常的慢
●索引为什么不是BST?
- 左子树所有节点的值均小于根节点的值。
- 右子树所有节点的值均大于根节点的值。
- 左右子树也分别为二叉查找树。
当二叉查找树是平衡的时候,也就是树的每个节点的左右子树深度相差不超过 1 的时候,查询的时间复杂度为 O(log2(N)),具有比较高的效率。然而,当二叉查找树不平衡时,例如在最坏情况下(有序插入节点),树会退化成线性链表(也被称为斜树),导致查询效率急剧下降,时间复杂退化为 O(N)。
●索引为什么不是AVL(自平衡树)?
AVL 树的特点是保证任何节点的左右子树高度之差不超过 1,因此也被称为高度平衡二叉树,它的查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(logn)。
VL 树采用了旋转操作来保持平衡。主要有四种旋转操作:**LL 旋转、RR 旋转、LR 旋转和 RL 旋转。**其中 LL 旋转和 RR 旋转分别用于处理左左和右右失衡,而 LR 旋转和 RL 旋转则用于处理左右和右左失衡。
1. 由于 AVL 树需要频繁地进行旋转操作来保持平衡,因此会有较大的计算开销进而降低了数据库写操作的性能。
2. 在使用 AVL 树时,每个树节点仅存储一个数据,而每次进行磁盘 IO 时只能读取一个节点的数据,如果需要查询的数据分布在多个节点上,那么就需要进行多次磁盘 IO。
磁盘 IO 是一项耗时的操作,在设计数据库索引时,我们需要优先考虑如何最大限度地减少磁盘 IO 操作的次数。
●红黑树介绍
红黑树是一种自平衡二叉查找树,通过在插入和删除节点时进行颜色变换和旋转操作,使得树始终保持平衡状态,它具有以下特点:
- 每个节点非红即黑;
- 根节点总是黑色的;
- 每个叶子节点都是黑色的空节点(NIL 节点);
- 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
- 从任意节点到它的叶子节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)
6.
和 AVL 树不同的是,红黑树并不追求严格的平衡,而是大致的平衡。
正因如此,红黑树的查询效率稍有下降,因为红黑树的平衡性相对较弱,可能会导致树的高度较高,这可能会导致一些数据需要进行多次磁盘 IO 操作才能查询到,这也是 MySQL 没有选择红黑树的主要原因。也正因如此,红黑树的插入和删除操作效率大大提高了,因为红黑树在插入和删除节点时只需进行 O(1) 次数的旋转和变色操作,即可保持基本平衡状态,而不需要像 AVL 树一样进行 O(logn) 次数的旋转操作。
B 树& B+树
B 树也称 B-树,全称为 多路平衡查找树 ,B+ 树是 B 树的一种变体。
●啥是B+树?
- B 树的所有节点既存放键(key) 也存放数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
- B 树的叶子节点都是独立的;
- B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
- B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
- 在 B 树中进行范围查询时,首先找到要查找的下限,然后对 B 树进行中序遍历,直到找到查找的上限;而 B+树的范围查询,只需要对链表进行遍历即可。综上,B+树与 B 树相比,具备更少的 IO 次数、更稳定的查询效率和更适于范围查询这些优势。
●为什么MyISAM与InnoDB底层都是B+树,但是执行的效果不一样?
6. MyISAM 引擎中,B+Tree 叶节点的 data 域存放的是数据记录的地址。在索引检索的时候,首先按照 B+Tree 搜索算法搜索索引,如果指定的 Key 存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引(非聚集索引)”。
7. InnoDB 引擎中,其数据文件本身就是索引文件。相比 MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。这个索引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。这被称为“聚簇索引(聚集索引)”,而其余的索引都作为 辅助索引 ,辅助索引的 data 域存储相应记录主键的值而不是地址,这也是和 MyISAM 不同的地方。
在根据主索引搜索时,直接找到 key 所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂
索引类型总结
按照底层存储方式角度划分:
●聚簇索引(聚集索引):索引结构和数据一起存放的索引,InnoDB 中的主键索引就属于聚簇索引。●非聚簇索引(非聚集索引):索引结构和数据分开存放的索引,二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。
按照应用类型划分:
●主键索引:加速查询 , 列值唯一(不可以有 NULL), 表中只有一个。
●普通索引:仅加速查询。
●唯一索引:加速查询 , 列值唯一(可以有 NULL)。
●覆盖索引:一个索引包含(或者说覆盖)所有需要查询的字段的值。
●联合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并。
●全文索引:对文本的内容进行分词,进行搜索。目前只有 CHAR、VARCHAR ,TEXT 列上可以创 建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
主键索引(一级索引)
数据表的主键列使用的就是主键索引。
一张数据表有只能有一个主键,并且主键不能为 null,不能重复。在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在 null 值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。
这也是为了使得底层的B+树效率上上升, 用上底层的数据结构
二级索引
二级索引(Secondary Index)的叶子节点存储的数据是主键的值,也就是说,通过二级索引可以定位主键的位置,二级索引又称为辅助索引/非主键索引。
●唯一索引(Unique Key):唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
●普通索引(Index):普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。
●前缀索引(Prefix):前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,因为只取前几个字符。
●全文索引(Full Text):全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
聚簇索引与非聚簇索引
●聚簇索引(聚集索引)
聚簇索引(Clustered Index)即索引结构和数据一起存放的索引,并不是一种单独的索引类型。InnoDB 中的主键索引就属于聚簇索引。
在 MySQL 中,InnoDB 引擎的表的 .ibd文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
聚簇的优点
- 查询速度非常快:聚簇索引的查询速度非常的快,因为整个 B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。
- 相比于非聚簇索引, 聚簇索引少了一次读取数据的 IO 操作。对排序查找和范围查找优化:聚簇索引对于主键的排序查找和范围查找速度非常快 , 对于B+树来说更不用说
聚簇的缺点
- 依赖于有序的数据:因为 B+树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
- 更新代价大:如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
●非聚簇索引
非聚簇索引(Non-Clustered Index)即索引结构和数据分开存放的索引,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。
MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。非聚簇索引的叶子节点并不一定存放数据的指针,因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
非聚簇索引的优点
- 更新代价比聚簇索引要小 。非聚簇索引的更新代价就没有聚簇索引那么大了,非聚簇索引的叶子节点是不存放数据的。
非聚簇索引缺点:
- 依赖于有序的数据:跟聚簇索引一样,非聚簇索引也依赖于有序的数据,插入时排序,不然在插入乱序的索引适合, 底层需要通过不断的比较, 对索引结点进行分裂, 如果数据是整型还好,否则类似于字符串 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
- 可能会二次查询(回表):这应该是非聚簇索引最大的缺点了。 当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。
!!非聚簇索引一定回表查询吗?
非聚簇索引不一定回表查询。
这种情况, 用户准备使用 SQL 查询用户名,而用户名字段正好建立了索引。
SELECT name FROM table WHERE name='jwq';
那么这个索引的 key 本身就是 name,查到对应的 name 直接返回就行了,无需回表查询。
`即使是 MYISAM 也是这样,虽然 MYISAM 的主键索引确实需要回表,因为它的主键索引的叶子节点存放的是指针。
但是!如果 SQL 查的就是主键呢?
SELECT id FROM table WHERE id=1;
主键索引本身的 key 就是主键,查到返回就行了。这种情况就称之为覆盖索引了。
覆盖索引与联合索引
●覆盖索引
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为 覆盖索引(Covering Index. 如果在一个业务中, 需要多次查询某一个字段,或者多个字段, 可以建立覆盖索引的方式来极大的提升搜索的效率问题,极大减少IO次数
在 InnoDB 存储引擎中,非主键索引的叶子节点包含的是主键的值。这说明,当使用非主键索引进行查询时,数据库会先找到对应的主键值,然后再通过主键索引来定位和检索完整的行数据。这个过程被称为“回表查询”。
覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。
举个例子: 普通索引,如果一条 SQL 需要查询 name,name 字段正好有索引,那么直接根据这个索引就可以查到数据,也无需回表。
●联合索引
使用表中的多个字段创建索引,就是 联合索引,也叫 组合索引 或 复合索引。
ALTER TABLE `cus_order` ADD INDEX id_score_name(score, name);
最左前缀匹配原则
最左前缀匹配原则指的是在使用联合索引时,MySQL 会根据索引中的字段顺序,从左到右依次匹配查询条件中的字段。如果查询条件与索引中的最左侧字段相匹配,那么 MySQL 就会使用索引来过滤数据,这样可以提高查询效率。
假设有一个联合索引(column1, column2, column3),其从左到右的所有前缀为(column1)、(column1, column2)、(column1, column2, column3)(创建 1 个联合索引相当于创建了 3 个索引),包含这些列的所有查询都会走索引而不会全表扫描。
假设查询
- select * from table where column1 = ‘1’ and column2 = ‘2’ and column3 = ‘3’
- select * from table where column2 = ‘2’ and column3 = ‘3’
- select * from table where column1 = ‘1’ and column2 = ‘2’
- select * from table where column2 = ‘2’ and column1 = ‘1’
1,3,4都用到了索引,虽然4的条件是column2 在前,column1 在后, 这通过sql的优化器优化后会变 成满足使用索引的情况
最左匹配原则会一直向右匹配,直到遇到范围查询(如 >、<)为止。
对于 >=、<=、BETWEEN 以及前缀匹配 LIKE 的范围查询,不会停止匹配, 主要是因为边界值, 在前一个索引值column1相等的情况下, 我们的column2仍然是有序的, 可以用到column2的索引,详细可以看文章 对于SQL聚合索引的错误结论
索引下推
索引下推(Index Condition Pushdown,简称 ICP) 是 MySQL 中提供的一项索引优化功能,它允许存储引擎在索引遍历过程中,执行部分 WHERE字句的判断条件,直接过滤掉不满足条件的记录,从而减少回表次数,提高查询效率。
如果在一个表中, column1是索引列, column2不是索引列
select * from table where column1='A' and column2>100
●如果是没有索引下推的功能, 那么就要进行回表查询,在查询到column1='A’的时候, 会回表查询到对应的column1='A’的用户, 再去判断column2>100的数据
●有索引下推的功能的情况, 就会再查询column1='A’的同时,判断column2>100的数据,最后只有满足所有条件的会返回
原理
索引下推的下推其实就是指将部分上层(Server 层)负责的事情,交给了下层(存储引擎层)去处理。
●没有索引下推之前:
存储引擎层先根据 zipcode 索引字段找到所有column1='A 的用户的主键 ID,然后二次回表查询,获 取完整的用户数据;
存储引擎层把所有 column1=‘A’ 的用户数据全部交给 Server 层,Server 层根据column2>100这一条件再进一步做筛选。
●有了索引下推之后:存储引擎层先根据 zipcode 索引字段找到所有 column1=‘A’ 的用户,然后直接判断column2>100,筛选出符合条件的主键 ID;
二次回表查询,根据符合条件的主键 ID 去获取完整的用户数据;
存储引擎层把符合条件的用户数据全部交给 Server 层。
可以看出,除了可以减少回表次数之外,索引下推还可以减少存储引擎层和 Server 层的数据传输量。