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 数据不在主记录中

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

相关推荐
Fleshy数模33 分钟前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
az44yao1 小时前
mysql 创建事件 每天17点执行一个存储过程
mysql
秦老师Q3 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
橘子133 小时前
MySQL用户管理(十三)
数据库·mysql
Dxy12393102163 小时前
MySQL如何加唯一索引
android·数据库·mysql
我真的是大笨蛋4 小时前
深度解析InnoDB如何保障Buffer与磁盘数据一致性
java·数据库·sql·mysql·性能优化
怣504 小时前
MySQL数据检索入门:从零开始学SELECT查询
数据库·mysql
人道领域4 小时前
javaWeb从入门到进阶(SpringBoot事务管理及AOP)
java·数据库·mysql
千寻技术帮5 小时前
10404_基于Web的校园网络安全防御系统
网络·mysql·安全·web安全·springboot
spencer_tseng6 小时前
MySQL table backup
mysql