约束、持久化与存储边界
把允许状态写成规则,区分写入、提交、持久化和跨边界效果。
约束说明哪些状态允许存在
数据约束将“不能出现什么状态”写成可以执行的规则。属性类型约束值的表示;非空约束限制缺失;唯一约束限制重复;引用约束限制联系;检查约束限制允许的组合。各规则的检查时机和 NULL 语义由实现决定。
例如一条联系必须引用存在的对象,先在应用里查询再插入还留下并发间隙:另一参与者可能在两步之间删除对象。把引用规则交给有相应保证的存储系统,能够让所有受该约束控制的写入面对同一条规则。跨库、外部服务或不受约束的文件仍在保证范围之外。
约束与输入校验各有位置
输入校验检查一个请求是否可解释、格式是否正确,并提供及时反馈。存储约束在持久化边界维护状态。两处可以检查相同条件,因为请求通过后还可能遇到并发变化;错误返回应明确是非法输入、冲突还是系统失败。
不要把检查表达式写成含糊的“没有错误就通过”。SQL 三值逻辑里 NULL 可以代表未知,某些检查在结果未知时允许记录进入。需要禁止缺失时必须另外说明;规则怎样处理未知值会影响允许状态。PostgreSQL 的约束说明分别定义了 CHECK、非空和唯一行为。
写入经过哪些完成点
应用计算出数据、存储服务接受请求、事务成功提交、日志或数据达到所要求的持久介质、其他副本可读,是不同完成点。接口必须说明返回成功时已经到达哪一个点,调用者才能决定是否重试、通知用户或发起后续工作。
持久化通常需要将变更写到掉电后仍能保留的介质,并使用日志或等价机制恢复完整状态。写入操作系统缓存与介质确认之间可能存在差距;同步策略、硬件和恢复实现共同决定保证。备份还需要覆盖介质丢失和误操作,事务日志本身不等于所有恢复需求已经满足。
一个事务边界之外的效果
事务可以把受同一事务管理的多项操作组成一个提交单元。它无法直接撤销已经发送的邮件、已经调用的外部接口或另一个独立数据库的提交。需要跨边界协作时,先定义每个效果的完成点、可重复处理方式和恢复责任。
常见安排是把业务记录和待发送事件一起提交,再由独立执行者发送。发送结果仍可能不确定,接收者需要身份键、重复判定和状态查询等契约。这个安排将“应当发送”的事实可靠保存,实际交付保证仍取决于后续流程。
存储结构怎样演进
数据模型变化需要考虑已经保存的记录和同时运行的旧程序。新增字段可以先允许缺失、填充旧记录,再收紧约束;删除字段需要确认消费者已退出。长时间迁移还要考虑锁、写入竞争、失败恢复和进度位置。
版本号只是变化的标识。真正决定兼容的是每个消费者如何解释字段、未知值和操作。无法在一个原子步骤完成的迁移应声明阶段状态,并让每个阶段保持所需性质。
来源与检查范围
事务原子性与提交的实现例子见 PostgreSQL 官方 Transactions;恢复机制见 Reliability and the Write-Ahead Log。本文解释完成点及保证边界,没有执行掉电、磁盘损坏或跨系统交付实验。
最后更新于