PostgreSQL之事务

作者:瀚高PG实验室(Highgo PG Lab) 丹心明月

 

对于所有的数据库系统来说,事务是基石。它将多个步骤绑定为一个操作,要么都执行,要么都不执行。步骤之间的中间状态对于其他事务不可见,而且如果在事务执行过程中失败,那么会回滚所有步骤。

 

例如,考虑一个银行数据库,其中包含各种客户帐户的余额以及分支机构的总存款余额。假设我们想要记录从Alice账户支付$100.00给Bob账户的操作。简化点的步骤中,所执行的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');

在这里,命令的具体细节并不是重点,重点是,我们需要执行一系列的update操作去实现我们的目的。银行人员需要确认,这些update要么全部都执行了,要么全部都不执行。不能Bob收到钱了,却没有扣Alice的钱。我们需要能够保证在执行update过程中,万一出现问题,这些update都不要执行。而将这些update操作绑定到一个事务中提供了这个保证。此即为事务中的原子性:对于其他事务来说,事务的执行要么全部成功,要么都不执行。

 

当然也需要保证,在事务执行完成后,可以在数据库中永久的留存,即使在事务执行后发生了宕机,也不会丢掉事务执行的结果。例如,我们不希望,因为系统宕机,导致Bob的取款行为未做记录。而事务型数据库保证了事务在返回成功之后,事务所做的所有更改都已经永久的保存了下来(例如,存储到了磁盘上)。

 

事务型数据库另一个重要的特性,也与原子性有关:当多事务并行运行时,互相之间无法看到其他事务所做的未完成的更改。例如,如果一个事务在统计所有账户余额,那么在这期间,对于Alice或者Bob账户的余额变化就不应统计在内。所以,事务的全部成功或根本不执行,不仅仅体现在留存的永久性上,也应体现在对其他事务的可见性上。一个事务的更改,只有在全部完成后(即事务完成),才可以对其他事务可见。

 

PostgreSQL中,通过在SQL命令中使用BEGIN和COMMIT命令设置事务。所以上例中的银行事务应该如下:

BEGIN;

UPDATE accounts SET balance = balance - 100.00

WHERE name = 'Alice';

-- etc etc

COMMIT;

如果,在事务执行过程中我们又不想提交了(或许是因为我们刚刚知道Alice的余额不足了),那么我们可以使用ROLLBACK来回滚操作,这样所有的update就会被取消。

 

实际上,PostgreSQL视每个SQL语句以事务方式执行。如果你未使用BEGIN命令,那么每个语句会有一个隐式的BEGIN和(如果执行成功)COMMIT。使用BEGIN开头和COMMIT结尾的语句,通常称为事务块。

 

注:一些客户端会自动发出BEGIN和COMMIT命令,以使你默认获得事务块的效果。可通过查看你在用客户端的文档以确认。

 

可以通过使用保存点(savepoints)以更细的粒度控制事务中的语句执行。保存点(savepoints)使得可以提交事务中的一部分语句,而忽略另一部分语句。在使用SAVEPOINT命令定义保存点之后,可以使用ROLLBACK TO命令回滚到该保存点。介于保存点与回滚命令之间的语句将会忽略,而在保存点之前的变更会保留。

 

设置的保存点,可以多次回滚。当然,如果你确认不需要某个保存点了,也可以删除该保存点(使用release savepoint savepoint_name;),以释放系统资源。但要注意,释放或者回滚到某一保存点,那么该保存点之后定义的保存点将同时被自动释放。

 

这些都发生在事务块中,所以对其他会话不可见。如果提交事务,则对所有会话可见;如果回滚了事务,则变更不可见。

 

回到之前的银行数据库,假设我们从Alice的账户中扣除$100.00到Bob账户中,之后却发现其实应该将钱转到Wally账户中。我们可以通过使用保存点实现:

BEGIN;

UPDATE accounts SET balance = balance - 100.00

WHERE name = 'Alice';

SAVEPOINT my_savepoint;

UPDATE accounts SET balance = balance + 100.00

WHERE name = 'Bob';

-- oops ... forget that and use Wally's account

ROLLBACK TO my_savepoint;

UPDATE accounts SET balance = balance + 100.00

WHERE name = 'Wally';

COMMIT;

虽然这个示例比较简单,但是通过对保存点的使用,可以实现更多复杂的事务控制。此外,rollback to是唯一可以将因为错误而被系统置于中止状态的事务重新置于控制之下的方式,而不用将事务全部回退或重新开始。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值