Files
rikako-note/mysql/mysql文档/mysql_表.md
2025-02-02 19:57:30 +08:00

175 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 表
## 索引组织表
innodb存储引擎中表都是根据主键顺序组织存放的这种存储方式被称为索引组织表index organized table。在innodb存储引擎表中每张表都有主键primary key如果在创建表时没有显式指定主键那么innodb会按照如下方式创建主键
- 首先判断表中是否存在非空的唯一索引unique not null字段如果有则其为主键
- 如果不存在非空唯一索引那么innodb会自动创建一个6字节大小的指针作为主键
如果有多个非空唯一索引innodb存储引擎将会选择第一个定义的非空唯一索引作为主键。
## innodb逻辑存储结构
在innodb的存储逻辑结构中所有的数据都被逻辑存放在表空间table space中。表空间则由`段segementextentpage`组成。
组成如图所示:
<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的默认页大小设置为4K8K。
页是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代表infimum011代表supremum1xx保留 |
| 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 headerpage headerfile trailer的大小是固定的分别为38568字节这些空间用于标记页的一些信息例如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是稀疏目录二叉查找结果只是一个粗略的结果