mysql 索引 常见的索引模型 InnoDB的索引模型

索引的出现是为了提高数据查询的速度,就像书的目录一样,可以快速定位我们想要的数据!

索引的常见模型

常见、也比较简单的数据结构有搜索树、哈希表、有序数组(较少)

哈希表

  • 哈希表是一种以键 - 值(key-value)存储数据的结构,我们可以根据 key找到其对应的 Value。哈希的思路就是把值放在数组里(用一个哈希函数把 key 换算成一个相对于数组里确定的位置,然后把 value 放在数组的这个位置)。不置可否,多个 key 值经过哈希函数的换算,会出现同一个值的情况。这时候可以拉出一个链表。

在这里插入图片描述
图中,假设,row1和 row3根据key算出来的值都是5,所以后面跟了一个链表。这时你要查 row3 对应的记录,处理步骤就是:首先,将 row3 通过哈希函数算出 5;然后,按顺序遍历,找到 row3。

可以设想下,如果现在要找row[1] – row[n]这个区间的所有记录,就必须全部扫描一遍了。所以,哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。

有序数组

有序数组在等值查询和范围查询场景中的性能就都非常优秀。借助于上面这个例子,如果我们使用有序数组来实现的话,示意图如下所示
在这里插入图片描述
假设记录没有重复,这个数组就是按照某id递增的顺序保存的。这时候如果你要查 row[n] 对应的记录,用二分法就可以快速得到,这个时间复杂度是 O(log(N))。另外,这个索引结构支持范围查询。你要查记录在[n, m]区间的 所有记录,可以先用二分法找到row[n](如果不存在 row[n],就找到大于 row[n] 的第一个 记录),然后向右遍历,直到查到第一个大于 row[m] 的记录,退出循环。

如果仅仅看查询效率,有序数组就是最好的数据结构了。但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须得挪动后面所有的记录,成本太高。所以,有序数组索引只适用于静态存储引擎,比如你要保存的是 2017 年某学校的所有学生的考试成绩,这类
不会再修改的数据。

搜索树

二叉搜索树 (Myisam索引引擎)

二叉搜索树是经典数据结构。还是上面的例子,如果我们用二叉搜索树来实现的话,示意图如下所示
在这里插入图片描述

二叉搜索树的特点是:每个节点的左儿子小于父节点,父节点又小于右儿子。
这样如果你要查 row1 的话,按照图中的搜索顺序就是按照 row4-> row2-> row1 这个路径得到。这个时间复杂度是 O(log(N))。另外为了维持 O(log(N)) 的查询复杂度,你就需要保持这棵树是平衡二叉树。为了做这个保证,更新的时间复杂度也是 O(log(N))。

多叉搜索树(innodb索引引擎)

树可以有二叉,也可以有多叉。多叉树就是每个节点有多个子节点,子节点之间的大小保证从左到右递增。

在这里插入图片描述
因为B+树的叶子节点是page (页),一个页里面可以存多个行

为什么要用N叉树

二叉树是搜索效率最高的,但是实际上大多数的数据库存储却并不使用二叉树。其原因是,索引不止存在内存中,还要写到磁盘上,想象一下一棵 100 万节点的平衡二叉树,树高 20(2^20==1048576)。一次查询可能需要访问 20 个数据块。如果采用硬盘存储数据,从磁盘随机读一个数据块需要 10 ms 左右的寻址时间。也就是说,对于一个 100 万行的表,如果使用二叉树来存储,单独访问一个行可能需要 20 个 10 ms 的时间,这个查询可真够慢的。为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。

这里,“N 叉”树中的“N”取决于数据块的大小。在InnoDB里,每个页默认16KB(可以设置),假设索引的是8B的long型数据,每个key后有个页号4B,还有6B的其他数据,那么每个页的扇出系数为16KB/(8B+4B+6B)≈1000,即每个页可以索引1000个key,在高度h=3时,s=1000^3=10亿!!也就是说,InnoDB通过三次索引页的I/O,即可索引10亿的key

多叉搜索树与二叉搜索树的区别
  • 在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
  • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

所以得出以下结论

  • B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效
  • 由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间
数据库为什么使用B+树而不是B树
  • B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
  • B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体数据的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
  • B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
  • B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
  • 增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率

索引

索引类型

根据叶子节点的内容,索引类型分为主键索引和非主键索引
在这里插入图片描述

基于主键索引和普通索引查询的区别

假设有这样一个表


mysql> create table T(
id int primary key, 
k int not null, 
name varchar(16),
index (k))engine=InnoDB;

表中 R1~R5 的 (ID,k) 值分别为 (1,100)、(2,200)、(3,300)、(5,500) 和 (6,600),两棵树的示例示意图如下。
在这里插入图片描述

  • 如果语句是 select * from T where ID=5,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
  • 如果语句是 select * from T where k=500,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表

所以一般情况下建议创建一个自增主键,这样非主键索引占用的空间最小

覆盖索引

如果执行的语句是 select ID from T where k between 30 and 5000,这时只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经“覆盖了”我们的查询需求,我们称为覆盖索引,它是一种微妙常用的优化查询的手段

前缀索引

语法:index(field(10)),使用字段值的前10个字符建立索引,默认是使用字段的全部内容建立索引。

前提:前缀的标识度高。比如密码就适合建立前缀索引,因为密码几乎各不相同。

实操的难度:在于前缀截取的长度。

我们可以利用select count(*)/count(distinct left(password,prefixLen));,通过从调整prefixLen的值(从1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前prefixLen个字符几乎能确定唯一一条记录)

最左前缀原则

  • 顾名思义,就是最左优先,在创建联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
  • 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
  • =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

索引下推

MySQL 5.6 之前,通过非主键索引查询数据时,需要找到对应满足要求的主键,然后到主键索引上找出数据行,再对比字段值。而 MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段(覆盖索引)先做判断,直接过滤掉不满足条件的记录,减少回表次数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值