范式与库表设计
好的表结构是性能与可维护性的地基。设计阶段多花一小时,往往能省下上线后的无数次迁移。本章讲清从 ER 建模到落地建表的完整过程。
设计流程
- 需求分析:梳理实体(名词)与关系(动词),明确查询模式。
- 概念建模:画 ER 图,确定实体、属性与基数(1:1、1:N、M:N)。
- 逻辑设计:转成关系表,定义主键、外键与约束。
- 规范化:按范式消除冗余,再按性能需要适度反范式。
- 物理设计:选择字段类型、字符集、分区与索引。
三大范式
反范式:为查询而冗余
范式减少写入异常,但会带来更多连接,影响读性能。读多写少的场景常有意冗余:
- 订单表冗余
user_name,避免每次列表查询都连接用户表。 - 统计表冗余
order_count,用触发器或异步任务维护。 - 宽表冗余维度字段,服务于 OLAP 分析。
Tip
反范式的前提是有明确的维护策略:冗余字段由谁在何时更新?若无法保证一致性,宁可不冗余。工程上常见「以范式为底,以反范式为性能补丁」。
主键选型
Warning
随机主键(如 UUID v4)在 InnoDB 中会造成页分裂与空间碎片,显著降低写入性能。需要分布式唯一 ID 时,优先选择趋势递增的雪花类 ID,或使用有序 UUID(如 ULID/UUIDv7)。
字段与类型
- 够用就好:能用
INT别用BIGINT,能用VARCHAR(n)别用TEXT(需权衡)。 - 金额用
DECIMAL:FLOAT/DOUBLE存在精度误差,不可用于金额。 - 时间用
TIMESTAMP/DATETIME:统一时区约定(推荐存 UTC)。 - 状态用
TINYINT+ 注释:比字符串省空间、比较快。 - 布尔用
TINYINT(1)(MySQL)或原生BOOLEAN(PostgreSQL)。 - 避免
NULL泛滥:NULL参与索引与比较时有特殊语义,能设默认值就设。
关系基数与中间表
多对多关系需要中间表(关联表),并常在其上附加关系属性:
小结
- 先规范化消除冗余,再按查询模式适度反范式,并明确一致性维护策略。
- 主键优先趋势递增;避免随机主键带来的写入放大。
- 类型选择「够用、精确、有默认值」;金额用
DECIMAL,时间统一 UTC。 - 多对多用中间表,并把关系自身的属性放在中间表上。