阅读innodb 存储结构文档
This commit is contained in:
174
mysql/mysql文档/mysql_表.md
Normal file
174
mysql/mysql文档/mysql_表.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# 表
|
||||
## 索引组织表
|
||||
innodb存储引擎中,表都是根据主键顺序组织存放的,这种存储方式被称为索引组织表(index organized table)。在innodb存储引擎表中,每张表都有主键(primary key),如果在创建表时没有显式指定主键,那么innodb会按照如下方式创建主键:
|
||||
- 首先判断表中是否存在非空的唯一索引(unique not null)字段,如果有,则其为主键
|
||||
- 如果不存在非空唯一索引,那么innodb会自动创建一个6字节大小的指针作为主键
|
||||
|
||||
如果有多个非空唯一索引,innodb存储引擎将会选择第一个定义的非空唯一索引作为主键。
|
||||
|
||||
## innodb逻辑存储结构
|
||||
在innodb的存储逻辑结构中,所有的数据都被逻辑存放在表空间(table space)中。表空间则由`段(segement),区(extent),页(page)`组成。
|
||||
|
||||
组成如图所示:
|
||||
|
||||
<img src="https://pic2.zhimg.com/v2-7a3fe8eb03c68e1378f24847e464e139_1440w.jpg" data-caption="" data-size="normal" data-rawwidth="421" data-rawheight="275" data-original-token="v2-d7e41f080ece2820138fd0331f965a79" class="origin_image zh-lightbox-thumb" width="421" data-original="https://pic2.zhimg.com/v2-7a3fe8eb03c68e1378f24847e464e139_r.jpg">
|
||||
|
||||
### 表空间
|
||||
表空间为innodb存储引擎逻辑结构的最高层,所有数据都存放于表空间中。innodb存在一个默认的共享表空间`ibdata1`,在开启`innodb_file_per_table`参数后,每张表内的数据可以单独存放到一个表空间。
|
||||
|
||||
#### innodb_file_per_table
|
||||
`innodb_file_per_table`参数启用会导致每张表的`数据、索引、插入缓冲bitmap页`存放到单独的文件中;但是其他数据,例如`回滚(undo)信息,插入缓冲页,系统事务信息,double write buffer`等还是存放在默认的共享表空间中。
|
||||
|
||||
### 段(segment)
|
||||
如上图所示,表空间是由段(segment)所组成的,常见的段分为`数据段,索引段,回滚段`等。
|
||||
|
||||
在innodb存储引擎中,数据即索引,索引即数据。`数据段即为B+树的叶子节点(Leaf node segment)`,`索引段即为B+树的非叶子节点(Non-leaf node segment)`。
|
||||
|
||||
### 区(Extent)
|
||||
区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性,innodb存储引擎会一次性从磁盘申请4~5个区。在默认情况下,innodb存储引擎中页的大小为`16KB`,一个区中包含64个页。
|
||||
|
||||
在新建表时,新建表的大小为96KB,小于一个Extent的大小1MB,因为每个段Segmenet开始时,都会有至多32个页大小的碎片页,等使用完这些页后才会申请64个连续页作为Extent。
|
||||
|
||||
都与一些小表或是undo这样的段,可以在开始时申请较少的空间,节省磁盘容量开销。
|
||||
|
||||
### 页(Page)
|
||||
innodb存储引擎中页的大小默认为`16KB`,默认的页大小可以通过`innodb_page_size`参数进行修改。通过该参数,可以将innodb的默认页大小设置为4K,8K。
|
||||
|
||||
页是innodb磁盘管理的最小单位,在innodb中,常见的页有:
|
||||
- 数据页(B-tree node)
|
||||
- undo页(undo log page)
|
||||
- 系统页(system page)
|
||||
- 事务数据页(transaction system page)
|
||||
- 插入缓冲位图页(insert buffer bitmap)
|
||||
- 插入缓冲空闲列表页(insert buffer free list)
|
||||
- 未压缩的二进制大对象页(uncompressed blob page)
|
||||
- 压缩的二进制大对象页(compressed blob page)
|
||||
|
||||
### 行
|
||||
innodb存储引擎是面向行的,数据按行进行存放。每个页中至多可以存放`16KB/2 - 200`行的记录,即7992行记录。
|
||||
|
||||
## innodb行记录格式
|
||||
innodb存储引擎以行的形式进行存储,可以通过`show table status like '{table_name}'`的语句来查询表的行格式,示例如下:
|
||||
```sql
|
||||
show table status like 'demo_t1'
|
||||
```
|
||||
|
||||
| Name | Engine | Version | Row\_format | Rows | Avg\_row\_length | Data\_length | Max\_data\_length | Index\_length | Data\_free | Auto\_increment | Create\_time | Update\_time | Check\_time | Collation | Checksum | Create\_options | Comment |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| demo\_t1 | InnoDB | 10 | Dynamic | 0 | 0 | 16384 | 0 | 32768 | 0 | 1 | 2025-01-30 15:18:57 | null | null | utf8mb4\_0900\_ai\_ci | null | | |
|
||||
|
||||
上述示例中表的row_format为dynamic。
|
||||
|
||||
### Compact
|
||||
在使用Compact行记录格式时,一个页中存放数据越多,其性能越高。
|
||||
|
||||
Compact格式下行记录的存储格式如下:
|
||||
<table>
|
||||
<tr>
|
||||
<td>变长字段长度列表</td>
|
||||
<td>NULL标志位</td>
|
||||
<td>记录头信息</td>
|
||||
<td>列1数据</td>
|
||||
<td>列2数据</td>
|
||||
<td>......</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
#### 变长字段长度列表
|
||||
Compact行记录格式其首部是一个`非Null变长字段长度`列表,其按照列的顺序逆序放置,变长列的长度为:
|
||||
- 如果列的长度小于255字节,用1字节表示
|
||||
- 如果列的长度大于255字节,用2个字节表示
|
||||
|
||||
变长字段的长度不能小于2字节,因为varchar类型最长长度限制为65535。
|
||||
|
||||
#### NULL标志位
|
||||
NULL标志位为bitmap,代表每一列是否为空。如果行中存在n个字段可为空,那么NULL标志位部分的长度为ceiling(n/8)。
|
||||
|
||||
#### 记录头信息
|
||||
record header,固定占用5字节,其中,record header的各bit含义如下所示:
|
||||
| 名称 | 大小(bit) | |
|
||||
| :-: | :-: | :-: |
|
||||
| () | 1 | 未知 |
|
||||
| () | 1 | 未知 |
|
||||
| deleted_flag | 1 | 该行是否已经被删除 |
|
||||
| min_rec_flag | 1 | 该行是否为预订被定义的最小记录行 |
|
||||
| n_owned | 4 | 该记录拥有的记录数 |
|
||||
| heap_no | 13 | 索引堆中该条记录的排序记录 |
|
||||
| record_type | 3 | 记录类型,000代表普通,001代表B+树节点指针, 010代表infimum,011代表supremum,1xx保留 |
|
||||
| next_record | 16 | 页中下一条记录相对位置 |
|
||||
|
||||
除了上述3个部分之外,其他部分就是各个列的实际值。
|
||||
|
||||
> 在Compact格式中,NULL除了占有NULL标志位外,不占用任何实际空间。
|
||||
|
||||
> 每行数据中,除了有用户自定义的列外,还存在两个隐藏列,即`事务id列`和`回滚指针列`,长度分别为6字节和7字节。
|
||||
>
|
||||
> 如果innodb表没有自定义主键,每行还会增加一个rowid列。
|
||||
|
||||
### 行溢出数据
|
||||
innodb存储引擎可能将一条记录中某些数据存储在真正的存储数据页面之外。一般来说,blob、lob这类大对象的存储位于数据页面之外。
|
||||
|
||||
当行数据的大小特别大,导致一个页无法存放2条行数据时,innodb会自动将占用空间大的`blob`字段或`varchar`字段值放到额外的uncompressed blob page中。
|
||||
|
||||
### dynamic
|
||||
dynamic格式几乎和compact格式相同,但是对于每个blob字段,其存储只消耗20字节用于存储指针。
|
||||
|
||||
而对于Compact格式,其会在blob格式中存储768字节的前缀字节。
|
||||
|
||||
### char存储结构
|
||||
`char(n)`字段中n代表`字符长度`而非字节长度,故而在不同字符集下,char类型字段的内部存储可能不是定长的。
|
||||
|
||||
例如,在utf8字符集下,`ab`和`我们`两个字符串,其字符数都是2个,但是`ab`其占用2字节,而`我们`占用4字节,即使同样是`char(2)`类型的字符串,其占用字节数量仍然有可能不同。
|
||||
|
||||
## innodb数据页结构
|
||||
innodb中页是磁盘管理的最小结构,页类型为B-tree Node的页存放的即是表中行的实际数据。
|
||||
|
||||
innodb数据页由如下7个部分组成:
|
||||
- File Header(文件头)
|
||||
- Page Header(页头)
|
||||
- Infimum和Supremum Records
|
||||
- user records(用户记录,即行记录)
|
||||
- free space(空闲空间)
|
||||
- page directory(页目录)
|
||||
- file trailer(文件结尾信息)
|
||||
|
||||
file header,page header,file trailer的大小是固定的,分别为38,56,8字节,这些空间用于标记页的一些信息,例如checksum,数据页所在B+树的层数等。
|
||||
|
||||
<img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d066e690d22484ebe33bbb4977c3cfb~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp" alt="image.png" loading="lazy" class="medium-zoom-image">
|
||||
|
||||
### File Header
|
||||
File Header用于记录页的一些头信息,由8个部分组成,共占38字节:
|
||||
| 名称 | 大小 | 说明 |
|
||||
| :-: | :-: | :-: |
|
||||
| FIL_PAGE_SPACE_OR_CHKSUM | 4 | 代表页的checksum值 |
|
||||
| FIL_PAGE_OFFSET | 4 | 表空间中页的偏移位置 |
|
||||
| FIL_PAGE_PREV | 4 | 当前页的上一个页,B+树决定叶子节点为双向列表 |
|
||||
| FIL_PAGE_NEXT | 4 | 当前页的下一个页 |
|
||||
| FIL_PAGE_LSN | 8 | 代表该页最后被修改的日志序列位置LSN |
|
||||
| FIL_PAGE_TYPE | 2 | 存储引擎页类型 |
|
||||
| FIL_PAGE_FILE_FLUSH_LSN | 8 | 该值仅在系统表空间的一个页中定义,代表文件至少被更新到了该LSN值,对于独立的表空间,该值为0 |
|
||||
| FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 | 代表页属于哪个表空间 |
|
||||
|
||||
### Infimum和Supremum record
|
||||
在innodb存储引擎中,每个数据页都有两行虚拟的行记录,用于限定记录边界。Infimum是比该页中所有主键值都要小的值,Supremum是比任何可能值都要大的值。`这两个值在页创建时被建立,并且在任何情况下都不会被删除。`
|
||||
|
||||
### user record 和 free space
|
||||
user reocrd代表实际存储行记录中的内容,free space则是代表空闲空间,同样是链表数据结构,在一条记录被删除后,该空间会被加入到空闲列表中。
|
||||
|
||||
### page directory
|
||||
page directory(页目录)中存放了记录的相对位置,这些记录指针被称为目录槽(directory slots)。`innodb中槽是一个稀疏目录,一个槽中可能包含多个记录`,记录Infimum的n_owned总为1,记录Supremum的n_owned取值范围为`[1,8]`,其他用户记录的n_owned为`[4,8]`。当记录被插入或删除时,需要对槽进行分裂或平衡的维护操作。
|
||||
|
||||
在slots中,记录按照索引键值顺序存放,可以通过二叉查询迅速找到记录的指针。
|
||||
|
||||
由于在innodb存储引擎中,page directory是稀疏目录,二叉查找结果只是一个粗略的结果,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user