Mysql索引结构与索引原理及hash索引介绍

 主流索引查找算法

  1. 线性查找,是一个链表,要搜索的话,需要从第一个往后一个个找
  2. 二分查找,是线性查找的升级,也就是说二分查找是可以用线性查找的数据接口,但是算法不一样
  3. 二叉树查找,进一步提高性能,引入了二叉树
  4. 平衡二叉树,二叉树因为有平衡的问题,又进一步出现了平衡二叉树
  5. B树,在平衡二叉树之后又发现了一个问题,之前的数据结构每一个节点都是一个行数据,这样的话对于磁盘的利用率是有问题的,因为我的数据要最终落到磁盘上,以一个节点为单位去读取磁盘效率是很低的
  6. B+树,B树效率也有问题,出现了B+树,可以理解B+树是B树和线性的结合

 


算法原理动画模拟地址:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
线性查找
首先结构是一个链表,比如从1-100找id是5的,要一个个的找,时间复杂度跟数据表量的大小有关,表越大,时间越长,时间复杂度O(N)
原理:从第一个开始,逐个匹配

二分查找
结构同样也是一个链表,只不过每次查找的时候,把链表一分为二,找到链表中间的位置后,把查找目标和中间的位置做比较,假如目标比中间位置小,那么从链表开始位置到链表结束为止作为再次查找的区间再次一分为二的查找,直到找到为止,复杂度是O(logN)
原理:拿出有序数列中点位置作为比较对象,根据中点数据大小,选取一半数据作为新的数据,每次可以将数量减小一半
问题:必须要知道这一段数据的中点在哪里,但在很多实际情况是不知道的,比如磁盘随机读写的情况下,比如一个表有1000W行数据,在磁盘里并不是连续读写,因为磁盘很多碎片,找不到中间位置,然后就升级了二叉树

二叉树查找

 

比如要找0017这个数,从根节点找(0006),因为0017比0006大,那么根节点开始从右侧找,找到0009,0017比0009还大,那么继续从右边找,直到找到为止。复杂度O(logN)

问题:有可能退化为线性查找,就比如上图,数字越来越大,00017一直往右,那么就不平衡了

平衡二叉树

查找的原理和二叉树一样,只不过它会在增删改查的时候自动旋转,使数据结构树保持平衡(最好看动画模拟了解更透彻)
问题:一个节点所包含的数据量太低,一个节点只有一条数据,一行的数据都在一个节点里,在计算机系统里,不管是内存还是硬盘,读写度都有一个最小的单位,比如机械磁盘最小单位在512B,如果是SSD那么最小读写单位是4K-8K之间,也就是每次存取数据至少要4K的数据,如果存储的不足4K,那么有两种选择,一种是和别的数据一起存储在一个4K的块里,另一种是浪费4K的块存储这么一点数据。数据库里一般不大,一条数据占了4K磁盘的块,对于现在的数据库是无法接受的,所以B树就这么衍生

B树

  • 这里的 B 是 Balance(平衡)的缩写。它是一种多路的平衡搜索树。
  • 它跟普通的平衡二叉树的不同是,B树的每个节点可以存储多个数据,而且每个节点不止有两个子节点,最多可以有上千个子节点。
  • B树中每个节点都存放着索引和数据,数据遍布整个树结构,搜索可能在非叶子节点结束,最好的情况是O(1)。
  • 一般一棵 B 树的高度在 3 层左右,3 层就可满足百万级别的数据量。

 

B树 每个节点都存储了一定的范围区间,区间更多的情况下,搜索也就更快。
比如普通的二叉树对于1~100的索引值,首先分为1~50和51~100两部分。
而B树可以分为四个区间 1~25,26~50,51~75,76~100。甚至可以划分为更多区间,这样一次就能排除四分之三的数据

B+树:

 

B+树是B树的一种变种,它与 B树 的 区别 是:

  • 叶子节点保存了完整的索引和数据,而非叶子节点只保存索引值,因此它的查询时间固定为 log(n).
  • 叶子节点中有指向下一个叶子节点的指针,叶子节点类似于一个单链表
  • 正因为叶子节点保存了完整的数据以及有指针作为连接,B+树可以增加了区间访问性,提高了范围查询,而B树的范围查询相对较差
  • B+树更适合外部存储。因为它的非叶子节点不存储数据,只保存索引。
  • InnerDB使用的正是这种当做索引

 

 

 

 

b树中有存储节点信息和指针、数据。b+树中非叶子节点存储的是节点的信息和指针没有存储数据,b+树中数据都是存储在叶子节点。因为节点的内存大小是固定的,所以一个节点能够存储的指针和数据是有限的。相同的数据量b+树每个节点能够存储的指针比b树存储的指针更多,所以b+树的树的深度更小,cpu进行io读取节点信息的次数更少,所以效率相对更高.

 

 

MySQL 中的 Hash 索引
采用 Hash 进行检索效率非常高,基本上一次检索就可以找到数据,而 B+ 树需要自顶向下依次查找,多次访问节点才能找到数据,中间需要多次 I/O 操作,从效率来说 Hash 比 B+ 树更快。

我们来看下 Hash 索引的示意图:

 

 

键值 key 通过 Hash 映射找到桶 bucket。在这里桶(bucket)指的是一个能存储一条或多条记录的存储单位。一个桶的结构包含了一个内存指针数组,桶中的每行数据都会指向下一行,形成链表结构,当遇到 Hash 冲突时,会在桶中进行键值的查找。

那么什么是 Hash 冲突呢?

如果桶的空间小于输入的空间,不同的输入可能会映射到同一个桶中,这时就会产生 Hash 冲突,如果 Hash 冲突的量很大,就会影响读取的性能。

通常 Hash 值的字节数比较少,简单的 4 个字节就够了。在 Hash 值相同的情况下,就会进一步比较桶(Bucket)中的键值,从而找到最终的数据行。

Hash 值的字节数多的话可以是 16 位、32 位等,比如采用 MD5 函数就可以得到一个 16 位或者 32 位的数值,32 位的 MD5 已经足够安全,重复率非常低。

我们模拟一下 Hash 索引。关键字如下所示,每个字母的内部编码为字母的序号,比如 A 为 01,Y 为 25。我们统计内部编码平方的第 8-11 位(从前向后)作为 Hash 值:

 

Hash 索引与 B+ 树索引的区别
我们之前讲到过 B+ 树索引的结构,Hash 索引结构和 B+ 树的不同,因此在索引使用上也会有差别。

Hash 索引不能进行范围查询,而 B+ 树可以。这是因为 Hash 索引指向的数据是无序的,而 B+ 树的叶子节点是个有序的链表。
Hash 索引不支持联合索引的最左侧原则(即联合索引的部分索引无法使用),而 B+ 树可以。对于联合索引来说,Hash 索引在计算 Hash 值的时候是将索引键合并后再一起计算 Hash 值,所以不会针对每个索引单独计算 Hash 值。因此如果用到联合索引的一个或者几个索引时,联合索引无法被利用。
Hash 索引不支持 ORDER BY 排序,因为 Hash 索引指向的数据是无序的,因此无法起到排序优化的作用,而 B+ 树索引数据是有序的,可以起到对该字段 ORDER BY 排序优化的作用。同理,我们也无法用 Hash 索引进行模糊查询,而 B+ 树使用 LIKE 进行模糊查询的时候,LIKE 后面前模糊查询(比如 % 开头)的话就可以起到优化作用。
对于等值查询来说,通常 Hash 索引的效率更高,不过也存在一种情况,就是索引列的重复值如果很多,效率就会降低。这是因为遇到 Hash 冲突时,需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时。所以,Hash 索引通常不会用到重复值多的列上,比如列为性别、年龄的情况等。

巩固

mysql支持的索引类型

Btree索引
使用的是b+树的结构存储数据,在b+树中每个叶子节点都包括指向下一个叶子节点的指针,这样方便进行叶子节点之间的遍历。
Btree索引特点?
1:Btree索引实际上是B+树的结构存储数据。
对于不同的存储引擎,实现有不同,比如myisam索引在叶子节点是通过数据的物理位置来引用行的,而innodb是通过主键来引用被索引的行
2:Btree索引可以加快数据的查询速度。
通常索引的大小远小于表中数据大小,使用Btree索引,存储引擎不需要全表扫描获取数据,取而代之的是从索引的根节点开始搜索,在索引的根节点存放了指向下层子节点的指针,存储引擎根据指针向下层查找,通过比较节点页的值和要查找的值就可以拿到合适的指针进入下层的子节点,而这些指针实际上是定义了子节点中值得上限和下限,所以呢存储引擎最终要找到对应的值就可以判断值是否存在,最终存储引擎通过Btree索引找到符合要求的叶子节点,叶子节点比较特别,它的指针是指向被索引的数据,而不是其他叶子节点。innodb指向的是主键,而myisam指向的是数据的物理地址。
3:Btree索引更适合范围查找
由于Btree索引对索引是顺序存储的,所以Btree索引适合范围查找

Btree索引在什么情况下才能被使用到?
1:全局匹配的查询
比如在order_sn创建索引,要查找order_sn=‘9877772000’的订单,那么这就是全局匹配查询
2:匹配最左前缀查询
没有单独在order_sn创建索引,而是创建一个order_sn和order_date的联合索引,对于上面的条件还是可以用联合索引,只要是联合索引的第一列符合条件,那么会被使用到;但查order_date=某某日,那么这就不会使用索引
3:匹配列前缀查询
指的是可以匹配某一列的开头部分,比如查找order_sn like '987%',这也可以使用order_sn的联合索引。
4:匹配范围值得查询
order_sn > '987777' and order_sn < '99999'
5:精确匹配左前缀,并范围匹配另外一列
order_sn='987777' and order_date >'2000-10-11'
6:只访问索引的查询(覆盖索引)
也就是查询只需要查索引,无需访问行。
7:排序。


Btree索引的限制有哪些?
1:命中的数据占用了表中大部分数据,那么mysql查询优化器会认为全表扫描方式更好。
2:不是按照最左原则,无法使用索引。
3:使用索引时不能跳过索引中的列(这里是说的是左边的列,而不是最左边的列)
比如联合索引(a,b,c),查询的时候查a=1 and c=2,那么这里只能使用a列
4:not in 和 <>也无法使用索引
5:如果查询中有某个列的范围查询,则其右边所有的列都无法使用索引。


Hash索引
支持hash索引的类型有innodb和memory引擎。

Hash索引特点?
1:Hash索引是基于Hash表实现,只有查询条件精确匹配Hash索引中的所有列时,才能够使用到Hash索引。
也就是说,Hash索引只能用在等值查询,那么范围和模糊查询就不可以了
2:对于Hash索引中的所有列,存储引擎都会为每一行计算一个Hash码,Hash索引中储存的就是Hash码。
Hash索引表中保存每一个Hash索引所代表的数据行的指针,由于Hash索引本身只存储Hash码,所以Hash索引结构比较紧凑,那么查询速度比较快。

Hash面临的问题有哪些?
1:使用Hash索引查找数据必须经过两次读取,因为Hash索引包括的是hash码、对应行指针、键值,所以先找到行,再通过行读取。
2:无法排序
3:全键值匹配,不支持部分匹配
4:有可能产生hash冲突,为了保证查找效率,一般hash码比较小,这样但容易冲突;但身份证可以比较适合建立hash索引


使用索引的好处有哪些?
1:索引大大减少了存储引擎需要扫描的数据量。
索引文件的大小远小于数据文件的大小。比如innodb引擎发生一次io最小的单位是以叶为单位,一页可以存储的信息越多,那么读取效率月越快,默认innodb页大小为16K,由于索引的大小通常远比一行的大小小的多,所以一页内可以存储更多的索引数据,因此通过索引查找读取页的数量少,这样也就减少了存储引擎扫描的数量,加快了速度。
2:索引可以帮我们进行排序,避免使用临时表,因为索引是有序的。
3:索引可以把随机I/O变为顺序I/O,因为索引是有序的。

 

posted @ 2017-09-22 11:32  温柔的风  阅读(2505)  评论(1编辑  收藏  举报