MySQL数据库规范是避免线上故障的实践指南:主键须用自增BIGINT,禁用UUID或业务字段;字段类型要精准(如TINYINT存状态、DECIMAL存金额);索引遵循最左前缀且总数≤5;字符集必须utf8mb4,引擎强制InnoDB,禁用外键。

MySQL 数据库规范不是一堆教条,而是用血泪换来的“少踩坑指南”——它直接决定你上线后查不出数据、改不动字段、扛不住流量时,是连夜改代码,还是喝着咖啡等告警。
主键怎么设?别让 id 成为性能杀手
绝大多数表都该有 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,但关键在“为什么必须是自增整数”:InnoDB 的聚簇索引按主键物理排序,随机主键(比如 UUID 或业务订单号)会导致频繁页分裂、磁盘随机写飙升。实测大表插入吞吐量可能跌 3–5 倍。
- 禁止用
VARCHAR(32)存 UUID 当主键(除非你明确做了 UUID ordered 变种) - 禁止把
user_id设为主键——它不自增,且可能被更新;应建UNIQUE KEY并加索引 - 如果真需要逻辑唯一标识(如订单号),单独加
UNIQUE INDEX,别动主键
字段类型选错,空间和性能双崩
用 TINYINT UNSIGNED 存状态(0/1/2/3),比用 ENUM 或 VARCHAR(10) 省 3 字节/行,1000 万行就省下近 30MB 内存+索引空间,且排序、比较更快。
-
DATETIME优先于TIMESTAMP:后者依赖时区、范围小(1970–2038)、自动更新行为易引发意外 - 金额一律用
DECIMAL(16,2),不用FLOAT——浮点误差在结算场景里就是资损 -
TEXT必须拆表:它不进 InnoDB 行内存储,会拖慢全表扫描;评论、日志类字段,单独建t_order_comment关联 - IP 地址用
INT UNSIGNED+INET_ATON()/INET_NTOA(),比VARCHAR(15)节省空间、支持范围查询
索引不是越多越好,idx_user_status_created 这种命名背后有陷阱
复合索引 INDEX idx_user_status_created (user_id, status, created_at) 能加速 WHERE user_id = ? AND status = ? ORDER BY created_at,但对 WHERE status = ? 完全无效——因为没满足最左前缀。
- 单表索引总数 ≤ 5 个:每多一个索引,INSERT/UPDATE 就多一次 B+ 树维护开销
- 字段顺序决定能覆盖哪些查询:高频过滤字段放最左,
ORDER BY字段放最后(如需避免 filesort) - 避免对低选择性字段(如
gender TINYINT只有 0/1)单独建索引,基本不起作用 - 索引名必须见名知意:
idx_order_user_id比idx_1强一万倍;唯一索引用uniq_order_no
字符集和引擎选错,上线即事故
用 utf8 存 emoji?会报错或截断。MySQL 的 utf8 实际是 utf8mb3,最多存 3 字节字符;真正兼容 emoji 的是 utf8mb4,建库建表必须显式指定。
- 所有表强制
ENGINE=InnoDB:MyISAM 不支持事务、行锁、崩溃恢复,现代业务根本不能用 - 建表语句必须带
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci,否则继承库默认(可能是过时的 utf8) - 所有字段加
COMMENT,哪怕只是COMMENT '1:启用,2:禁用'——三个月后你绝对想感谢当初写注释的自己 - 禁止外键:
FOREIGN KEY在高并发下易死锁,且业务层做校验更灵活;靠开发约定 + 唯一索引约束替代
真正难的从来不是“知道要做什么”,而是每次建表时,是否愿意花 30 秒确认 id 类型、字符集、注释和索引字段顺序——这些地方不卡壳,后面半年都不会为慢查询和数据不一致掉头发。










