当前位置: 首页 > news >正文

MySQL一行记录是如何存储的?

以例示意

CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(20), age INT, phone VARCHAR(20), address VARCHAR(100) ) ENGINE=InnoDB;

插入一行:

INSERT INTO users VALUES (1, 'Tom', 18, '13800000000', 'Shanghai');

这行在 InnoDB里不是简单存成

1 Tom 18 13800000000 Shanghai

而是额外存一些管理信息

变长字段长度列表: name 长度、phone 长度、address 长度 NULL 值列表: 哪些字段是 NULL 记录头信息: 这条记录的状态、下一条记录的位置等 隐藏列: 事务 ID trx_id 回滚指针 roll_pointer 可能还有隐藏 row_id 真实数据: id = 1 name = 'Tom' age = 18 phone = '13800000000' address = 'Shanghai'

为什么有“变长字段长度列表”?

因为像这些字段:

因为VARCHAR所修饰的变量长度不确定,本例中name、phone、address均为变长字段,不是固定长度,比如 name='Tom'只需要三个字符的空间,但name='Alexander'需要更多空间。

所以 InnoDB 要在记录里额外存每个变长字段实际用了多少字节。官方文档也说明,对于非 NULL 的变长字段,记录头会保存该列长度,长度字段通常占 1 或 2 字节。

为什么有NULL值列表

如果某个字段允许为NULL,InnoDB 不需要真的存一个字符串"NULL"

他会用类似位图(考过408的兄弟应该不会陌生)的方式标记,即类似于该字段为null时标1,否则标0。

例如:

INSERT INTO users(id, name, age, phone, address) VALUES (2, 'Jack', 20, NULL, NULL);

这一行里phoneaddressNULL,InnoDB 会在 NULL 位图中标记它们。对于COMPACT行格式,可为 NULL 的列会用位向量记录,N 个可为 NULL 的列大约占CEILING(N/8)字节。

什么是记录头信息 record header?

记录头信息是 InnoDB 管理这一行用的,不是你表里定义的字段。内容大概记录

这条记录是否被删除
下一条记录的位置
记录类型
堆中的位置
字段数量相关信息

可以理解成链表和页内管理用的“元信息”。

你写的 SQL 看不到这些内容,但 InnoDB 内部需要靠它们组织数据页里的记录。

什么是隐藏列

InnoDB 的聚簇索引记录除了用户定义的列,还会额外存一些隐藏信息。官方文档说明,聚簇索引记录包含所有用户定义列,另外还有 6 字节事务 ID 字段和 7 字节回滚指针字段;如果表没有主键,还会有 6 字节 row ID 字段。即

trx_id 事务 ID
roll_pointer 回滚指针
row_id 没有主键时才可能有

trx_id表示最后修改这行记录的事务 ID。它和事务隔离、MVCC 有关。

roll_pointer指向 undo log,用来找到这行记录的历史版本。

比如你在事务中更新一行数据

UPDATE users SET age = 19 WHERE id = 1;

InnoDB需要能找到旧版本 age = 18。有同学问了,找到旧版本数据有啥用呢?

这里主要有两个功能

1、保证原子性(Atomicity):提供事务回滚(Rollback)的能力。

当你执行UPDATE users SET age = 19 WHERE id = 1;时,只要你还没有敲下COMMIT提交事务,这个操作就是可以撤销的。如果此时你敲了ROLLBACK命令,或者你的代码突然抛出异常崩溃了。MySQL可以通过roll_pointer知道旧数据长什么样,把18重新盖回到表里,即要么全部发生,要么全部没有发生。

2、为了并发时的“时空穿越”

假设现在是双11高峰期,事务 A正在执行你的 SQL,把age改成了19,但还没提交(未提交的脏数据)事务 B这时恰好进来了,执行了一句SELECT age FROM users WHERE id = 1;

为了防止脏读,可以采用加锁排队的做法,因为事务 A 正在改这行数据,所以这行数据被锁住了。事务 B 只能傻乎乎地干等着,直到事务 A 提交了才能读。这叫“读写冲突”,高并发下性能极差!

为了提高并发性,InnoDB 根本不让事务 B 等!事务 B 发现当前最新的数据age=19是被一个还没提交的事务修改的,它无权查看。于是,事务 B 就会顺着roll_pointer指针,像坐时光机一样回到过去,从undo_log里把历史版本age=18(只要还没commit就不是读脏数据,读还没commit的19,这个19才是脏数据)拎出来,作为查询结果返回给用户。也就是快照读:写操作只锁当前最新版本,读操作去读历史版本。读写互不阻塞,极大提升了并发性能!

真实列数据怎么存

聚簇(主键索引)索引里的一行记录

InnoDB 表数据本身就是按主键组织的 B+ 树。

PRIMARY KEY(id)

那么聚簇索引里叶子节点保存完整行:

id = 1 -> 完整用户记录
id = 2 -> 完整用户记录
id = 3 -> 完整用户记录

所以查询:

SELECT * FROM users WHERE id = 1;

可以直接通过主键索引找到完整行。

二级索引里的一行记录

如果有二级索引:

CREATE INDEX idx_age ON users(age);

二级索引记录不是保存完整行,而是保存:

age = 18, id = 1
age = 20, id = 2
age = 25, id = 3

所以执行:

SELECT * FROM users WHERE age = 18;

流程通常是:

1. 先查二级索引 idx_age,找到 id = 1
2. 再拿 id = 1 去聚簇索引查完整行
3. 这一步就是回表

总结

MySQL InnoDB 的一行记录,本质上是存储在B+ 树数据页中的一条 record

不只存你表里的字段,而是大概包含

变长字段长度信息
NULL 标记
记录头信息
事务隐藏字段
用户真实字段
可能的页外字段指针

http://www.jsqmd.com/news/813731/

相关文章:

  • AI抢走工作?别慌!这7大新职业正在崛起,高薪等你来拿!
  • TypeScript领域建模实战:基于斯坦福本体论七步法构建健壮数据模型
  • 智能汽车目标假车路径跟踪控制【附仿真】
  • OpenTron:基于Node.js的模块化Discord机器人开发框架详解
  • 突破内存墙:Google Gemma 4 如何通过推测解码实现 3 倍提速?
  • 终极指南:如何使用KMS_VL_ALL_AIO一键激活Windows和Office
  • AI代码质检员Codeffect:10个智能体自动审查与优化生成代码
  • Cursor Pro破解工具:如何彻底解决API限制并实现无限免费使用
  • Hysteria:极速抗审查代理工具,多模式跨平台优势尽显
  • 2026 简历制作平台推荐:5 款主流工具深度测评(含 AI 辅助、模板库及导出对比)
  • Python正则表达式详解(一)
  • 跨境电商OPC,掌握这几款产品,实现效率提升,欢迎评论交流
  • 毕业答辩 PPT 做了 3 天还被导师打回?okbiye AI PPT 一键搞定,我把流程和效果都给你测透了
  • DC-DC转换器技术解析与应用指南
  • 嵌入式Day14--函数指针与指针函数
  • 3步搞定视频硬字幕提取:本地化、多语言、高效率的终极解决方案
  • 尾盘选股法程序开发学习初期
  • 08:redis-实战+原理
  • 基于MCP协议实现AI助手安全远程操控服务器的完整指南
  • 番茄小说下载器终极指南:一键获取全网小说并智能转换格式
  • AI Agent驱动的智能着陆页生成:从概念到Next.js工程实践
  • 我到底是不是嘉豪?
  • 基于Semantic Release与GitHub Actions的前端自动化发布流程实战
  • 哈密顿赞颂拉格朗日方程为“科学的诗篇“
  • 逃离“时间回廊”:深度解析华为 FusionCompute 虚拟机时间回退迷局
  • 如何使用 Jenkins 流水线自动构建并推送 Docker 镜像到私有仓库
  • Scrapstyle:基于样式解析的现代Web数据抓取方案
  • MPC轨迹规划与控制算法【附代码】
  • Sunshine游戏串流服务器:快速搭建你的终极跨平台游戏串流系统
  • 城市规划和软件系统设计:复杂度管理的艺术