MySQL 表的物理存储结构取决于存储引擎
MySQL 本身不直接管理数据文件,而是由底层存储引擎负责。最常用的是
InnoDB和
MyISAM,它们的表结构和存储方式差异极大,选错引擎会导致事务失效、并发异常或崩溃恢复失败。
InnoDB将表结构(
.frm文件)与数据+索引统一存放在共享表空间(
ibdata1)或独立表空间(
.ibd文件),支持行级锁、ACID、外键
MyISAM把结构(
.frm)、数据(
.MYD)、索引(
.MYI)拆成三个文件,只支持表级锁,不支持事务 执行
SHOW CREATE TABLE t1;可确认当前表用的是哪个引擎;建表时务必显式指定
ENGINE=InnoDB,避免依赖 MySQL 默认配置(5.7 默认是 InnoDB,8.0 也是,但旧环境或 Docker 镜像可能不同)
每一行数据在磁盘上不是“明文字段拼接”
InnoDB 中,一行记录实际以
compact或
dynamic行格式(row format)组织,包含隐藏字段、字段长度偏移量、NULL 标志位等元信息。这意味着: 即使定义了
VARCHAR(255),空字符串或 NULL 并不占满 255 字节,但会额外消耗 1–2 字节记录真实长度 每行有隐藏的
DB_ROW_ID(6 字节)、
DB_TRX_ID(6 字节)、
DB_ROLL_PTR(7 字节)用于 MVCC 和回滚,除非表已定义主键,否则
DB_ROW_ID会作为聚簇索引键 使用
SELECT LENGTH(column) FROM t1;看到的是逻辑长度,不是磁盘占用;真实空间需查
information_schema.INNODB_SYS_TABLES和
INNODB_SYS_INDEXES
索引即数据,聚簇索引决定主键存储顺序
InnoDB 的主键索引(聚簇索引)叶子节点直接存整行数据,而非指针。这意味着:
没有显式定义主键时,InnoDB 会自建一个不可见的ROW_ID作为聚簇索引,且该 ID 是单线程递增的,高并发插入可能成为瓶颈 主键尽量选短、有序、不变的字段(如
BIGINT自增),避免用
VARCHAR(36)UUID——它会导致页分裂频繁、B+ 树深度增加、缓冲池命中率下降 二级索引叶子节点只存主键值(不是磁盘地址),所以
SELECT * FROM t1 WHERE idx_col = ?会先查二级索引,再回表查聚簇索引,两次 B+ 树查找
TEXT / BLOB 类型数据可能被单独存放
当字段类型为
TEXT、
BLOB、
MEDIUMTEXT等,且单行总长度超过约 8000 字节时,InnoDB 会把实际内容移到溢出页(off-page),只在主记录中保留前 768 字节(
REDUNDANT/
COMPACT)或 20 字节(
DYNAMIC/
COMPRESSED)指针。 可通过
SHOW VARIABLES LIKE 'innodb_file_format';和
SHOW TABLE STATUS LIKE 't1';中的
Row_format判断是否启用了
DYNAMIC(推荐) 建表时显式指定:
CREATE TABLE t1 ( id BIGINT PRIMARY KEY, content TEXT ) ROW_FORMAT=DYNAMIC ENGINE=InnoDB;否则默认
COMPACT下,大字段会挤占主记录空间,导致单页能存的行数减少,查询性能劣化
真正影响性能的往往不是字段定义本身,而是字段类型与存储格式、索引结构、引擎特性的组合效果。比如
TINYINT和
ENUM('Y','N') 在语义上都表示开关,但后者在排序、索引合并、备份还原时行为更难预测。细节不在语法,而在落盘那一刻。 