3.4. 事务

事务是所有数据库系统的一个基本概念. 一次事务的要点就是它把多个步骤捆绑成了一个单一的,不成功则成仁的操作. 其它并行的事务是看不到在这些步骤之间的中间状态的,并且如果发生了一些 问题,导致该事务无法完成,那么所有这些步骤都完全不会影响数据库.

比如,假设一个银行的数据库包含各种客户帐户的余额,以及每个分行的 总余额.假设我们要记录一次从 Alice 的帐户到 Bob 的帐户的金额为 $100.00 的支付动作.那么,完成这个任务的简单到极点的 SQL 命令象下面这样

UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');

这些命令的细节在这儿并不重要;重要的是这里牵涉到了好几个独立 的更新来完成这个相当简单的操作.我们的银行官员会希望要么所有这些 更新都生效,要么全部不起作用.我们当然不希望一次系统崩溃就导致 Bob 收到 100 块不是 Alice 支付的钱,也不希望 Alice 老是不花钱从 Bob 那里拿到物品.我们需要保证∶如果在操作的过程中出了差错, 那么所有这些步骤都不会发生效果.把这些更新组合成一个 事务就给予我们这样的保证. 事务被认为是原子的∶从其它事务的角度来看, 它要么是全部发生,要么完全不发生.

我们还需要保证∶一旦一个事务完成并且得到数据库系统的认可, 那么它必须被真正永久地存储,并且不会在随后的崩溃中消失. 比如,如果我们记录到了一个 Bob 撤单的动作,那么我们不希望 仅仅在他走出银行大门之后的一次崩溃就会导致对他的帐户的 扣减动作消失.一个事务型数据库保证一个事务所做的所有更新 在事务发出完成响应之前都记录到永久的存储中(也就是磁盘).

事务型数据库的另外一个重要的性质和原子更新关系密切∶ 当多个事务并行地运行的时候,那么每个事务都不应看到其它事务 所做的未完成的变化.比如,如果一个事务正忙着计算所有分行的 余额总和,那么它不应该包括来自 Alice 的分行的扣帐和来自 Bob 分行的入帐,反之亦然.所以事务必须是黑白分明的,不仅仅体现在它们 在数据库上产生的永久影响出发,而且体现在它们运转时的自身的可视性上. 一个打开的事务做的更新在它完成之前是其它事务无法看到的,而且所有更新 是同时可见的.

PostgreSQL 里,一个事务是通过把SQL 命令用 BEGINCOMMIT 命令包围实现的. 因此我们的银行事务实际上看起来象下面这样

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- 等等
COMMIT;

如果在该事务的过程中,我们决定不做提交(可能是我们刚发现 Alice 的 余额是负数),那么我们可以发出 ROLLBACK 命令而不是 COMMIT 命令,那么到目前为止我们的所有更新都会被取消.

PostgreSQL 实际上把每个 SQL 语句当做在一个事务中 执行的来看待.如果你没有发出 BEGIN 命令,那么每个独立 的语句都有一个隐含的 BEGIN 和(如果成功的话) COMMIT 语句包围在周围.一组包围在 BEGINCOMMIT 语句中间的语句有时候被称做事务块

注意: 一些客户库自动发出 BEGINCOMMIT, 因此你可能不需要特意请求就可以获取事务块的效果.查看你使用的 接口的文档.