这段内容是 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
数据不在主记录中
如果你正在设计数据库表结构,我可以帮你估算某个表的存储空间 ,或者给出字段类型优化建议。欢迎继续提问!