mysql数据库表的基本结构与数据存储方式

10次阅读

mysql表物理存储由存储引擎决定,InnoDB将数据与索引统一存放并支持行级锁、ACID和聚簇索引,MyISAM则分存结构、数据、索引且仅支持表级锁;建表须显式指定ENGINE=InnoDB并优选短有序主键,大字段应设ROW_format=DYNAMIC以避免溢出页性能损耗。

mysql数据库表的基本结构与数据存储方式

MySQL 表的物理存储结构取决于存储引擎

MySQL 本身不直接管理数据文件,而是由底层存储引擎负责。最常用的是 InnoDBMyISAM,它们的表结构和存储方式差异极大,选错引擎会导致事务失效、并发异常或崩溃恢复失败。

  • InnoDB 将表结构(.frm 文件)与数据+索引统一存放在共享表空间(ibdata1)或独立表空间(.ibd 文件),支持行级锁、ACID、外键
  • MyISAM 把结构(.frm)、数据(.MYD)、索引(.MYI)拆成三个文件,只支持表级锁,不支持事务
  • 执行 SHOW CREATE table t1; 可确认当前表用的是哪个引擎;建表时务必显式指定 ENGINE=InnoDB,避免依赖 MySQL 默认配置(5.7 默认是 InnoDB,8.0 也是,但旧环境或 docker 镜像可能不同)

每一行数据在磁盘上不是“明文字段拼接”

InnoDB 中,一行记录实际以 compactdynamic 行格式(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_TABLESINNODB_SYS_INDEXES

索引即数据,聚簇索引决定主键存储顺序

InnoDB 的主键索引(聚簇索引)叶子节点直接存整行数据,而非指针。这意味着:

  • 没有显式定义主键时,InnoDB 会自建一个不可见的 ROW_ID 作为聚簇索引,且该 ID 是单线程递增的,高并发插入可能成为瓶颈
  • 主键尽量选短、有序、不变的字段(如 BIGINT 自增),避免用 VARCHAR(36) UUID——它会导致页分裂频繁、B+ 树深度增加、缓冲池命中率下降
  • 二级索引叶子节点只存主键值(不是磁盘地址),所以 SELECT * FROM t1 WHERE idx_col = ? 会先查二级索引,再回表查聚簇索引,两次 B+ 树查找

TEXT / BLOB 类型数据可能被单独存放

当字段类型为 TEXTBLOBMEDIUMTEXT 等,且单行总长度超过约 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 下,大字段会挤占主记录空间,导致单页能存的行数减少,查询性能劣化

真正影响性能的往往不是字段定义本身,而是字段类型与存储格式、索引结构、引擎特性的组合效果。比如 TINYINTenum('Y','N') 在语义上都表示开关,但后者在排序、索引合并、备份还原时行为更难预测。细节不在语法,而在落盘那一刻。

text=ZqhQzanResources