物理模型建表时遗漏UNIQUE索引需先查重清洗再添加;外键字段必须显式建索引;复合查询优先联合索引;JSON字段慎用于可检索场景;时间字段依时区与范围需求选DATETIME或TIMESTAMP。
逻辑模型里“用户”有邮箱唯一约束,物理模型建表时忘了加 UNIQUE 索引怎么办
直接后果是数据层失去校验能力,应用层唯一性检查可能被并发绕过,产生脏数据。数据库不拦,代码再严谨也白搭。
补救不是加个 ALTER TABLE users ADD UNIQUE (email); 就完事——得先确认现有数据是否真满足唯一性:
- 查重复:
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1; - 有重复必须人工清洗或合并,不能跳过
- 加索引前建议在非高峰时段执行,尤其是大表;MySQL 5.7+ 支持
ALGORITHM=INPLACE,但依然会锁表写入 - 注意:PostgreSQL 对空字符串和
NULL的UNIQUE处理不同(NULL不参与冲突判断),MySQL 则把多个NULL当作不同值——这点常被忽略
一对多关系中,“外键字段要不要加索引”不是选择题而是必选项
没索引的外键字段,删父记录或连表查询时会全表扫描子表,性能断崖式下跌。ORM 自动生成的迁移脚本(比如 Django 的 ForeignKey)通常不会自动建索引,得手动补。
实操要点:
- 只要外键字段用于
JOIN、WHERE或ORDER BY,就必须建索引;哪怕只是级联删除,数据库内部也要定位子记录 - 复合查询场景下,单列索引可能不够:比如常查
WHERE category_id = ? AND status = ?,优先建联合索引(category_id, status),而非只建category_id单列索引 - MySQL 中外键约束本身会隐式创建索引(仅限 InnoDB),但 PostgreSQL 不会——别依赖引擎特性,显式建更安全
JSON 字段在 MySQL 5.7+/PostgreSQL 中该不该用,取决于查询模式
用 JSON 存配置、标签、动态属性很爽,但爽完就卡:无法高效按 JSON 内字段检索,索引支持有限,类型校验靠应用层。
判断依据很简单:
- 如果从不按 JSON 内某个 key 做
WHERE或排序(比如只存日志快照、前端埋点原始数据),JSON类型没问题 - 如果经常查
data->'$.tags'或需要JSON_CONTAINS,MySQL 要建虚拟列 + 普通索引,PostgreSQL 得用jsonb_path_opsGIN 索引——写法复杂、维护成本高 - 更隐蔽的坑:
JSON字段无法做GROUP BY或DISTINCT(因精度/顺序问题),想按内容去重得转成字符串哈希后存额外字段
时间字段用 DATETIME 还是 TIMESTAMP?看时区和存储范围需求
别凭感觉选。两者行为差异直接影响数据一致性:
-
TIMESTAMP自动转为 UTC 存储,读取时转回连接时区;DATETIME原样存储,不涉及时区转换 - 如果业务要求“所有用户看到的时间都基于本地时区”,且服务器时区固定,
TIMESTAMP更省心;但如果要存历史时间(比如 1900 年的档案),TIMESTAMP在 MySQL 中只支持 1970–2038 年 - PostgreSQL 的
TIMESTAMP WITH TIME ZONE行为更明确,但 ORM(如 SQLAlchemy)默认映射可能忽略时区,导致入库值被悄悄转换 - 一个硬经验:日志类系统统一用
DATETIME+ 应用层存 UTC 时间戳,避免数据库和应用时区配置不一致引发错乱
物理模型不是逻辑图的机械翻译,每个字段类型、索引、约束背后都有执行计划、并发控制、存储引擎的隐形博弈。最容易被跳过的,是上线前用真实数据量跑一次 EXPLAIN,看关键查询是不是真走索引——画得再漂亮的 ER 图,挡不住一条没索引的外键拖垮整个订单页。










