跳至内容
04 量体裁衣:数据类型、约束与可靠数据表达

第 4 章 量体裁衣:数据类型、约束与可靠数据表达

逻辑模型说明“系统要保存什么事实”,物理模式则必须回答“这些事实允许以什么二进制表示、何时判错、怎样迁移、付出多少读写成本”。把 price numericstatus textcreated_at timestamptz 写进表,只是选了类型类别;若没有精度、值域、时区、状态转换和失败语义,它们仍不是可靠的数据合同。

本章把 ch03 的逻辑模型 v0 原地迁移为 ch04-v1。范围内四项未决被关闭:人民币金额改用“分”的整数表示,事件瞬间统一为 timestamptz(3),内部键接入 identity 序列,订单与支付状态改由查找表、迁移图和伴随字段共同约束。与此同时,本章明确留下两条边界:支付总额与订单总额等跨表不变量将在后续事务/数据库逻辑章节处理;当前没有体量和生命周期证据,因此不预先分区。

本章目标

完成本章后,读者应当能够:

  • 根据业务精度、范围、运算与序列化合同选择整数、numeric 或其他数值类型;
  • 区分文本的存储、比较、排序、大小写折叠与 Unicode 正规化语义;
  • 区分瞬间、当地民事时间、业务日期与持续时间,正确使用 timestamptz
  • 为内部键、业务键、外部引用和公开标识选择不同生成策略;
  • 在布尔、枚举、CHECK、查找表和状态机之间作有证据的选择;
  • 明确 SQL NULL、JSON null、缺席与不适用的差别;
  • 正确使用 default、identity、sequence 与 stored generated column;
  • 用命名 PK/UK/FK/CHECK/EXCLUDE 表达不变量,并读懂对应 SQLSTATE;
  • 只对支持的约束使用 DEFERRABLE,理解延迟检查与 ON CONFLICT 的冲突;
  • 从行宽、TOAST、索引和写放大评估类型/约束的物理成本;
  • 通过体量、生命周期或裁剪证据进入分区决策,而不是把分区当默认模板;
  • 在 Pigsty L1 完成 v0→v1 事务迁移、反例审查、复位和 verify:state 取证。

开始之前

本章的升级路径要求已经运行 ch03 的 setup + seed,当前摘要应为:

model_version=ch03-v0
relation_checksum=cd7daa66543a6b5e0a5d7fc269558a6c

沿用 ch02 的私有 PGSERVICEFILEpg36-admin service。实验基线是 PostgreSQL 18.4、Pigsty v4.4.0、Ubuntu 24.04;本章实际使用的 DDL 保持 PostgreSQL 14–18 可用。PG18 新增但旧版本没有的能力会单独标注,不能倒推成全版本事实。

下载资产:

从 v0 到 v1

决策面ch03-v0ch04-v1仍未解决
金额unconstrained numericcurrency_code='CNY' + bigint多币种、退款、税费
时间无显式精度的 timestamptz事件 timestamptz(3);UTC 验证;状态时间一致性地方日程/长期时区规则
内部标识手工 bigintBY DEFAULT AS IDENTITY + PK多写者公开 UUID
状态值任意 textowner-only catalog + FK新状态发布流程
状态转换任意覆盖transition table + guard trigger金额等跨表转换前置条件
派生事实view 中临时相乘stored line_total_minor跨行聚合
文本键只做唯一ASCII 格式与规范写入全球化身份匹配
分区未决定ADR 接受“暂不分区”ch26 量级复查
    flowchart LR
  A["ch03-v0<br/>逻辑关系可运行"] --> B["迁移前证明<br/>可表示、无越界"]
  B --> C["事务 DDL<br/>类型 + 约束 + 序列"]
  C --> D["反例<br/>错误类型与约束名"]
  D --> E["verify:state<br/>ch04-v1"]
  E --> F["ADR<br/>暂不分区"]
  

v1 的“可靠”是有范围的:它保证本章登记的行内、值域、引用与状态边规则;它不声称一个本地约束能够证明外部支付真实发生,也没有把“订单至少一行、已付金额等于应付金额”伪装成普通 CHECK

所属位置

  • 卷别:上卷:应用开发(独立导读页,不构成章节父目录)
  • 教学分组:第一篇:筑基——建立 PostgreSQL 工程认知
  • 兼容入口:/ch04//volume-1/data-types-constraints/

本章目录

4.1 金额、文本与时间

先定义单位、比较和时间语义,再选类型名;本章案例把 CNY 金额闭合到整数“分”,把事件瞬间闭合到毫秒精度。

4.2 标识、状态与半结构化数据

内部引用键、外部标识和状态码不是同一种数据;半结构化类型也不能代替需要独立约束与生命周期的关系事实。

4.3 NULL、默认值与生成值

默认值、identity 与生成列分别回答“缺省输入”“键生成”和“行内派生”,不能互换。

4.4 用约束表达不变量

命名约束既是数据库防线,也是稳定的错误定位和目录审计接口;实验会实际验证 EXCLUDE 与延迟唯一检查。

4.5 类型与约束的物理代价

类型与约束会改变行宽、索引数量、TOAST 访问和每次写入的工作量;可靠性设计必须把这些成本显式记账。

4.6 分区决策门

分区是一项针对大表生命周期和访问路径的物理决策。当前模型选择不分区,并保留可触发复查的证据门槛。

4.7 实战:把逻辑模型落成可靠物理模式

升级路径从真实 v0 数据出发,迁移前拒绝无法无损转成“分”的金额,迁移后逐项验证错误语义、应用角色路径、可重入和安全复位。

章节产物

task.sh all 在已经存在 ch03-v0 时执行:

manifest → migrate → verify → negative → constraint-lab

新环境可用 task.sh install 通过同一条迁移链建立空 v1、加载确定性 v1 样例并执行全部审查。两条路径最终必须得到同一摘要:

status=ok
model_version=ch04-v1
money_unit=CNY-fen
session_timezone=UTC
customer_count=2
product_count=3
order_count=2
item_count=3
payment_count=2
order_transition_count=4
partition_decision=not-now
relation_checksum=f8a7bfae59c6d16cd323abecfefe1014

迁移已在 PostgreSQL 18.4 实测以下路径:首次升级、重复升级跳过、复位后从 v0 重建、空库 fresh install、fresh install 重跑、应用角色生成 identity 与执行状态转换、两位以上金额迁移前拒绝且事务完整回滚。

章节验收

  1. 能解释为什么本案例选择整数“分”,也能说出何时应改用 numeric(p,s)
  2. 能证明 timestamptz 保存瞬间但不保存原始 zone name,并复现 DST 双重 01:30;
  3. 能区分 identity、sequence 与 PK 的责任,迁移后不会产生键碰撞;
  4. 非法状态值、非法转换和缺少伴随时间分别由不同规则拒绝;
  5. 能说明 array、range、JSONB 与拆表的边界,而不是统一套用“灵活”;
  6. 能从 pg_constraint 识别约束类型、是否验证和是否可延迟;
  7. 能复现排他冲突和事务内唯一值交换;
  8. 能列出 v1 新增的隐式/显式索引及写放大;
  9. 分区决定有数据、生命周期和查询证据门,而不是凭行数拍脑袋;
  10. allinstall、拒绝路径与 reset 都有独立证据,最终 checksum 一致;
  11. 明确 v1 尚未关闭的跨表/外部事实,不把本章 DDL夸大为完整电商生产模型。

下一章 ch05《运筹帷幄:查询、事务与锁的核心心智模型》 将在这套可靠类型合同上建立查询执行、并发可见性与锁等待的共同原理地图;ch10 与 ch13 再处理并发状态转换和跨表数据库逻辑。

参考资料


上一章:正本清源:从业务规则到关系模型 · 返回上卷导读 · 下一章:运筹帷幄:查询、事务与锁的核心心智模型 · 查看全书目录 · 查看索引中心

最后更新于