目录
1、引言
在学习了解数据索引时,一直是迷迷糊糊的,不真实,知道索引像目录一样,可以加快检索的速度,但还是有很多的问题。
1、二叉查找树、平衡二叉树、红黑树、B-Tree,B+Tree,这么多数据结构,为何要用B+Tree?
a、B+Tree的高扇出性、平衡性和矮胖性;
b、Mysql设计利用了磁盘预读原理,将一个B+Tree节点大小设为一个页大小,在新建节点时直接申请一个页的空间,这样就能保证一个节点物理上存储在一个页里,加之计算机存储分配都是按页对齐的,这样就实现了每个Node节点只需要一次I/O操作。
2、现在已经知道InnoDB索引使用了B+Tree,那么它的节点是怎么组织的?
一个节点对应着一个物理页,其中有一个字段来表示是数据节点还是索引节点。具体可以下文的数据页结构
3、为什么B+索引树的高度一般多少层(有说三层),这样的话,根节点包含了多少关键字(有说超过100),还是当树高超过三层,会自适应增加?
一棵树最多可以收录多少数据? 动态的进行调整,因为页大小是固定的了,所以当索引长度大了,扇出系数则小。
在InnoDB里,每个页默认16KB,假设索引的是8B的long型数据,每个key后有个页号4B,还有6B的其他数据(参考《MySQL技术内幕:InnoDB存储引擎》P193的页面数据),那么每个页的扇出系数为16KB/(8B+4B+6B)≈1000,即每个页可以索引1000个key。在高度h=3时,s=1000^3=10亿!!也就是说,InnoDB通过三次索引页的I/O,即可索引10亿的key。通常来说,索引树的高度在2~4。
4、MyISAM和InnoDB的索引的不同?
相同点:B+Tree树
不同点:叶节点存储的数据不一样,MyISAM叶节点存储的是数据所在的地址,索引和数据(.frm是表定义文件,.MYI是MyISAM的索引文件,.MYD是MyISAM的数据文件 )是独立分开的;InnoDB叶节点存储的是数据,索引文件和数据文件(.ibd是InnoDB的文件)是相同的;InnoDB必须有主键(没有,系统会自动生成),MyISAM不要求,MyISAM的主键索引也是非聚簇索引,跟辅助索引的区别就是唯一性;对于辅助索引来说,InnoDB还需要回表操作(大约6次I/O),因此InnoDB会有覆盖索引(在辅助索引页节点即可得到查询的数据)的概念,而MyISAM可以直接找到行地址
5、叶子节点中的数据与磁盘的同步?
如果叶子中的数据发生了更改,则叶子中的数据为脏数据,因为与磁盘不一致,因此需要刷新到磁盘中。利用重做日志(Write Ahead Log)可以实现,既修改前现写日志,再写数据。Checkpoint技术;缩短数据恢复时间;缓冲池不够用时,将脏页刷新到磁盘;重做日志不可用时,将脏页刷新到磁盘 [持久性]
6、数据库的恢复,重做日志的生成?
事务提交的时候,先写到“重做日志”中,然后再写缓冲页,这样宕机之后可以从“重做日志”中恢复。但是新的问题是,重做日志会越来越大,针对这个的解决方案,是利用checkpoint技术,按照策略将脏页回写(1、mster线程每秒或每十秒回写一定比例脏页;2、LRU列表中可用页小于100,从尾部拆除的页中有脏页,则回写;3、重做日志不可用;4、脏页太多了),这样的话,checkpoint之前的重做日志就可以循环利用了,因为它们已经回写到磁盘
7、ibd文件和行的大小关系?
当创建一张表,会生成一个对应的idb文件,这个文件的初始大小为96KB,初始申请时,是以碎片页申请的,一共32个碎片页,当超过32个碎片页,则会以区的形式申请,既1M(64个连续的页)。
8、怎么查找纪录?
首先根据索引(B+Tree)找到记录所在的页(I/O操作),然后在页中再进行二叉查找(Page Directory)
9、如何实现数据库中的页都可以访问到?
B+Tree的叶子节点存储着所有的数据,一个叶子节点对应着一个页,而且叶子节点是双向链表串起来的;而页内,利用偏移地址来访问每一行的纪录。
10、MySQL和MyISAM表的功能区别
MyISAM:不支持事务、表锁、支持全文索引
MySQL:支持事务、行锁、支持外键;
11、当叶中记录未满时,插入一条数据,是否要移动数据?
User Records 就是整个页面中真正用于存放行记录的部分,而 Free Space 就是空余空间了,它是一个链表的数据结构,为了保证插入和删除的效率,整个页面并不会按照主键顺序对所有记录进行排序,它会自动从左侧向右寻找空白节点进行插入,行记录在物理存储上并不是按照顺序的,它们之间的顺序是由 next_record
这一指针控制的。
2、数据结构的选择
我们都知道,查找树可以加快查找速度,但是它们的局限在于树高度不可控,而数据库的数据是以磁盘为媒介存储的,所以在读取的时候必然要涉及IO操作,而IO操作是最耗时的,所以如果不断的进行磁盘操作,树的查找速度自然就降下来了,所以为了尽量避免磁盘操作,可以尽量使树变得矮胖,因此B+/-Tree脱颖而出。树的出度上限:d_{max} =floor( pagesize / (keysize + datasize + pointsize));
3、InnoDB索引存储原理
3.1 InnoDB的存储结构
表空间:存储着所有的数据,ibdata1文件,如果开启了innodb_file_per_table,则每张表单独一个表空间,但是依然会有一部分是在共享表空间中,共享表空间包括:回滚信息,插入缓冲索引页,系统事务信息等;而单独的表空间存储着数据页、索引页,插入缓冲bitmap页。
表空间是由各个段组成的,如数据段、索引段、回滚段等
区:大小恒为1M,由页组成,默认64页
页:默认16KB
创建的表默认96KB,其中每个段开始时,使用32个碎片页,而并非直接申请一个区
3.2 存储格式:compact
变长字段列表是逆序存放,NULL标志位1个字节(书上说的应该,可能不对,因为按照下述原理,一行最多是8个可空的列),纪录头信息是5个字节,隐藏的事务ID列,回滚指针列分别为6字节、7字节,若没有主键,则会有一列rowid列,为6字节长度。
可以注意到,next_record,标示下一条记录的偏移量,也印证了行的最大长度为2^16,既65535,这个长度包括了这些固定或隐含的列。
char字段的列,未达到设置的长度时,由0x20填充
NULL的列,当可空的列为NULL时,则在NULL标志位设置相应的位是1,比如0000 0110,第2,3列为空,而在后续的实际列内容中则不存储NULL
deleted_flag, 删除的时候并非物理删除,而是设置标志,这样会造成页空洞的问题
3.3 存储格式:redundant
n_fields:标示数据最多的列是1023
3.4 数据页结构
File Header | 记录页的一些头信息,如页偏移量、上一页、下一页、页类型等,固定长度为38个字节。
| |||||||||||||||||||||||||||||||||||||||||||||
Page Header | 记录页的状态信息,堆中记录数、指向空闲列表的指针、已删除记录的字节数、最后插入的位置等,固定长度共56个字节。
PAGE_DIRECTION、PAGE_LAST_INSERT、PAGE_N_DIRECTION 这三个变量影响着页的分裂,当页满(页未满时直接加入free的顶部,维护好链表即可),又插入一个未按顺序的索引值时,这时候需要分裂页,并不是简单从中间分裂(以下case是向右分裂)。 1、随机插入,从中间分裂 2、如果在同方向插入的数据超过5条,定位点后有3条纪录,则分裂点是定位点后的第3条纪录 3、若定位点后没有三条纪录,则以定位点为分裂点
| |||||||||||||||||||||||||||||||||||||||||||||
Infimun+Supremum Records | 在InnoDB存储引擎中,每个数据页中有两个虚拟的行记录,用来限定记录的边界。 Infimun记录是比该页中任何主键都要小的值; Supermum指比任何可能大的值还要大的值。 这两个值在页创建时被建立,并且在任何情况下不会被删除。在Compact行格式和Redundant行格式下,两者占用的字节数各不相同。 | |||||||||||||||||||||||||||||||||||||||||||||
User Records |
| |||||||||||||||||||||||||||||||||||||||||||||
Free Space | 指空闲空间,同样也是个链表数据结构。当一条记录被删除后,该空间会被加入空闲链 表中 | |||||||||||||||||||||||||||||||||||||||||||||
Page Directory | 页目录存放了记录的相对位置,并不是偏移量,有些时候这些记录称为Slots(槽),InnoDB并不是每个记录一个槽,槽是一个稀疏目录,即一个槽中可能属于多个记录,最少属于4条记录,最多属于8条记录。需要牢记的是,B+树索引本身并不能找到具体的一条记录,B+树索引能找到只是该记录所在的页。数据库把页载入内存,然后通过Page Directory再进行二叉查找。只不过二叉查找的时间复杂度低,同时内存中的查找很快,因此通常忽略了这部分查找所用的时间。 | |||||||||||||||||||||||||||||||||||||||||||||
File Trailer | 为了保证页完整地写入磁盘(如写过程的磁盘损坏、机器宕机等),固定长8个字节。 | |||||||||||||||||||||||||||||||||||||||||||||
4、索引优化
- 最左前缀原理与相关优化
在辅助索引里,由于索引是按索引列顺序存放的,所以索引会有前缀匹配的问题,要注意以下几个场景,
查询没有使用索引第一列、或者隔了某个列没有使用(这种情况只能使用前面的索引列);
范围查询列之后的索引不再使用;
模糊查询索引列之后的索引列不能使用;
索引列使用了函数或表达式则不能使用;
- 索引选择性
索引选择性越大,索引的效率越高,评估索引的选择性可以使用 不重复的值/ 总记录数 公式
数据库查看选择性命令: show index from tableName
InnoDB根据采样进行跟新
1、更新策略
表中1/16的数据发生变化
stat_modified_counter > 20W
2、采样策略
取得B+树的叶子节点总数 ,记为A
随机取8个节点,统计每一个页中纪录数的不同,Pi
通过采样计算,(P1+P2+...+P8) * A / 8
5、参考
https://blog.codinglabs.org/articles/theory-of-mysql-index.html
《MySql技术内幕:InnoDB存储引擎》
https://www.cnblogs.com/itxiaok/p/10356623.html
附:
1、查看页号
SELECT b.name, a.name, index_id, type, a.space, a.PAGE_NO
FROM information_schema.INNODB_SYS_INDEXES a,information_schema.INNODB_SYS_TABLES b
WHERE a.table_id = b.table_id AND a.space <> 0;
2、树的定义
B-Tree
m阶B-Tree满足以下条件:
1、每个节点最多拥有m个子树
2、根节点至少有2个子树
3、分支节点至少拥有m/2颗子树(除根节点和叶子节点外都是分支节点)
4、所有叶子节点都在同一层、每个节点最多可以有m-1个key,并且以升序排列
5、P[i]指向关键字属于(K[i-1], K[i])的子树
待定...
二、B+Tree
B+ Tree由二叉树 -> 平衡二叉树 -> B-Tree演进而来
与B-Tree相比,B+Tree有以下不同点:
每个节点的指针上限为2d而不是2d+1。
内节点不存储data,只存储key;叶子节点不存储指针。
P[i]指向关键字属于[K[i-1], K[i])的子树
与B-Tree的直观感受是:节点中存储的数据不一致,B-Tree的节点存储关键字和数据,以及指针;B+Tree存储的是关键字和指针
进化:叶节点带有顺序指针