共计 4769 个字符,预计需要花费 12 分钟才能阅读完成。
一. 基础知识
- 本文应用的 Mysql 版本:
8.0.12-debug
- 本文用到的 Linux 命令:
xxd
- 本文须要应用到的知识点:
Mysql
数据页存储, 能够参见https://segmentfault.com/a/1190000037436803
1.1 问题
- Data Dictironary 是什么?
- Data Dictironary(DD, 数据字典)是无关数据库对象的合集, 例如表、视图、索引等, 能够看做是数据库的元信息。换句话说, 数据字典存储了无关表构造的信息, 每个表具备的列, 表的索引等。
2. 零碎表是什么? 跟本人创立的表有何不同?
- 零碎表有很多, 常见的有
mysql.schemata
,mysql.tables
,mysql.indexes
- 咱们创立的表的元信息是放到零碎表中的
- 在内存中, 这些元信息以对象的形式提供给内部应用, 比如说, 创立一个表, 内存中会创立这个表的数据字典对象, 零碎表以及咱们创立的表都会有本人的数据字典对象
- 能够认为零碎表的元信息就存储在本人的数据字典对象中, 这些信息会被序列化到磁盘的 mysql.ibd 文件中
3.DD 存储在哪些地方?
这里是针对 Mysql 8 的数据字典,
- 数据字典信息须要长久化, 存储在 mysql.ibd 文件中, 应用专门的表空间 id
- 在每个独立的表空间中, 也备份了一份这个表空间相干的 dd 对象序列化信息
- 零碎表空间, 也就是 ibdata1 文件中, 并没有存储 dd 对象的相干信息
1.2 DD 存储
Mysql 8 之前, 数据字典存储构造如下图,
从图中能够看出, DD 信息存储在多个中央,
- 局部数据存储在文件中
- 局部数据存储在 mysql 零碎表中
- InnoDB 零碎表也存储了局部数据
这种存储形式存在以下问题:
- 数据存储在多个中央, 难以保护治理
- MyISAM 零碎表容易损坏
- 不反对原子操作
Mysql 8 对数据字典进行了从新设计, 并且应用 InnoDB 存储,
- 将原来存储数据字典的文件全副删除, 对立放到数据字典表空间中
- 对于原来应用 MyISAM 存储的零碎表, 全副替换为 InnoDB 存储, 为实现原子 DDL 提供了可能性
1.3 如何查看数据字典
- 通过命令行连贯 Mysql 服务器查看
SET SESSION debug='+d,skip_dd_table_access_check';
SELECT name, schema_id, hidden, type FROM mysql.tables where schema_id=1 AND hidden='System';
能够看到 DD 中存储了很多零碎表,
+------------------------------+-----------+--------+------------+
| name | schema_id | hidden | type |
+------------------------------+-----------+--------+------------+
| catalogs | 1 | System | BASE TABLE |
| character_sets | 1 | System | BASE TABLE |
| collations | 1 | System | BASE TABLE |
| columns | 1 | System | BASE TABLE |
| dd_properties | 1 | System | BASE TABLE |
| events | 1 | System | BASE TABLE |
| index_stats | 1 | System | BASE TABLE |
| indexes | 1 | System | BASE TABLE |
| schemata | 1 | System | BASE TABLE |
| tables | 1 | System | BASE TABLE |
| tablespace_files | 1 | System | BASE TABLE |
| tablespaces | 1 | System | BASE TABLE |
+------------------------------+-----------+--------+------------+
- 这里咱们仅列出了一部分, InnoDB 还存储了很多其余的零碎表
- 这些零碎表存储了各种元数据, columns 存储了表的列信息, indexes 则存储了表的索引信息, schemata 存储了数据库的信息。
2. 通过 ibd2sdi
工具查看
ibd2sdi test.ibd
- tips: 能够通过 utilities/ibd2sdi.cc 文件, 查看反序列化过程, 进而能够看出 sdi 页面存储构造
1.4 Serialized Dictionary Information(SDI)
- SDI 是什么?
- SDI, 字典序列化信息, 也就是将数据字典中的对象序列化后的数据
2.SDI 如何组织?
- DD 中蕴含很多的数据字典对象, 这些对象的 SDI 数据以 B + 树的形式进行组织
- SDI 数据根本都是压缩后存储的, 压缩形式应用的是 zlib
3.SDI 举例
{
"mysqld_version_id":80012,
"dd_version":80012,
"sdi_version":1,
"dd_object_type":"Table",
"dd_object":{
"name":"x",
"columns":[
{
"name":"id",
"type":4,
...
},
{
"name":"DB_TRX_ID",
"type":10,
...
},
{
"name":"DB_ROLL_PTR",
"type":9,
...
}
],
...
"indexes":[
{
"name":"PRIMARY",
"hidden":false,
...
}
],
...
}
}
- 能够看到, SDI 中存储了这个表的元信息, 比如说这个表的列组成, 索引信息等。
二. SDI 存储页示例
上文中, 咱们说过在每个表的独立表空间中, 存储了这个表相干的 DD 对象序列化后的信息。在这一节, 咱们学习如何查看 SDI 信息。
- 咱们首先建设一个表
CREATE TABLE `x` (`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
- 表所在的库是 hbshi
2. 查看这表的独立表空间文件, 通过 xxd x.ibd x.txt
查看这个表空间的数据页, 这里咱们次要看第 4 页, 这 1 页存储了这个表相干的数据字典序列化信息,
000c000: 723e 3b3c 0000 0003 ffff ffff ffff ffff r>;<............
000c010: 0000 0000 0128 06fc 45bd 0000 0000 0000 .....(..E.......
000c020: 0000 0000 0004 0002 04d2 8004 0000 0000 ................
000c030: 0172 0001 0001 0002 0000 0000 0000 0000 .r..............
000c040: 0000 ffff ffff ffff ffff 0000 0004 0000 ................
000c050: 0002 00f2 0000 0004 0000 0002 0032 0100 .............2..
000c060: 0201 0f69 6e66 696d 756d 0003 000b 0000 ...infimum......
000c070: 7375 7072 656d 756d cb80 0000 10ff f100 supremum........
000c080: 0000 0200 0000 0000 0000 0900 0000 0016 ................
........
........
000fff0: 0000 0000 0070 0063 723e 3b3c 0128 06fc .....p.cr>;<.(..
对于一个表空间而言, 可能会有多个数据字典对象, 这些对象序列化后会以 B + 树的形式组织, 咱们看一下具体的存储构造,
整体页存储构造:
- 最小记录: 记录头[00c05e, 00c062], 具体数据[00c063, 00c06a]
- 最大记录: 记录头[00c06b, 00c06f], 具体数据[00c070, 00c077]
- 第 1 条记录: 2 字节的内容长度[00c078, 00c079]; 5 字节的记录头[00c07a, 00c07e]; 记录内容[00c07f, 00c16a]。
- 第 2 条记录: 2 字节的内容长度[00c16b, 00c16c], 5 字节的记录头[00c16d, 00c171], 记录的数据[00c172, 00c4d1]
咱们再看下每个记录的具体存储:
- 长度: 1 或者 2 个字节的长度字段, 阐明 内容长度
- 5 个字节的记录头
- 33 个字节的记录阐明
- 记录的具体内容, 记录头后面的 1 或者 2 个字节就是阐明这个记录的内容长度的
记录阐明:
- 4 个字节的类型信息
- 8 个字节的 id
- 6 个字节的事务 id
- 7 个字节的回滚指针
- 4 个字节的非压缩长度
- 4 个字节的压缩长度
存储构造如下图所示,
3.SDI 记录遍历流程
- 从最小记录计算第 1 条记录, 00c063 + 010f = 00c172
- 计算第 2 条记录, 00c172 + ff0d = 00c07f
- 计算第 3 条记录: 00c07f + fff1 = 00c070
留神: 游标以每个记录的内容开始, 例如最小记录的开始地位为 00c063, 而不是 00c05e。计算的下一个记录地位, 也是内容开始的地位。
4. 如何查看 SDI 压缩后的数据
- 首先通过 bin/ibd2sdi 工具, 将 ibd 文件中的 sdi 信息打印
- 从打印中的信息中, 抉择其中一项
- 从选中的一项中抽出 object 字段, 将其压缩, 这里咱们应用 python 脚本进行压缩, 压缩形式应用 zlib
#!/usr/bin/env python
# -*- coding=utf-8 -*-
#
import os
import sys
import json
import time
import datetime
import hashlib
import zlib
reload(sys)
sys.setdefaultencoding("utf-8")
def help():
f = open("./test.data")
l = f.readline()
d = zlib.compress(l)
f2 = open("./tmp.data", 'w+')
f2.write(d)
help()
- 比照压缩后的数据以及表空间存储的数据
三. DD 表空间
- InnoDB 罕用表空间
- 表空间占用 4 个字节, 最大值为 0xffff ffff, 这个表空间是有效的, 没有应用
0xffff fffe
是 data dicitonary 的表空间, 也就是 mysql.ibd0xffff fffd
是长期表空间, 也就是 ibtmp1.ibd- [
0xffff fffc
,0xffff fff0
]是为日志筹备的 - [
0xffff ffef
,0xffff ff70
]是 undo log - [
0x0000 0002
,0xffff ff6f
]是能够失常应用的表空间 - 1 是
sys/sys_config
表空间 - 0 是零碎表空间, 也就是 ibdata1
2.DD 表空间存储
- DD 表空间存储构造与个别的独立表空间存储构造是雷同的
- 第 4 页 (page 3) 是 SDI 存储的 root page, SDI 以 B + 树进行组织
- 除了 SDI 信息外, 具体的元数据表也存储在这个表空间中, 存储形式与个别的表是一样的。
四. 总结与思考
- 本文次要介绍了 Mysql 数据字典的概念以及数据字典的存储, 这里咱们简略总结一下,
- 零碎表以及咱们创立的表, 元信息都存储在数据字典中
- 咱们本人创立的表的元信息存储在零碎表中, 同时也会序列化到本人的表空间中
- 零碎表的元信息, 存储在零碎表的数据字典对象中, 这些信息会被序列化到 mysql.ibd 文件中
- SDI 信息以 B + 树的形式进行组织
2. 思考
- DD 是数据库对象的合集, Mysql server 层以及 InnoDB 层都须要这些信息, 他们是如何具体操作的?
- DDL 的原子性是如何实现的?
五. 参考
- https://dev.mysql.com/doc/ref…
正文完