字段类型选型是表设计中看似简单却最容易埋坑的环节。选错了类型,轻则浪费存储空间,重则导致查询变慢、索引失效,甚至出现数据异常。本节从实战角度梳理三大类字段类型的特性,并给出明确的选型建议。
数值类型:精确与近似、范围与空间的平衡
MySQL 的数值类型分为整数、浮点数和定点数三类,每一类都有不同的适用场景。
整数类型
整数类型存储的是精确的整数值,是业务中最常用的类型。MySQL 提供了 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT 五种,区别仅在于可表示的范围和占用空间:
| 类型 | 占用字节 | 有符号范围 | 无符号范围 |
|-----------|----------|-----------------------------------------|-----------------------------|
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 |
| SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 |
| MEDIUMINT | 3 | -8388608 ~ 8388607 | 0 ~ 16777215 |
| INT | 4 | -2147483648 ~ 2147483647 | 0 ~ 4294967295 |
| BIGINT | 8 | -2^63 ~ 2^63-1 | 0 ~ 2^64-1 |
选型原则十分直接:选择能覆盖业务预期最大值的最小类型。
- 状态标记、布尔值(0/1)、枚举值数量小于 128 的状态,直接用
TINYINT。一个字段省几个字节,在亿级数据量下就是数 GB 的差距。 - 常见的 ID、用户 ID、订单编号,只要预期不会超过 21 亿,用
INT是最安全的选择,占用空间小,计算速度快。不要一上来就给所有主键用 BIGINT,除非你确实有海量数据的规划(比如自增 ID 真的会超过 21 亿)。很多业务的用户量级,INT 完全足够。 - 只有当数据量可能超过 INT 上限时,才使用
BIGINT,比如分布式 ID 方案中产生的 64 位 ID、流水号。 INT(11)这种写法中的 11 并不是存储限制,只是显示宽度提示(在严格模式下几乎无影响),实际存储空间不变。不要被括号里的数字迷惑。
另外,建议为无负数的字段显式添加 UNSIGNED 属性,既能扩大正数范围,也向阅读表结构的人表明该字段不可能为负。但要注意,UNSIGNED 与有符号值进行计算时,可能会产生预期外的结果(比如减法结果如果为负,会变成一个很大的正数)。在 MySQL 8.0 中,开启 NO_UNSIGNED_SUBTRACTION 可以有效规避这一陷阱。
浮点数与定点数
当需要存储带小数的数值时,你面临 FLOAT、DOUBLE 和 DECIMAL 三种选择。
FLOAT和DOUBLE是近似存储,它们采用二进制浮点数格式,无法精确表示许多十进制小数(比如 0.1)。这意味着WHERE amount = 0.1可能查不到刚刚插入的 0.1。凡是涉及金额、库存精确计算,绝对不要用 FLOAT 或 DOUBLE。DECIMAL(M, D)是定点数,以字符串形式存储,精度完全可控。其中 M 是总位数,D 是小数位数。比如DECIMAL(10,2)表示最多 8 位整数和 2 位小数,适合存储价格(如 12345678.99)。这是存储金额的唯一正确选择。 当然 DECIMAL 占用空间比整数大,计算效率也略低,但在业务正确性面前,这点性能损耗完全可以接受。
实际选型速查:
- 年龄、数量、状态、计数等 →
SMALLINT或INT - 金额、折扣率、百分比 →
DECIMAL(金额用 DECIMAL(18,6) 甚至更精确都可,视系统精度需要) - 科学计算、存储精度要求不高的传感器数据 →
DOUBLE
字符串类型:长度、可变与性能的权衡
字符串类型的选择直接影响存储空间和查询效率。MySQL 的字符串类型主要有 CHAR、VARCHAR、TEXT/BLOB 系列,以及 ENUM、SET 这类少见的枚举类型。
CHAR 与 VARCHAR
这是使用频率最高的两种字符串类型,区别在于 是否变长。
CHAR(N)是定长字符串,无论实际存储数据多短,都占用 N 个字符的空间(末尾空格会被删除)。N 的取值范围是 0 ~ 255。适合存储长度固定或基本固定的短字符串,比如手机号、身份证号、MD5 值、UUID(36 字符)、Y/N 标记。因为长度固定,读取时不需要计算变长信息,性能略优于 VARCHAR。VARCHAR(N)是变长字符串,按实际数据长度占用空间,额外需要 1 或 2 个字节存储长度信息。N 表示最大字符长度,取值范围是 0 ~ 65535(但实际受行大小限制)。适合存储长度变化较大的字段,如姓名、邮箱、地址、标题等。它能显著节约空间,避免 CHAR 造成的空洞。
很多开发者习惯不假思索地把所有字符串都定义为 VARCHAR(255),这是一种懒人做法。255 这个数字并非银弹,它只是恰好能用 1 个字节存储长度信息(VARCHAR 长度小于 256 时,长度标识占 1 字节)。如果你的字段内容不会超过 20 个字符,定义 VARCHAR(30) 更合理,既省空间,又暗示了该字段的预期长度。
TEXT 与 BLOB
当单个字段需要存储的数据量较大时(超过几 KB),就不能再用 VARCHAR,而要换用 TEXT 或 BLOB。
TEXT系列用于存储文本,有 TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT,最大可存 4GB 数据。BLOB系列存储二进制数据,也有对应的 TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB。
选型时的几个关键事实:
- TEXT 和 BLOB 类型的数据不会完全存储在行内,当数据量较大时,InnoDB 会把它放在独立的外部存储页中,行内只保留一个指针。这会导致全表扫描或范围查询时出现额外的磁盘随机读,性能下降明显。
- 无法对 TEXT/BLOB 列创建全列索引,只能指定前缀长度。如果查询需要完整文本匹配,效率会很差。
- 存储评论、文章正文、日志详情等大段文本是合理的;但如果存储的是图片、文件,更好的做法是将它们存放到对象存储(如 OSS、S3),数据库只存放链接地址(VARCHAR)。这样数据库体积可控,备份恢复也更快。
字符集与排序规则
定义字符串类型时,几乎都需要指定字符集(CHARACTER SET)和排序规则(COLLATION)。对于中文业务,推荐使用 utf8mb4 字符集,它是真正的 UTF-8 完整实现,支持 emoji 等 4 字节字符。MySQL 旧版本的 utf8 实际上只支持最多 3 字节字符,是个历史坑,务必避用。
排序规则决定了字符比较、排序时的顺序。utf8mb4_general_ci 通用快速,但排序相对粗糙;utf8mb4_0900_ai_ci(MySQL 8.0 默认)提供了更准确的 Unicode 排序。_ci 后缀表示大小写不敏感,这也是中文场景最常用的选择。
日期时间类型:时区、精度与存储空间的考量
时间在系统中无处不在,选型失误可能导致跨时区问题、查询无效、甚至数据错乱。常用的日期时间类型有 DATE、TIME、DATETIME、TIMESTAMP 和 YEAR。
| 类型 | 占字节 | 范围 | 特点 |
|-----------|--------|-------------------------------------------|--------------------------------|
| DATE | 3 | 1000-01-01 到 9999-12-31 | 只存日期,没有时间 |
| TIME | 3 | -838:59:59 到 838:59:59 | 时间间隔或当日时间 |
| DATETIME | 5~8 | 1000-01-01 00:00:00 到 9999-12-31 23:59:59 | 固定存储,与时区无关 |
| TIMESTAMP | 4~7 | 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC | 存储时转 UTC,取出时转会话时区 |
| YEAR | 1 | 1901 ~ 2155 | 已弃用,不推荐再使用 |
DATETIMEvsTIMESTAMP是最常见的纠结点。核心差异只有一句话:DATETIME 存什么读什么,与服务器时区无关;TIMESTAMP 内部存的是 UTC 时间戳,读取时自动转换为当前会话的时区。 如果你的系统有多个时区的用户,TIMESTAMP 能自动适应,但它的范围有限(2038 年前)。对于绝大多数国内业务,统一用服务器的Asia/Shanghai时区,DATETIME 就很合适,逻辑更直观。除非你需要处理多时区,否则 DATETIME 的麻烦更少。- 自 MySQL 5.6.4 起,DATETIME 和 TIMESTAMP 都可以指定微秒精度(如
DATETIME(3)表示毫秒精度)。不过多数场景秒级精度已经足够,过高的精度没有意义,还会增加存储。 - 不要用 VARCHAR 存储日期时间。这样做无法使用日期函数进行比较、排序,也无法利用日期相关的索引优化,甚至会因为格式不一致出问题。
created_at和updated_at这类时间戳字段,强烈建议用DATETIME并设置默认值CURRENT_TIMESTAMP,记录自动更新。推荐用ON UPDATE CURRENT_TIMESTAMP自动维护修改时间,让数据库替你保证时间线准确。
总结选型思路
- 选数值类型时:优先用整数,确定范围选最小类型;金额永远用 DECIMAL;不要浮点数做业务判断。
- 选字符串类型时:长度固定用 CHAR;变长用 VARCHAR,控制合理长度;大文本慎用 TEXT,非必要不存二进制文件。
- 选日期时间类型时:大多情况 DATETIME 省心好用;多时区需求考虑 TIMESTAMP;绝不使用字符串存时间。
这些看似琐碎的细节,却关系到数据库长期运行的性能与数据质量。字段类型是表设计的基石,地基打得越扎实,上层查询优化和业务扩展就越从容。