“影子分页为逻辑页号维护到物理页的映射。开始更新型 数据库事务 时,稳定的 shadow page table $S$ 继续指向最近提交状态;当前表 $C$ 初始共享这些映射。修改逻辑页 $i…”
形式陈述 ​
数据库事务 begin 开始,内部产生读取
定义域刻画业务前置与完整性约束;提交表示系统接受本次变换,中止则表示本次尝试不应作为已完成效果留给之后的事务。这个抽象没有规定内部必须只有一条语句,也没有要求事务独占数据库。
事务边界把“程序已经算出结果”和“数据库承诺结果”分开。写操作可以先进入私有工作区、缓冲页或日志,只有提交协议完成后才成为该事务的成功结果。相反,应用收到普通查询结果并不表示事务已经提交;连接断开、约束失败或显式回滚仍可令它中止。
良构执行只有一个终止决定,并且在
事务本身只是工作单位。ACID 事务性质说明希望这个单位具备哪些保证,调度与并发控制决定多个事务怎样交错,恢复算法决定崩溃后怎样兑现提交或中止。把这些机制塞进“事务”一词,会让接口无法说明自己究竟承诺了什么。
直觉
一次银行转账在业务上是一件事,在存储层却至少包含扣款、加款和若干检查。事务像给这些步骤套上一个共同封套:外部不应只收到半张账单。封套内部可以有索引更新、触发器或多页修改,封套边界仍只有一次成功或失败决定。
这种分组也给并发推理提供名字。系统可以说“事务
例子与边界
账户初值为
begin
read A = 500
write A = 420
read B = 300
write B = 380
commit
其业务变换保持
事务不保证程序逻辑正确。把 write B = 280 写错仍可原子提交,却破坏资金守恒;完整性约束或应用证明必须另行排除。事务也不等于分布式原子提交:涉及两个独立资源管理器时,两阶段提交可以协调共同决定,但它不替本地事务选择隔离级别。
长事务还有工程边界。它持锁或固定旧快照的时间更久,可能扩大冲突、版本保留与恢复成本;把整天的业务流程包进一个数据库事务,通常也无法跨越人工审批和不可撤销的外部副作用。此时需重新划分边界或使用补偿工作流,而不是假设数据库能撤销已发出的邮件。
另一个常见边界发生在 commit 响应丢失时:服务器可能已经提交,只是客户端没有收到确认。客户端不能把超时直接解释为 abort 并无条件重放,否则可能重复扣款。可靠 API 通常让请求携带唯一业务键,或提供查询最终事务结果的接口;这是处理“结果未知”,不是改变事务原子性。
推论与应用
明确事务边界后,可以把一次执行投影成事务调度,检查读取来源、提交次序与串行等价;也可以在崩溃模型下定义哪些提交必须恢复。并发控制和恢复分别约束同一组事务事件,因此两边使用的事务标识、begin/commit/abort 状态必须一致。
数据库驱动常提供自动提交模式:每条语句隐式构成一个事务。这是边界选择,不是“没有事务”。显式事务则把多条语句纳入同一决定。审查 API 时应确认异常是否自动 abort、重试是否创建新事务标识,以及客户端收到 commit 成功前后分别能作何推断。
测试事务接口还应覆盖重复 begin、事务内语句错误、savepoint 回滚、commit 超时和连接池复用。若错误后的连接仍带着旧事务上下文,下一位调用者可能意外继承锁或未提交写;边界管理错误会破坏上层所有调度分析。
参考资料
- Philip A. Bernstein, Vassos Hadzilacos, and Nathan Goodman, Concurrency Control and Recovery in Database Systems, Addison-Wesley, 1987, Chs. 1–2。
- Jim Gray and Andreas Reuter, Transaction Processing: Concepts and Techniques, Morgan Kaufmann, 1992, Chs. 1–4。
- Theo Härder and Andreas Reuter, “Principles of Transaction-Oriented Database Recovery,” ACM Computing Surveys 15(4), 1983, pp. 287–317。