索引的原理

索引是什么

索引是帮助MySQL高效获取数据的排好序的数据结构

最重要的点是有序的,我们用索引就是为了快速的查找数据,如果一堆数据是无序的,程序只能挨个遍历每个元素,对比值,才能找到某个元素,最坏的情况要比对N次, N 是这一堆数据的长度。如果数据是有序的,我们就可以使用二分查找算法,他的时间复杂度是 O(long N),效率比直接挨个查找快的多。

二分查找算法关键步骤就是找到区间的中间值,然后确定要查找的值落在左区间还是右区间,一直重复这个步骤直到找到该值。于是就可以将这种查询方法映射成一种数据结构——树。我们规定一种树,有左节点,右节点,和当前节点。并且左节点 < 当前节点 < 右节点 . 如下图所示:

由于树具有方便快速查找的特性,我们一般都会使用树结构去存储索引,并对简单的查找二叉树做了很多优化,比如 红黑树,平衡二叉树, B 树 B+树

树的构建,删除, 查找都有一定的算法,这里不详细描述,只需知道树有一个通用的特性:树的高度越低,查找效率越高

所以索引的构建 , 本质上是控制树的高度

索引数据结构

  • 二叉树:
  • 红黑树
  • Hash 表
  • B Tree

树形索引

表中的数据与索引结构映射关系可以理解如下图:

加入要找到 col2 = 23 的记录,如果不使用索引,我们需要对整张表扫描,从 34 -> 77 -> 5 -> 91 -> 22 -> 89 -> 23, 需要对比7次才能找到

使用索引时, 查找路径时是 34 -> 22 -> 23 只需对比3次就行。在表中数据量极大时,差别更明显

树的动画

推荐一个在线工具,它以动画的形式描述了每种树的构建与查找方法

为什么不是简单的二叉树?

我们知道MySQL索引采用的是 B+树,那么为什么不是其他的树呢?

因为在顺序插入下,树的高度会一直增加,等同于链表。无法控制树的高度,如下图:

如果需要查找6,仍然需要查找6次

为什么不是红黑树?

红黑树(平衡二叉树): 虽然会自动平衡节点位置,但仍然高度不可控。表比较大时会导致树的高度很高。增加查找次数

为什么最终选择B+树 而不是B树

要解决这个疑问,我们需要知道这两种树的构造,如下图

B Tree:

B + Tree:

水平方向可以存放更多的索引key

B+树将数据全部放到叶子节点,留下更多的空间放 key, key 越多,宽度越宽,同样的数据量,宽度越大,高度越小。查找次数就越小。

为什么需要 扩展树的宽度而不是树的深度呢?

如果按照上面的说法,我们拓宽了树的宽度,减少了树的高度,但是比较次数并没有发生改变,只不过是减少了纵向的比较,增加了横向的比较

这个疑问的前提是所有的数据都在内存中,直接在内存中进行比较大小。 但是事实并非如此,不可能把表中的所有数据都加到内存中,必须先从磁盘中加在一部分数据到内存,然后在内存中比较大小,内存中运算的速度远远大于从磁盘加载数据的速度。磁盘加载数据是机械运动,需要电机带动磁针转圈扫描磁道。内存运算则是电子运动,不可同日而语。

数据从磁盘加载到内存中,是有最小单位的,这个单位是 页, 不是 字节或者 位, 页是固定字节数据,由操作系统决定,这样可以减少加载磁盘的次数。

由于B Tree 的每一层都已经是有序的,我们把树中水平方向的数据放在磁盘相邻的地方,每次从磁盘加载一页数据时,便可以得到部分或全部的水平方向的结点,不用再次排序。

在水平方向在内存中使用二分查找的效率远远大于从磁盘中加载一页数据, 所以我们希望树越宽越好,这样一次性加载的数据就越多,而不是越高越好

对于B+ 树,我们假设要查找50这个数据,先从根节点即(15 56 77) 这些数据中找到50所处的范围,因为 (15 56 77) 已经是有序的,可以根据二分查找算法找到 50 处于 15–56之间,
然后加载 15 所指向的下一页数据 (15 20 49),再次根据二分查找算法,找到50处于 49之后,再从磁盘加载49所指向的数据页,找到50

数据量估算

MySQL 自己也有一个逻辑 页,一般是操作系统中 页 的整数倍,这个逻辑页的数据可以通过配置修改,但是不建议,MySQL 是经过大量的测试,为我们定义了一个合理的默认值 16Kb

可以通过下面语句查询:

show global status like 'Innodb_page_size'

假设上图中表示的是主键索引,类型是 bigint, 占 8 个字节。指向下一页的指针占 6 个字节, 那么这一页可以存放 16 * 1024 / (8 + 6) = 1170 个key, 同理第二页即 (15 20 49 …) 也可以放 1170 个key , 对于第三页,也就是叶子节点,包含了主键和对应整行的数据。就按照一行数据放1KB 吧(已经比较大了) 能放 16 行,那么只有一页根节点的话, 这个索引索引树能放 1170 * 1170 * 16 =21,902,400 行数据。 这棵树的高度只有3,就已经能支持上千万的数据量了。也就是只需加载3次磁盘就可以查找到数据了。并且MySQL 存放根节点的页还有优化,可能会把这个页常驻内存。

叶子节点包含所有的索引字段

如上图所示,在主键索引中,叶子节点包含了表中的所有字段,对于一些全表扫描的查询来说,直接扫描叶子节点便可以得到数据,不用再从索引树上挨个查找

叶子节点直接包含双向指针,范围查找效率高

对于一些范围查询比如 id > 20 and id < 50, 在索引树上定位到 20 之后直接使用右向指针定位到下一个比20大的数据,依次往下,直到 50,便可以检出该区间的数据,如果没有这个指针,(B Tree)则需要再次回到索引树中去查找 , 极大的提高了范围查找的性能

Hash 索引

hash 索引原理如下:

更快

大多情况下 Hash 索引比B+ Tree 索引更快,Hash 计算的效率非常高,且仅需一次查找就可以定位到数据(无hash冲突的情况)

不支持范围查询

图中有些歧义,Hash 后的值是没有顺序的,也不是整数,所以无法进行高效的范围查询查询

hash 冲突问题

如果在某列上有很多相同的行,比如 name 字段,叫 张三的人非常多。会产生很多次hash冲突,只能退化成列表搜索了

表引擎

我们常说的 MyISAM 引擎 或者 InnoDB 引擎是基于表的,是表的一个属性, 可不是基于数据库的, 同一个数据库中可以有不同引擎的表

MyISAM 和 InnoDB 引擎

不同引擎的表在磁盘中产生的文件也不一样,数据库文件位置默认在安装目录/data 下

MyISAM 引擎

  • frm: 表结构相关, frame(框架) 缩写`
  • MYD: MyISAM Data 表数据
  • MYI: MyISAM Index 表索引

索引结构中的叶子节点的 data 存放的是 数据行的位置,及这一行在 MYD 文件的位置, 而不是直接放的真实数据

InnoDB

  • frm 表结构信息
  • ibd 表数据加索引

表数据组织形式

表结构本身就是按照 B+ Tree 结构存储, 叶子节点放的是出索引列其他列的数据

聚集与非聚集索引

聚集索引 (InnoDB 主键索引)

叶子节点直接包含整行数据

非聚集索引 (MyISAM 索引, InnoDB 非主键索引)

叶子节点不包含整行数据,包含的是对应行所在的位置,或者主键Id

单从索引结构的来看,聚集索引的查找速度高于非聚集索引

InnoDB 只有一个聚集索引,默认是主键索引, 非主键索引的叶子节点存放的是主键的值,如下图

这样做的目的有两个:

    1. 节约空间,避免将整行的数据存放多份
    1. 保证数据的一致性,否则每增加一行,对应的每个索引都要维护一份行数据。必须要等到每个索引都更新完,数据才能插入成功

★★★ 为什么建议InnoDB 表必须有主键,并且是整型自增的?

InnoDB 整个表的数据就是用B+ 树组织的,如果存在主键,就用主键为索引,叶子节点存储行数据

如果没有主键,InnoDB 就会找到一个每行数据都不相同的列作为索引来组织整个表的数据

如果没有找到这种列,就会建一个隐藏的列,自动维护值,用这个隐藏的列来组织数据,所以我们要主动做这种工作减少数据库的负担

为什么是整型

因为在查找数据的过程中,需要多次比较大小,整型的比较运算速度大于字符串, 并且占用空间小

为什么是自增

这一点涉及到B+ 树的构建,我们知道索引一个最重要的特性就是排好序 的。如果我们不是顺序插入的,那么树就要自己额外做排序,调整树结构,浪费了性能

  • 避免叶子节点的分裂
  • 避免B+ 树做平衡调整

联合索引

联合索引和单索引差不多,只不过是先按第一个字段排序,再按第二个字段排序,然后再按第三个字段排序。

这种排序规则表明了只有在第一个字段相等的情况下,第二字段才是有序的。第二字段相等的情况下,第三个字段才是有序的。

所以 name = 'Bill' and age = 20 and position = 'dev' 可以用到全部索引, 因为 name 确定了,age 是有序的,age 可以走索引, age 确定后 position 可以走索引。这个联合索引可以全部用到

如果是 name = 'Bill and age > 30 and position = 'dev'' , 首先name 可以走索引,name 确定后 age 是有序的,age 也可以走索引,但是 age > 30 导致 age 查出来的数据有多个(31 32), 31 和 32 下的 position (dev admin ) 不是有序的,便无法利用二分算法进行查找。所以无法利用 position 这个索引,这也就是左前缀法则的原理和联合索引失效的原理

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MySQL索引是一种特殊的数据结构,用于快速查找数据表中的行。它可以帮助 MySQL 在处理 SELECT、UPDATE 和 DELETE 语句时更快地找到所需的数据。 索引的工作方式类似于书籍的目录。例如,如果你想在一本书中查找有关“计算机科学”的信息,你可以在书的目录中找到“计算机科学”这一项,然后转到相应的页码来查看内容。同样,在 MySQL 中,你可以在索引中查找特定的值,然后转到相应的数据行来查看内容。 MySQL 支持多种索引类型,包括普通索引、唯一索引、主键索引和全文索引。普通索引是最常见的索引类型,它允许一列中出现重复的值。唯一索引则要求一列中的值必须唯一,不能有重复的值。主键索引是一种特殊的唯一索引,它用于唯一标识数据表中的每一行。全文索引用于搜索数据库中的文本内容。 索引可以帮助 MySQL 加快查询速度,但是它也有一定的开销。在创建索引时,MySQL 需要为索引中的每一行建立一个索引,并在插入 ### 回答2: MySQL索引原理是通过建立索引来加快数据的查找和检索速度。索引是在表中的一个数据结构,它包含了对数据的引用地址,能够使数据库系统快速定位到需要查询的数据记录,从而提高查询效率。 MySQL中常见的索引类型有主键索引、唯一索引和普通索引。主键索引是一种唯一性索引,用于标识表中的每一行数据,保证每一行数据都有一个唯一的标识,加速对数据的操作。唯一索引保证索引列中的数据是唯一的,确保数据的完整性。普通索引是最基本的索引类型,用于加速对数据的查询操作。 MySQL索引原理可以分为B+树索引和哈希索引两种。B+树索引MySQL最常用的索引类型,它采用B+树数据结构来存储索引数据,通过使用该数据结构可以快速定位到数据所在的磁盘块,从而减少磁盘IO,提高查询效率。B+树索引适用于范围查找、排序和分组等操作。哈希索引是将数据的键值通过哈希函数计算得到一个唯一的哈希值,将这个哈希值与数据存储的位置建立映射关系,从而实现快速的数据定位和查找。哈希索引适用于等值查询,在某些特定场景下具有较高的查询效率。 创建索引可以加速查询,但同时也会增加写入操作的开销。索引的维护需要占用额外的空间和时间,当数据发生变动时,需要更新索引的信息。因此,在创建索引时需要考虑到实际的读写比例,避免过多的索引导致性能下降。另外,索引的选择也需要根据具体的应用场景来决定,不同的查询操作需要选择不同的索引类型,以提高查询效率。 ### 回答3: MySQL索引原理主要是通过B树(B-Tree)和哈希索引来实现的。 B树是一种平衡的多路搜索树,MySQL的B树索引是指通过对关键字进行排序,将数据存储在需要的页中的一种索引结构。B树索引适用于频繁插入和删除数据的情况,因为B树的平衡性使得它的高度相对较低,查询速度较快。对于InnoDB引擎来说,默认的索引类型为B+树。 B+树是B树的一种变种,它将数据存储在叶子节点上,而非内部节点。叶子节点之间通过指针连接起来,提高了范围查询的效率。B+树索引也支持从左到右的最长前缀匹配,可以用于处理模糊查询。 哈希索引是将索引值通过一个哈希函数计算出一个哈希码,然后根据哈希码在索引表中进行查找的索引结构。哈希索引适用于等值查询,它具有快速的查找速度。但哈希索引对范围查询的支持较差,而且在大数据量的情况下,可能导致哈希冲突,影响性能。 MySQL还支持全文索引,它是一种用于快速搜索文本内容的索引方式。全文索引基于倒排列表实现,在建立全文索引时将文本内容进行分词,并存储每个词在哪些文档中出现。通过全文索引,可以实现对文本内容的全文搜索。 在使用MySQL索引时,需要根据业务需求创建合适的索引,避免创建过多或不必要的索引,因为索引会占用存储空间,并且在插入、更新和删除数据时会有额外的开销。同时,需要定期进行索引的优化和维护,以保证查询的性能和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值