九. MySQL InnoDB 底层结构

一. InnoDB 数据记录存储结构

  1. 在使用InnoDB作为存储引擎时,最小的存储单位为页,一页大小为16384字节也就是16kb, 其中有132个字节用来保存元数据相关信息,27个字节用来保存当前记录的附加信息, 一条记录的存储结构为:
    在这里插入图片描述
  1. 变长字段列表中保存了数据类型为可变超度的例如Varchar,字段的实际长度
  2. Null值列表: MySQL在存储允许为null的字段时会通过Null值列表特殊处理,会在这个列表中对字段是否允许为null进行标识
  3. 记录头信息: delete_mask标记该记录是否被删除,n_owned标记该记录的记录数, record_type当前记录的类型,next_record下一条记录的相对位置
  4. DB_ROW_ID: 没有主键,并且没有唯一键时自动生成的主键
  5. DB_TRX_ID: 事物id
  6. DB_ROLL_PTR: 回滚指针
  1. 问题: 使用InnoDB时一页为16kb, 当存储实际数据大小超过16KB时怎么办,这里就牵扯到了MySQL的行格式
  2. 行格式: InnoDB存储单位是页,但是MySQL在存储数据时是按照一行来进行存储的,这个存放方式称为行格式,MySQL中支持4种不同类型的行格式(存储格式):
  1. Compact、Redundant(比较老,可以看为是静态的,mysql5.0之后使用Compact),
  2. Dynamic,Compressed(这两种时动态的,5.7之后默认Dynamic)
  1. 当使用Compact 或 Redundant行格式时,会提供出专门的溢出页,实际的存储记录中用来保存一个字段实际数据的位置,最大只会保存768个字节,超过部分会保存到溢出页,保存当前数据所在溢出页的地址值(如上图,有列1值,如果列1值超过786超过部分就保存到溢出页,然后列1中保存超过部分数据所在溢出页的地址值)
  2. 当使用Dynamic或Compressed行格式时,字段的实际数据有专门的存放位置,存储记录中保存字段数据的位置存储的是指向实际数据的地址值
  3. MySQL中有规定一个页最少存储2条记录,在使用Compact格式时如果不想出现数据溢出,那么132+2*(27+n)<16384,也就是一行数据大小不能超过8100左右

二. InnoDB索引页结构

  1. 首先要知道在使用InnoDB是如果表中有主键,主键就是主索引,如果没有主键,会使用表中设置了唯一索引的字段为主索引,如果两个都不存在InnoDB内有存在一个长度为6字节的rowId, 会使用该数据作为主索引,也就是说只要时InnoDB下的表都存在一个主索引,所有数据都是基于这个主索引进行存储的
  2. 上面说的是存储记录结构,这个记录时存在InnoDB页中,那么页中的详细信息有哪些
  1. FileHeader: 文件头部,存储了当前页的描述信息,例如当前页的页类型,页编号,上一页与下一页的地址等
  2. PageHeader: 页面头部,例如记录了当前页存储了多少条记录,多少个槽,第一条记录的地址等等
  3. Infimum+Supremum: 两个虚拟行记录,mysql插入的,标记了当前页面的最小行记录和最大行记录
  4. UserRecords: 真实数据(上面说的记录数据就是存在这个位置的),当前页已使用空间
  5. FreeSpace: 当前页空闲空间
  6. PageDirectory: 页面目录,记录了页面中一些数据存储的相对外置
  7. FileTrailer: 文件尾部,用来校验当前页是否完整,在FileHeader文件头部实际还存在另外一个"校验和",通过这两个"校验和"比对,防止数据丢失
    在这里插入图片描述
  1. 行记录结构中记录头信息delete_mask与数据删除: B+树中页面通过链表连接,在执行删除操作时为减少链表的维护,并不是真实删除,而是设置delete_mask标识为删除,MySQL会获取这些被标记为删除状态的记录添加到一个专门的垃圾链表中,当下次存储数据打到这个删除位置时,覆盖并修改delete_mask状态即可
  2. 页面中的多条记录时怎么连接的: 通过Infimum最小记录与Supremum最大记录连接的, 在添加数据时MySQL会为每一条数据添加两个虚拟行记录,这两个行记录就相当于链表节点中当前节点的位置,与下一个节点的位置(单向链表)
  3. 页面中的PageDirectory页面目录: 一个页面中的多条数据组成一个单向链表,通过Infimum最小记录与Supremum最大记录连接, 但是单向链表查询性能不好2/n, 通过PageDirectory页面目录实现快速查找, MySQL会通过Infimum最小记录与Supremum最大记录对一个页中的多条数据进行分组,将分组后每个组中最后一条记录的地址偏移量存入PageDirectory中(这个地址偏移量称为slot 槽),在查询数据时,当定位到该数据所在页后,使用二分查找法在PageDirectory中定位到数据所在的槽,拿到槽中Infimum最小的记录,遍历获取指定数据,如下图:
  1. 查询数据定位到页后,使用二分查找法在PageDirectory中定位到该数据所在的槽
  2. 例如当前查询数据定位到槽1
  3. 拿到槽1中Infimum最小的数据,然后通过Supremum获取指定数据
    在这里插入图片描述
  1. 索引页是怎么管理的,MySQL中提出了系统表空间(针对MySQL服务本身的)与独立表空间(针对自定义表数据的),每个表对应一个"表名.ibd"文件,这个文件就可以认为是当前表的独立表空间

  2. 页是怎么存的, 我们使用InnoDB创建表时,每张表都会对应一个"表名.ibd"文件, 用来存储当前表的数据页,内部包含了实际数据信息,与索引相关信息,页的FileHeader中保存了当前页的描述信息,例如当前页的页类型,页编号,上一页与下一页的地址等,并且会记录当前页属于哪个表空间也就是哪个".ibd"文件,其中页号由4个字节组成,通过这4个字节进而了解到一个表空间中最多可以存储21亿左右个页,拿一页16kb大小,一个表空间最大能够存储大概21亿*16kb=64t左右的数据

  3. 通过FileHeader文件头中的页号了解到一个表空间中最多可以存储21亿左右个页, 为了提高MySQL的检索性能,会针对页进行分区,64个页组成一个分区(大概1M),并且会根据区划分不同的组,每个组中最大可以存储256个区,针对组又划分不同的段
    在这里插入图片描述

  4. 表空间在管理页时,针对页进行分区的好处是什么:减少随机io, 在一棵b+数中多个页是通过双向链表连接的(页中的行数据是通过单向链表连接的),会出现一个问题,在逻辑上两个相邻的数据页,对应到物理上可能是不相邻的,那么在查找时就可能出现随机io的问题,为了防止这个问题,规定一个区中的所有页在物理上也必须是相邻的,在申请创建新页时相邻,并且如果表的数据量特别大,在申请时会直接按照区为单位进行申请

  5. 表空间在管理页时,分段的好处是什么: 段是一个逻辑概念,在InnoDB的主索引树分为叶子节点跟非叶子节点,由于叶子节点与非叶子节点存储数据的不同将不同节点的数据放入不同的段中(叶节点段, 非叶节点段),更方便查询数据,另外还存在回滚段

  6. 树高估算

在InnoDB中最小存储单元是页,默认大小为16KB,一个指针为6个字节,如果主键使用bigint类型那么一个主键占8个字节,计算下来在InnoDB中根节点中可以存储16k/(6+6)=1170,也就是根节点下有1170个子节点,那么这1170个子节点下每个节点都会有1170个子节点,如果当前树高为3层,那么就是1170 * 1170=1368900约等于136万,因为一个页为16k,假设一条数据大小也是16k那么这个三层树只可以存储大概136万条数据,如果一条数据为1k,那么就可以存储1170 * 1170 * 16=21902400约2900万左右数据,在相同数据量情况下,单条数据越大,树越高
3. 在通过索引获取数据时实际走的是二分查找进行查询的,以主键索引为例,io次数等于树高-1,因为根节点是常驻内存的,树越高io次数也就越多

三. 简单总结

  1. 通过上面了解到使用InnoDB存储引擎的记录结构,行格式,与索引页结构, 索引页数据会存储在"表名.ibd" 表空间中
  1. InnoDB在建表时如果有主键会基于主键构建主索引,如果没有主键会选择一个设置了唯一索引的列作为主索引,如果两个都没有会自动创建一个为6个字节的row_id作为主索引使用,反正是InnoDB时一定会有主索引,然后基于主索引存储数据
  2. 数据是按照行格式一行一行进行存储在页中的,MySQL会为每一条数据行添加两个虚拟记录Infimum最小记录与Supremum最大记录,这两个行记录就相当于链表节点的当前节点的位置,与下一个节点的位置(单向链表)
  3. 为了提高查询性能,索引页中存在PageDirectory页面目录,MySQL会通过行记录的Infimum最小记录与Supremum最大记录对一个页中的多条数据进行分组,将分组后每个组中最后一条记录的地址偏移量存入PageDirectory中(这个地址偏移量称为slot 槽),在查询数据时,当定位到该数据所在页后,使用二分查找法在PageDirectory中定位到数据所在的槽,拿到槽中Infimum最小的记录,遍历获取指定数据
  4. 表空间在管理索引页时,为了提高查询性能,将多个页划分为区, 多个区划分为组,在组的上层又存在段的概念
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值