MySql InnoDB索引分析与优化

目录

1、引言

2、数据结构的选择

3、InnoDB索引存储原理

3.1 InnoDB的存储结构

3.2 存储格式:compact

3.3 存储格式:redundant

3.4 数据页结构

4、索引优化

5、参考 

附:

1、查看页号

2、树的定义


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个字节。

名称大小(Bytes)描述
FIL_PAGE_SPACE4该页的checksum
FIL_PAGE_OFFSET4该页在表空间中的页偏移量
FIL_PAGE_PREV4该页的上一个页
FIL_PAGE_NEXT4该页的下一个页
FIL_PAGE_LSN8该页最后被修改的LSN
FIL_PAGE_TYPE2该页的类型,0x45BF为数据页
FIL_PAGE_FILE_FLUSH_LSN8独立表空间中为0
FIL_PAGE_ARCH_LOG_NO4该页属于哪一个表空间
Page Header

记录页的状态信息,堆中记录数、指向空闲列表的指针、已删除记录的字节数、最后插入的位置等,固定长度共56个字节。

名称大小(Bytes)描述
PAGE_N_DIR_SLOTS2Page DirectorySlot的数量,初始值为2
PAGE_HEAP_TOP2堆中第一个记录的指针
PAGE_N_HEAP2堆中的记录数,初始值为2  
PAGE_FREE2指向可重用空间的首指针
PAGE_GARBAGE2已标记为删除(deleted_flag)的记录的字节数
PAGE_LAST_INSERT2最后插入记录的位置
PAGE_DIRECTION2最后插入的方向,PAGE_LEFT(0x01)PAGE_RIGHT(0x02)PAGE_NO_DIRECTION(0x05)
PAGE_N_DIRECTION2一个方向上连续插入记录的数量
PAGE_N_RECS2该页中记录(User Record)的数量
PAGE_MAX_TRX_ID8修改该页的最大事务ID(仅在辅助索引中定义)
PAGE_LEVEL2该页在索引树中位置,0000代表叶子节点
PAGE_INDEX_ID8索引ID,表示该页属于哪个索引
PAGE_BTR_SEG_LEAF10B+Tree叶子节点所在Leaf Node Segment的Segment Header(无关紧要)
PAGE_BTR_SEG_TOP10B+Tree非叶子节点所在Non-Leaf Node Segment的Segment Header(无关紧要)

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
  1. 存储实际插入的行记录
  2. 在Page Header中PAGE_HEAP_TOP、PAGE_N_HEAP的HEAP,实际上指的是Unordered User Record List

    • InnoDB不想每次都依据B+Tree键的顺序插入新行,因为这可能需要移动大量的数据

    • 因此InnoDB插入新行时,通常是插入到当前行的后面(Free Space的顶部)或者是已删除行留下来的空间

  3. 为了保证访问B+Tree记录的顺序性,在每个记录中都有一个指向下一条记录的指针,以此构成了一条单向有序链表

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相比,B+Tree有以下不同点:

每个节点的指针上限为2d而不是2d+1。

内节点不存储data,只存储key;叶子节点不存储指针。

P[i]指向关键字属于[K[i-1], K[i])的子树

与B-Tree的直观感受是:节点中存储的数据不一致,B-Tree的节点存储关键字和数据,以及指针;B+Tree存储的是关键字和指针

进化:叶节点带有顺序指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值