惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

W
WeLiveSecurity
博客园 - 【当耐特】
Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
Cloudbric
Cloudbric
The Register - Security
The Register - Security
小众软件
小众软件
PCI Perspectives
PCI Perspectives
G
Google Developers Blog
AI
AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享
Recent Commits to openclaw:main
Recent Commits to openclaw:main
量子位
TaoSecurity Blog
TaoSecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
F
Full Disclosure
N
Netflix TechBlog - Medium
博客园_首页
Last Week in AI
Last Week in AI
A
Arctic Wolf
B
Blog RSS Feed
J
Java Code Geeks
C
Cybersecurity and Infrastructure Security Agency CISA
I
InfoQ
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
MyScale Blog
MyScale Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Jina AI
Jina AI
有赞技术团队
有赞技术团队
S
Schneier on Security
L
Lohrmann on Cybersecurity
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
P
Palo Alto Networks Blog
S
Security @ Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
Security Latest
Security Latest
Vercel News
Vercel News
博客园 - 司徒正美
Webroot Blog
Webroot Blog
Hacker News: Ask HN
Hacker News: Ask HN
A
About on SuperTechFans

祈雨的笔记

安全多方计算MPC spark原理解析 kueue执行源码分析 spark on k8s执行源码分析 spark-operator源码解析 系统压测遇到的缓存击穿问题 我的世界PC与安卓联机 蚂蚁金服流量投放平台的AIG改造 G1大对象致Old区占用率高 日志打印导致接口响应率下跌分析 Groovy加载类导致OOM分析 ERROR日志打印导致CPU满载 记OceanBase死锁超时 应用发版期间服务响应超时 Ark Serverless初探 系统优化复盘一二三 The user specified as a definer does not exist Kong网关初探 API网关选型调研 CPU火焰图常用工具 配置中心选型调研 root操作Nginx导致用户组错误 基于Proxifier使用代理 FastJSON字段智能匹配踩坑 Nacos初探 记一次Nginx服务器CPU满荷载故障 基于券系统分库分表的思考 limit不参与SQL成本计算致索引失效 Linux常用性能监控命令 golang低版本http2偶现400 hostname in certificate didn't match 常见对称加密原理以及应用 tcp_tw_recycle引起的TCP握手失败 记一次mysql执行DDL导致锁表 mysql磁盘占用查看 mysql对text字段update致磁盘增长 elasticsearch报错index read-only TIME_WAIT与Http的Keep-Alive 记一次TIME_WAIT导致连接数报警 记一次生产事故OOM问题排查 redis分布式锁RedissonLock的实现细节 webservice复杂加密签名(2)java调用 webservice复杂加密签名(1)SoapUI mysql延时关联 利用中间人拦截实现APP内H5窜改 MySQL表字符集不同导致关联查询索引失效 通过SSH隧道远程办公 数据落盘方案 BeanDefinitionRegistryPostProcessor扩展 mysql空间索引 HTTPS攻击 spring循环依赖过程解析 elasticsearch性能优化 mysql IS NULL 使用索引 mysql字符集utf8mb4失效踩坑 常用加密算法 xml与javaBean转换 初探InnoDB MVCC源码实现 mysql索引原理 redis之list源码分析 redis之key过期源码分析 redis之string源码分析 redis之hash源码分析 线程池之ThreadPoolExecutor Using temporary与Using filesort mysql回表致索引失效 springboot(28)HTTP连接池 定时任务之ScheduledThreadPoolExecutor elasticsearch常用script聚合 elasticsearch实现like查询 elasticsearch实现乐观锁 elasticsearch准实时原理 springboot(27)自定义缓存读写机制CachingConfigurerSupport optimizer tracing arthas常用命令 HTTP和HTTPS详解 redis集群选举机制 kafka消息重试 一点压力测试的经验 kafka架构概念 explain分析sql语句字段的解释 JVM问题分析处理手册 logstash过滤器(2)date logstash过滤器(3)dissect logstash编码器(1)json logstash编码器(2)multiline logstash表达式 logstash输入(1)通用选项 logstash输入(3)file logstash过滤器(1)通用选项 logstash输入(2)stdin logstash安装 记一次前端vConsole导致JSON序列化错误排查 解决多个异步操作嵌套问题 fastjson反序列化失败autoType is not support RTMP串流服务 POI自动调整列宽错误 Nginx+Lua实现动态黑名单 使用curl命令模拟POST和GET请求 ResponseEntity下载文件
mysql数据页结构
祈雨的笔记 · 2019-06-22 · via 祈雨的笔记

数据页结构

mysql默认每个数据页为16KB,InnoDB引擎的Compact行记录结构由以下字段组成:

变长字段长度列表 null值列表 记录头信息 RowID 事务ID 回滚指针 列数据1 列数据N
不定长 不定长 5字节 不定长 6字节 7字节 不定长 不定长

变长字段长度列表

对于非固定长度的字段类型,例如varchartextblob、多字节编码的char等,通过变长字段长度列表记录当前行记录的对应字段值的长度(列顺序倒序存放)。

列名 值长度(10进制) 值长度(16进制)
a 'aaa' 3 0x03
b 'b' 1 0x01
c 'ccccc' 5 0x05

则示例的变长字段长度列表的值为05 01 03

null值列表

如果记录的列允许为null,则在该值中记录当前行中值为null的字段(列顺序倒序存放)。

列名 允许为null 是否为null 值(2进制)
a true true 1
b false - -
c true true 1
d true false 0

则示例的null值列表的值为011(二进制),又由于该值必须为整数字节,所以高位补0后的值为000000110x03。如果表中允许为null的列超过8个,则null值列表的值长度大于1字节。

记录头信息

记录头占固定长度5字节:

名称 大小(bit) 描述
() 1 未知
() 1 未知
deleted_flag 1 该行是否已被删除
min_rec_flag 1 为1,如果该记录是预先定义为最小的记录
n_owned 4 该记录拥有的记录数
heap_no 13 索引堆中该条记录的排序记录
record_tye 3 记录类型,000表示普通,001表示B+树节点指针,010表示Infimum,011表示Supremum,1xx表示保留
next_record 16 页中下一条记录的相对位置

案例

1
2
3
4
5
6
7
8
9
CREATE TABLE `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into user_info (id,name,age,address) values (1,'tom',18,'shanghai');

查看mysq目录下的文件user_info.ibd,通过命令hexdump -Cv user_info.ibd > user_info.txt或者使用软件例如UltraEdit查看二进制数据。

普通记录

0x0000C078位置

image

1
2
3
4
5
6
7
8
9
10
08                      变长字段address的长度为8
03 变长字段name的长度为3
00 null值字段列表
00 00 10 FF F0 记录头信息(5字节)
80 00 00 01 RowID:主键id的值为1
00 00 00 1D 4B 93 事务ID(6字节)
E7 00 00 80 31 01 10 回滚指针(7字节)
74 6F 6D 字段name的值:tom
80 00 00 12 字段age的值:18
73 68 61 6E 67 68 61 69 字段address的值:shanghai

最大/最小记录

数据页的每行数据以单向链表的形式连接,因此需要有链表头和链表尾。最小记录就是该单向链表的链表头,最大记录则是该单向记录的链表尾,且两者的记录位置固定不变。

最小记录的位置为0x0000C063,其值固定为infimum

image

1
2
01 00 02 00 1D          记录头信息(5字节)
69 6E 66 69 6D 75 6D 最小记录(固定值infimum)

最大记录的位置为0x0000C070,其值固定为supremum

image

1
2
02 00 0B 00 00          记录头信息(5字节)
73 75 70 72 65 6D 75 6D 最大记录(固定值supremum)

Page Directory

Page Directory中存放了当前数据页中部分数据的相对位置,即
Page Directory是当前数据页数据的目录,mysql通过Page Directory使用二分法减少查询时间。

Page Directory中每个相对位置占2个字节长度,且相对位置从0x0000FFF7倒序排序。其中至少有最小记录和最大记录的相对位置,如下图:

00 63 表示 0x0000C06300 70 表示 0x0000C070

image

当表行数据较多时(创建了35行数据),Page Directory会每4~8条记录存一个相对位置,例如下图:

image

00 63 -> 0x0000C063 -> 最小记录

00 F8 -> 0x0000C0F8 -> ID为4的记录

01 98 -> 0x0000C198 -> ID为8的记录

02 38 -> 0x0000C238 -> ID为12的记录

02 D8 -> 0x0000C2D8 -> ID为16的记录

03 78 -> 0x0000C378 -> ID为20的记录

04 18 -> 0x0000C418 -> ID为24的记录

04 B8 -> 0x0000C4B8 -> ID为28的记录

00 70 -> 0x0000C070 -> 最大记录

数据链表

数据页中行记录按照主键值由小到大顺序串联成一个单链表,且单链表的链表头为最小记录,链表尾为最大记录。并且为了更快速地定位到指定的行记录,通过Page Directory实现目录的功能,借助Page Directory使用二分法快速找到需要查找的行记录。

image

删除表(为了清空文件防止删除的记录干扰)后重新创建表和数据:

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into user_info (id,name,age,address) values (3,'tom',18,'shanghai');
insert into user_info (id,name,age,address) values (1,'peter',22,null);
insert into user_info (id,name,age,address) values (2,'wsen',21,'beijing');

image

最小记录:

1
2
01 00 02 00 44
69 6E 66 69 6D 75 6D 最小记录(固定值infimum)

最大记录:

1
2
04 00 0B 00 00
73 75 70 72 65 6D 75 6D 最大记录(固定值supremum)

ID为3的记录:

1
2
3
4
5
6
7
8
9
10
08                      变长字段address的长度为8
03 变长字段name的长度为3
00 null值字段列表
00 00 10 FF F0 记录头信息(5字节)
80 00 00 03 RowID:主键id的值为3
00 00 00 1D 4B 80
DB 00 00 80 2E 01 10
74 6F 6D 字段name的值:tom
80 00 00 12 字段age的值:18
73 68 61 6E 67 68 61 69 字段address的值:shanghai

ID为1的记录:

1
2
3
4
5
6
7
8
9
05                      变长字段name的长度为5
02 null值字段列表:0x02 -> 00000010即address为null
00 00 18 00 22 记录头信息(5字节)
80 00 00 01 RowID:主键id的值为1
00 00 00 1D 4B 81
DC 00 00 80 1B 01 10
70 65 74 65 72 字段name的值:peter
80 00 00 16 字段age的值:22
字段address的值:null

ID为2的记录:

1
2
3
4
5
6
7
8
9
10
07                      变长字段address的长度为7
04 变长字段name的长度为4
00 null值字段列表
00 00 20 FF B7 记录头信息(5字节)
80 00 00 02 RowID:主键id的值为2
00 00 00 1D 4B 86
DF 00 00 80 2F 01 10
77 73 65 6E 字段name的值:wsen
80 00 00 15 字段age的值:21
62 65 69 6A 69 6E 67 字段address的值:beijing

Page Directory

image

最小记录的记录头信息最后2字节00 44 -> 0x0000C063偏移0x0044 -> 0x0000C0A7,即ID为1的记录的id位置;

ID为1的记录的记录头信息最后2字节00 22 -> 0x0000C0A7偏移0x0022 -> 0x0000C0C9,即ID为2的记录的id位置;

ID为2的记录的记录头信息最后2字节FF B7 -> 0x0000C0C9偏移0xFFB7 -> 0x0000C080,即ID为3的记录的id位置;

ID为3的记录的记录头信息最后2字节FF F0 -> 0x0000C080偏移0xFFF0 -> 0x0000C070,即最大记录的记录位置;