数据库表中的一行行记录是怎么存储的呢?【行记录】
下面介绍一下重要的属性【compact记录结构】:
- 记录头信息:
- delete_mask:是否删除的标识
- min rec mask:B+树的每层非叶子节点中的最小记录都会添加该标记
- n owned:表示当前记录有几个列
- heap_no:表示当前记录在页的位置信息,分配了heap_no之后,值不会改变了,即使删除了堆中的某条记录
- record_type:表示当前记录的类型
- next_record:当前记录的真实数据到下一条记录的真实数据的距离,通过该属性将一个个记录串成单链表
- DB_ROW_ID(row_id):非必须,6字节,表示行ID,唯一标识一条记录【没定义主键,就用的这个】
- DB_TRX_ID:必须,6字节,表示事务ID
- DB_ROLL_PTR:必须,7字节,表示回滚指针
一系列连接起来的记录称之为堆(heap),其中堆的开始是从infimum开始,其余记录从heap_no= 1开始 以supremum标记记录的最大值
(一个头,一个尾)
如何管理这些行记录呢 ----【数据页】
介绍几个重点属性:
-
infimum :记录的起始边界
-
supremum :记录的结束边界
-
user records:真正存记录的地
-
page directory【记录的索引】:有好多槽位,一个槽位管几个行记录,通过二分法定位到某个行记录,然后通过行记录的记录头信息的next_record像单链表一样遍历,遍历次数就是记录头信息的n owned的值
如何快速定位数据页中的记录呢?----【索引】
为数据页建立索引,建立特殊的数据页--【目录页记录】,再为目录页再建立索引,如此反复则建立了b+树这个数据结构
这次只唠唠innodb的存储结构,索引后面再唠
如何管理这么多的数据页呢?【区->组->段->表空间】
区
为什么要引入区的概念?
方便管理,当表中数据很大时,为某个索引分配空间则以区为单位去分配而不是以页分配,以区为单位去读取数据,一个区64个连续的页,默认占用1MB大小,尽可能将随机IO变为顺序IO
区的分类
- 空闲的区free
- 有剩余空闲页面的碎片区free_frag
- 没有剩余空闲页面的碎片区full_frag
- 附属于某个段的区fseg
区的管理
XDES Entry 数据结构:每一个XDES Entry对应一个区
重点属性
List Node :可以将若干个XDES Entry 数据结构【区】串成一个双向链表,将三种链表串起来free链表、free_frag链表、full_frag链表,你或许想问这三个链表的头节点在哪记录啊?稍等片刻,其实是在INODE Entry结构【段】中的list base node属性中记录了,继续往后看!
1、每个段都建立这三种链表
2、每个索引对应两个段,每个段3个上述链表
组
每256个区又被划分成一个组
段
某些零散的页面【碎片区】以及一些完整的区的集合。
- 区标准区:存储同一个段的数据
- 碎片区东拼西凑的:以区为单位给段分配空间对于那些数据量小的,太奢侈了,将这些页面拼凑成一个碎片区,当某个段占用了32个碎片区之后,则就会以完整的区为单位分配存储空间
分类
- 数据段叶子节点段
- 索引段非叶子节点段
- 回滚段
- list base node 记录了三个链表的头节点
表空间
- 表空间想象成被切分为许许多多个页的池子,当我们想为某个表插入一条记录的时候,就从池子中捞出一个对应的页来把数据写进去
- 一个表空间最多支持64TB的数据
总结
- 表空间被划分成许多连续的区,每个区默认由64个页【一个页默认16KB】 也就是1MB组成
- 每256个区【256MB】划分为一个组,每个组最开始的几个页面是固定的类型 【FSP_HDR类型】【IBUF_BITMAP类型页】【INODE类型页】
- 段是一个逻辑上的概念,是一些零散的页面以及一些完整区的集合【杂牌军+正规军】
- 每个段对应一个INODE Entry结构,该结构存了一些与这个段相关的属性【三种链表的起始点】
- 每个区对应一个XDES Entry结构,该结构里有一个list node属性,会连接3种链表【代表三种页面状态的集合】FREE、NOT_FULL、FULL链表