把 MySQL 当成对象容器来建表
MySQL 是关系型数据库,不支持类继承、多态或封装。常见误区是照搬 Java 或 Python 的类结构,比如为
User和
Admin各建一张表,再加一堆重复字段(
name、
created_at),以为“符合面向对象”。结果导致数据冗余、更新异常、联查变重。
实际应优先用范式设计:提取共性到
users表,用
role字段或
user_roles关联表区分权限;特殊字段(如
admin_secret_key)按需单独建扩展表,而非复制整套结构。 避免在多个表中重复定义
updated_at、
status、
is_deleted等通用字段——它们属于业务语义,不是实体差异 不要为每个“子类”新建表并用
type字段模拟多态;联合查询时 MySQL 无法有效利用索引,且
UNION ALL易出错 若真需强类型隔离(如
customer和
supplier完全无交集),也应通过应用层控制写入路径,而非靠表名区分
用 JSON 字段存本该拆分的关系数据
看到 ORM 支持
JSON类型,就随手把地址、订单项、标签列表全塞进一个
meta字段。表面省事,实则破坏可查询性与一致性。
典型错误示例:
orders表里用
items_json存商品 ID、数量、单价,导致无法按“某商品销量”统计,无法加外键约束,备份/同步时 JSON 格式错误还静默失败。
CREATE TABLE orders ( id BIGINT PRIMARY KEY, user_id BIGINT NOT NULL, items_json JSON NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );
JSON字段不能建普通索引;想查“含商品 123 的订单”,只能全表扫描 +
JSON_CONTAINS(items_json, '"123"', '$.id'),性能差 外键、非空、唯一等约束全部失效;插入非法数据不会报错,直到应用读取时解析失败 迁移困难:从 MySQL 5.7 升到 8.0 后,
JSON函数行为有细微变化,历史数据可能解析异常
忽略字符集与排序规则导致的隐式转换
建表时随手写
CHARACTER SET utf8,却没注意 MySQL 的
utf8实际是
utf8mb3,不支持 emoji 和部分生僻汉字;更糟的是混用
utf8mb4_general_ci和
utf8mb4_0900_as_cs,让
=查询忽大忽小、
ORDER BY排序错乱。
常见现象:同一张
users表,
SELECT * FROM users WHERE name = '张三'有时命中有时不命,日志显示字段值明明是
'张三 '(带空格),但
=却匹配成功——因为用了
_ci(case-insensitive)且忽略尾部空格。 统一使用
CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs(MySQL 8.0+)或
utf8mb4_unicode_ci(兼容性更好),避免大小写/空格/重音敏感问题 连接层(如 JDBC URL)也要显式指定
characterEncoding=utf8mb4,否则客户端传入的 emoji 会变成
???已有表修改字符集需同时改列级 collation,仅改表级无效:
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs;
主键滥用自增 + 复合索引设计反模式
为图省事,所有表都设
id BIGINT AUTO_INCREMENT PRIMARY KEY,再额外加
UNIQUE(user_id, order_no)。问题在于:业务查询几乎不用
id,却强制走聚簇索引;而真正高频的
WHERE user_id = ? AND status = ?查询,因索引顺序不对(
user_id在前但
status未覆盖)导致回表严重。
另一个典型是给
logs表加
(created_at, level, module)复合索引,却忘了
created_at范围查询(
BETWEEN)后,后续字段无法走索引——
level和
module实际未生效。 主键优先选业务有意义的自然键(如
order_no),或用
BIGINT自增但确保高频查询能利用其顺序性(如分页场景) 复合索引遵循最左前缀原则:若常查
WHERE a = ? AND b > ? AND c = ?,索引应为
(a, b, c),而非
(a, c, b)对写多读少的表(如日志),考虑用
ARCHIVE引擎或分区表(
PARTITION BY RANGE (TO_DAYS(created_at))),别硬扛索引膨胀
最易被忽略的其实是“变更成本”:一张上线三个月的表,哪怕只是把
VARCHAR(255)改成
VARCHAR(512),在千万级数据上也可能锁表数十秒。设计阶段多花十分钟想清楚字段粒度和关联方式,远比后期加中间层或数据迁移省心。
