MySQL 字段类型怎么选:一张表看懂,附血泪教训
上个月接手一个老系统,发现订单表的金额字段是 FLOAT。对账差了三分钱,财务排查了一下午。FLOAT 的精度问题教科书写得明明白白,但总有人(包括三年前的我自己)觉得"应该没事"。
建表时多花三十秒选类型,胜过半年后通宵对账。这篇是我的选型手册。
数字类型
| 类型 | 字节 | 适用场景 |
|---|---|---|
TINYINT |
1 | 状态、性别、布尔(0-255) |
INT |
4 | 主键、普通计数(±21 亿) |
BIGINT |
8 | 大表主键、雪花 ID |
DECIMAL(M,D) |
变长 | 金额唯一选择 |
FLOAT/DOUBLE |
4/8 | 科学计算,别存钱 |
金额为什么必须 DECIMAL:FLOAT 是二进制浮点,0.1 + 0.2 ≠ 0.3 那个经典问题就是它。DECIMAL 按十进制精确存储,财务场景没有第二种选择:
-- ✅ 金额标准姿势:总共 10 位,小数 2 位
amount DECIMAL(10, 2) NOT NULL DEFAULT 0.00
-- ❌ 等着对账对到天亮
amount FLOAT
主键建议 INT UNSIGNED 或 BIGINT UNSIGNED:无符号让正数范围翻倍,而且主键本来就没有负数。
字符串类型
| 类型 | 适用场景 |
|---|---|
VARCHAR(n) |
变长字符串,n 按业务实际长度定 |
CHAR(n) |
定长,适合 MD5(32 位)、手机号的场景 |
TEXT 家族 |
大文本:文章内容、JSON |
VARCHAR(n) 的 n 不是越大越好:虽然存储按实际长度,但内存排序和临时表按声明长度分配。VARCHAR(255) 一把梭的人,sort buffer 哭给你看。用户名就 VARCHAR(32),标题 VARCHAR(128),别偷懒。
CHAR vs VARCHAR:CHAR 是定长,存 'abc' 到 CHAR(10) 也占 10 个字符空间。只有长度严格固定的场景(MD5、国家码)才用 CHAR,其他一律 VARCHAR。
TEXT 的坑:TEXT 列不能建普通索引(只能前缀索引)、查询时不能进内存临时表(走磁盘)。文章内容该用还得用,但别把高频查询条件的字段定成 TEXT。
时间类型:别再用 INT 存时间戳了
| 类型 | 字节 | 范围 | 特点 |
|---|---|---|---|
DATETIME |
8(5.6.4+ 为 5 + 小数) | 1000-9999 年 | 不涉及时区,存什么读什么 |
TIMESTAMP |
4 | 1970-2038 年 | 随时区转换,2038 年溢出 |
DATE / TIME |
3/3 | - | 只存日期/时间 |
我的规则:
- 业务时间用 DATETIME:创建时间、更新时间。范围无上限,不受时区影响
- TIMESTAMP 只剩一个优势:跨时区系统自动转换。绝大多数业务用不上,还背着 2038 炸弹
- 千万别用 INT 存时间戳:可读性为零(看到 1565424000 你得拿计算器),还丢了所有日期函数的便利
-- 标准姿势
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP 让 updated_at 在每次更新时自动刷新,省掉应用层维护。
JSON 类型(MySQL 5.7+)
存结构不固定的扩展字段(商品属性、表单配置):
attrs JSON
好处是有 JSON 校验、可以用 -> 和 ->> 查询内部字段、还能对特定路径建虚拟列索引。但记住:JSON 字段是扩展位的补充,不是表结构的替代品。高频查询、需要关联的字段必须平铺成列。
枚举与集合
ENUM('PUBLISHED', 'DRAFT') 看着美好,实际项目里我基本不用:加枚举值要 ALTER TABLE、迁移工具兼容性差、ORM 支持参差不齐。状态字段用 TINYINT + 代码里定义常量是更灵活的实践:
status TINYINT NOT NULL DEFAULT 0 COMMENT '0草稿 1已发布 2已下架'
COMMENT 一定要写,这是给三个月后的自己看的。
我的建表检查清单
- 主键:INT/BIGINT UNSIGNED AUTO_INCREMENT
- 金额:DECIMAL(10,2),杜绝 FLOAT
- 时间:DATETIME + 自动更新,杜绝 INT 时间戳
- 字符串:VARCHAR(n),n 按真实业务长度,不偷懒用 255
- 状态:TINYINT + COMMENT 写清取值含义
- 大文本才用 TEXT,查询条件字段绝不用 TEXT
- 字符集:utf8mb4(utf8 在 MySQL 里是残血版,存不了 emoji)
小结
- 金额 DECIMAL、时间 DATETIME、状态 TINYINT,三件套背下来
- VARCHAR 长度按业务定,sort buffer 会感谢你
- TEXT 别当查询条件,JSON 别当表结构
- 每个字段写 COMMENT,每个表用 utf8mb4
数据库类型选择是典型的"五分钟学习,五年受益"的知识。建表前把这篇翻出来对照一遍,能避开 90% 的低级坑。
看到这里了?如果这篇文章帮到过你
请我喝瓶可乐就好 🥤

评论0 条
还没有评论,来抢沙发~