MySQL数据类型存储全解析

这段内容是 MySQL 官方文档中关于"数据类型存储需求"(Data Type Storage Requirements)的章节(13.7),它详细解释了不同数据类型在磁盘上占用多少空间,以及影响存储的各种因素。

下面我将用通俗易懂的方式 为你逐部分解析,并结合实际开发场景告诉你 "这对我意味着什么?"


🧱 一、核心原则:MySQL 存储的基本规则

✅ 最大行大小限制

  • 每行最多 65,535 字节
    • 这是 MySQL 内部所有存储引擎的硬性限制(InnoDB、MyISAM 等)
    • ❗但 不包括 BLOB、TEXT、JSON 类型的实际数据
    • 它们只算 9~12 字节的指针,真实数据存在别处

🔍 举例:

你不能创建一个表,包含 30 个 VARCHAR(65535) 字段,因为总和远超 65K。


🛠 二、按类型详解存储需求


1️⃣ 数值类型(Numeric Types)

类型 占用空间 说明
TINYINT 1 字节 范围:-128 ~ 127
SMALLINT 2 字节 -32,768 ~ 32,767
MEDIUMINT 3 字节 -8M ~ 8M
INT / INTEGER 4 字节 -21亿 ~ 21亿
BIGINT 8 字节 极大整数
FLOAT 4 字节 单精度浮点
DOUBLE 8 字节 双精度浮点
DECIMAL(M,D) 动态计算 高精度小数
🔍 DECIMAL 的存储方式(重点!)
  • 9 个十进制数字 → 用 4 字节 存储
  • 多出来的"零头"按如下规则:
多出的位数 额外字节数
0 0
1~2 1
3~4 2
5~6 3
7~8 4

✅ 示例:DECIMAL(10,2) = 10 位总长度,2 位小数 → 整数部分 8 位

  • 小于 9 位 → 占 4 字节

📌 用途 :金融、金额字段推荐使用 DECIMAL,避免浮点误差。


2️⃣ 日期时间类型(Date & Time)

MySQL 5.6.4 开始 ,支持小数秒(fractional seconds),所以存储空间变了:

类型 旧版本(<5.6.4) 新版本(≥5.6.4)
YEAR 1 字节 1 字节
DATE 3 字节 3 字节
TIME 3 字节 3 + 小数秒(0~3 字节)
DATETIME 8 字节 5 + 小数秒(0~3 字节)
TIMESTAMP 4 字节 4 + 小数秒(0~3 字节)
小数秒额外开销:
精度(fsp) 额外字节数
0 0
1~2 1
3~4 2
5~6 3

✅ 示例:

  • TIME(0) → 3 字节
  • TIME(3) → 3 + 2 = 5 字节
  • DATETIME(6) → 5 + 3 = 8 字节

📌 建议 :除非需要毫秒级日志记录,否则用 DATETIME(0) 节省空间。


3️⃣ 字符串类型(String Types)

这是最容易出问题的部分,尤其是涉及 utf8mb4 和变长字段。

基本公式:
类型 存储空间
CHAR(M) M × w 字节,w 是字符集最大宽度(如 utf8mb4 是 4)
VARCHAR(M) L + 1 或 2 字节,L 是实际长度
TEXT 系列 L + 长度前缀字节数
长度前缀规则(关键!):
类型 长度前缀 最大容量
TINYTEXT / TINYBLOB 1 字节 < 2^8 = 256 字节
TEXT / BLOB 2 字节 < 65,536 字节(~64KB)
MEDIUMTEXT / MEDIUMBLOB 3 字节 < 16MB
LONGTEXT / LONGBLOB 4 字节 < 4GB

✅ 示例:

  • 'hello' 存入 VARCHAR(50)(latin1):5 + 1 = 6 字节
  • '😊你好'(3 个字符)存入 utf8mb4:每个最多 4 字节 → 实际 L ≈ 10 字节 → 总计 10 + 2 = 12 字节(因为最大可能超 255 字节,所以用 2 字节长度前缀)
💡 特别注意:VARCHAR 的长度前缀选择
  • 如果列定义的最大长度 ≤ 255 字节 → 用 1 字节 记录实际长度
  • 如果 > 255 字节 → 用 2 字节 记录长度

🚨 陷阱示例:

sql 复制代码
VARCHAR(200) CHARACTER SET latin1 → 最多 200 字节 → 用 1 字节长度前缀
VARCHAR(200) CHARACTER SET utf8mb4 → 最多 800 字节 → 用 2 字节长度前缀
⚠️ 行大小限制下的 VARCHAR 最大可用长度

由于整行不能超过 65,535 字节,且多个字段共享这个空间:

  • 使用 utf8mb4 时,每个字符最多占 4 字节
  • 所以 VARCHAR(N) 实际最多只能定义到:
text 复制代码
N = floor(65535 / 4) = 16,383 个字符

✅ 所以:VARCHAR(16383) CHARSET utf8mb4 是理论极限


4️⃣ 枚举和集合类型(ENUM / SET)

类型 存储空间 说明
ENUM 1 或 2 字节 ≤ 255 个值 → 1 字节;否则 2 字节
SET (N+7)/8 字节 N 是成员数量,向上取整到 1/2/3/4/8 字节

✅ 示例:

  • ENUM('男','女') → 1 字节
  • SET('阅读','运动','音乐','旅行') → 4 个元素 → (4+7)/8 = 1.375 → 向上取整为 1 字节

📌 优点 :节省空间,适合固定选项字段(如性别、标签)

📌 缺点 :修改枚举值需 ALTER TABLE,扩展性差


5️⃣ JSON 类型

  • 存储开销 ≈ LONGTEXT
  • 但有额外 二进制元数据开销 (用于快速查找)
    • 比如字符串多占 4~10 字节
  • 实际内容以 二进制格式(BSON-like) 存储,支持高效查询
  • 单个 JSON 文档最大不能超过 max_allowed_packet 参数值(默认 64MB 或 1GB)

📌 建议

  • 适合存储结构化但不固定的配置信息
  • 不要用来存大文本日志,用 TEXT 更高效

6️⃣ 空间类型(Spatial Types)

  • 格式:SRID (4字节) + WKB二进制数据
  • LENGTH() 函数可查看占用字节数
  • 常见类型:POINT, LINESTRING, POLYGON

📌 用途:地理信息系统(GIS)、地图应用


🧩 三、特殊存储引擎注意事项

1. InnoDB

  • 支持行压缩、页压缩
  • 大字段(>768字节)会自动"搬出主记录",存在"溢出页"(off-page)
  • 固定长度字段也可能被当作变长处理(如 CHAR(255) utf8mb4 可能 > 768 字节)

📌 影响:查询大字段稍慢,但主记录更紧凑

2. NDB(MySQL Cluster)

  • 强制 4 字节对齐
    • 即使 TINYINT 也占 4 字节
  • VARCHAR('abcd') 在 latin1 下本该 5 字节 → 实际占 8 字节
  • 每个允许 NULL 的表额外预留 4~8 字节
  • 没有主键 → 自动创建隐藏主键(31~35 字节开销)
  • TEXT/BLOB/JSON 分成两部分存储:主表 + 隐藏片段表

📌 建议:NDB 对存储不友好,设计时要特别精简字段


✅ 四、开发者必须知道的最佳实践

问题 解决方案
插入 emoji 报错? 改用 utf8mb4 字符集!
VARCHAR(255) 实际能存多少汉字? utf8mb4 下最多 255 ÷ 4 = 63 个汉字(最坏情况)
表字段太多提示"row size too large"? 减少字段数,或把大字段改为 TEXT
DECIMAL 用多少位合适? 金额建议 DECIMAL(10,2)DECIMAL(19,4)
如何节省空间? TINYINT 代替 INT 表示状态;少用 TEXT;避免 NULL
如何估算一张表的存储? 逐字段计算平均长度 × 行数,再加索引开销(约 1.5~2 倍)

📊 五、实用检查 SQL

sql 复制代码
-- 查看表结构和字符集
SHOW CREATE TABLE your_table;

-- 查看某字段实际占用字节数
SELECT LENGTH(your_column), CHAR_LENGTH(your_column) FROM your_table;

-- 查看数据库总大小(MB)
SELECT 
  table_schema,
  ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.tables
GROUP BY table_schema;

🎯 总结一句话:

MySQL 的存储不是简单的"一个字符一个字节"。你要考虑:字符集(特别是 utf8mb4)、变长字段的长度前缀、行大小限制、存储引擎特性(如 NDB 对齐)、以及 NULL 和小数秒带来的额外开销。


现在你可以回答这些问题了:

  • 为什么 VARCHAR(255) 是"黄金长度"?→ 因为 ≤255 时长度前缀只需 1 字节
  • 为什么不能有太多 VARCHAR(255) 字段?→ 总行大小不能超过 65,535 字节
  • 为什么 emoji 存不了?→ 因为用了 utf8(实际是 utf8mb3),不支持 4 字节字符
  • 为什么 TEXT 字段很多但表还不大?→ 因为 TEXT 数据不在主记录中

如果你正在设计数据库表结构,我可以帮你估算某个表的存储空间 ,或者给出字段类型优化建议。欢迎继续提问!

相关推荐
siriuuus4 小时前
Linux MySQL 多实例部署与配置实践
linux·运维·mysql
王木风5 小时前
1分钟理解什么是MySQL的Buffer Pool和LRU 算法?
前端·mysql
qq_404643347 小时前
MySQL中RUNCATE、DELETE、DROP 的基本介绍
数据库·mysql
像风一样!8 小时前
MySQL数据库如何实现主从复制
数据库·mysql
大白的编程日记.8 小时前
【MySQL】数据库表的CURD(二)
android·数据库·mysql
Olrookie9 小时前
MySQL运维常用SQL
运维·数据库·sql·mysql·dba
jingfeng51410 小时前
MySQL表的增删改查
数据库·mysql
枫叶_v12 小时前
【DB】Oracle转MySQL
数据库·mysql·oracle
自己收藏学习12 小时前
统计订单总数并列出排名
数据库·sql·mysql