一、索引数据结构二叉树,Hash,B+树详解
什么是索引?
面试的时候大家基本都回答,索引就像是字典的目录,通过定位的目录迅速的就能找到所对应的内容,这样回答可能比较片面。在我理解,索引是一种提高搜索效率散列存储的一种数据结构
为什么要使用索引?
在不使用索引的情况下,当一个表里面有上百万行数据的时候,查询某一条数据会导致扫描全表,一行数据一行数据的遍历,导致查询时间变长效率变低。而使用索引,我们知道索引是一种数据结构,如果以id作为索引字段,把id放在树上,那么就可以通过二分法快速的查询出来,提高效率。
mysql索引为什么使用B树或B+树
我们先从最简单的二分搜索树开始分析,层层递进。
二分搜索树
定义:
1.若它的左子树不为空,则左子树上所有结点的值均小于等于根结点的值;
2.若它的右子树不为空,则右子树上所有结点的值均大于等于根结点的值;
3.它的左右子树均为二分查找树。
但是我们知道二分搜索树的右子树均大于根结点,如果插入的数据递增会怎么样(类似自增id)?那会使二分搜索树变成一个链表的形式,这样跟查全表没有什么区别
平衡二叉树
定义:
1,它或者是一颗空树,或者具有以下性质的二叉排序树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树
条件一:它必须是二叉查找树
条件二:每个节点的左子树和右子树的高度差至多为1
每个节点的组成结构:
首先我们先说一下树的每一个节点都存有什么东西,我们可以把一个节点当成一个磁盘块,索引是存在磁盘上面的,每一个节点磁盘块里面存了三块内容,我们以id作为索引为例:
第一部分存的是关键字(索引字段),如果id为10那么就存10
第二部分是数据区,数据区里面存的是数据引用,也就是说存的是这一条id为10 的数据在磁盘上面的地址
第三部分存的是指向子节点的引用
平衡二叉树会左旋右旋进行自我的平衡,这样的话就解决了,二分搜索树形成链表的问题了,那么为什么不使用它作为索引的数据结构?
原因一:太深了,平衡二叉树只有两路,存储大量数据会导致树的层级变深,我们知道一次遍历树的节点,就会把节点读取到磁盘上,每一次都是一次磁盘io的损耗,当层级越深,磁盘io开销很大
原因二:太少了,操作系统与磁盘io一次交互的数据量为一页,这里一页为4k,而mysql还不一样,一次io交互的数据为16k,但是平衡二叉树只有两路,存的数据很少,导致增加磁盘io
平衡多路查找树(B树)
定义:
B树和平衡二叉树稍有不同的是B树属于多叉树又名平衡多路查找树(查找路径不只两个),数据库索引技术里大量使用者B树和B+树的数据结构
规则:
1,排序方式:所有节点关键字是按递增次序排列,并遵循左小右大原则;
2,子节点数:非叶节点的子节点数>1,且<=M ,且M>=2,空树除外(注:M阶代表一个树节点最多有多少个查找路径,M=M路,当M=2则是2叉树,M=3则是3叉);
3,关键字数:枝节点的关键字数量大于等于ceil(m/2)-1个且小于等于M-1个(注:ceil()是个朝正无穷方向取整的函数 如ceil(1.1)结果为2);
4,所有叶子节点均在同一层、叶子节点除了包含了关键字和关键字记录的指针外也有指向其子节点的指针只不过其指针地址都为null对应下图最后一层节点的空格子;
1,b树能够存储多路,路数计算,(16*1024)/字段定义大小,所以索引字段定义越小,路数就越多,就能存越多的数据,树的层级就变低,搜索的速度就变快
2,因为关键字的个数是m-1,所以当我们进行插入,删除字段的时候,可能会产生节点的合并或断裂,也会产生系统开销,所以为什么索引不要建太多,要合适比较好
B+树
定义:B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。为什么说B+树查找的效率要比B树更高、更稳定;我们先看看两者的区别
规则:
1,B+跟B树不同B+树的非叶子节点不保存关键字记录的指针,只进行数据索引,这样使得B+树每个非叶子节点所能保存的关键字大大增加。
2,B+树叶子节点保存了父节点的所有关键字记录的指针,所有数据地址必须要到叶子节点才能获取到。所以每次数据查询的次数都一样。
3,B+树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针。
4,非叶子节点的子节点数=关键字数。
1、B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
2、B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
3、B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
4、B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树的非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。
MyISAM和InnoDB
MyISAM
1,MyISAM:frm是表定义文件,myd是数据文件,myi是索引文件
2, MyISAM是非聚集索引,是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
3, MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针,比如上面以id作为索引,叶子节点里面数据区存的是这个id的数据的指针,也就是磁盘的地址,如果以name作为索引字段,叶子节点也是存此名字的数据的指针。
4,不支持事务
5,只支持表级锁
6,支持全文索引
InnoDB
1,InnoDB是聚集索引,也是使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。
2,如上图,我如果要查询tom这个人的信息,首先我要走name辅助索引,然后找到tom这个人对应的主键id,然后再走一次主键id的索引,最后找到这条信息并返回,这样回表的目的是为了在多个索引使用相同的数据作为索引数据时,这条数据的值被修改,但是其他多个索引更新这个值会有时间上的延迟,不可能完全一致而出现类似分布式事务问题
3,InnoDB支持默认表级锁
4,支持事务
5,对辅助索引采用回表使用主键索引的方式,在叶子节点获取数据,数据和索引在同一文件,查询列尽量使用联合索引,能在索引上查询到需要的字段则不需要回表
6,mysql5.7后InnoDB支持全文索引
7,b+树每一个节点能存放4K的数据,一个三层的b+树可存储大约2100w条数据
Hash索引
哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。对于hash相同的,采用链表的方式解决冲突。类似于hashmap。因为索引的结构是十分紧凑的,所以hash索引的查询很快。
hash索引的限制:
- 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。
- 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。
- 哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。
- 哈希索引只支持等值比较查询,包括=、IN()、<>(注意<>和<=>是不同的操作)。也不支持任何范围查询,例如WHERE price>100。
- 访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列值却有相同的哈希值)。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
- 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。例如,如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应行的引用,冲突越多,代价越大。