目录
1. 什么是事务?
事务(Transaction)指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
在关系数据库中,一个事务可以是一条 SQL 语句,一组 SQL 语句或整个程序。它将多个步骤捆绑成了一个单一的、要么全完成要么全不完成的操作。步骤之间的中间状态对于其他并发事务是不可见的,如果产生错误导致事务不能完成,则其中任何一个步骤都不会对数据库造成实质影响。
2. 事务的属性-ACID
- 原子性(Atomicity)--事务的原子性强调了一个事务是一个逻辑工作单元,是一个整体,是不可分割的。一个事务所包含的操作要么全部做,要不全部不做。
- 一致性(Consistency)-一个事务执行一项数据库操作,事务使数据库从一种一致性的状态变换成另一种一致性状态。
- 隔离性(Isolation)-在事务未提交前,它操作的数据,对其他用户不可见。
- 持久性(Durability)-一旦事务成功完成,该事务对数据库所施加的所有更新都是永久的。
3. 数据库事务的操作方式
3.1. SET TRANSACTION
设置当前事务的特性
SET TRANSACTION 命令设置当前会话的事务特性,可用的事务特性包括事务隔离级别、事务访问模式(读/写或只读)以及可延迟模式三类选项。
- 事务隔离级别是指 一个事务的隔离级别决定当其他事务并行运行时该事务能看见什么数据,包括如下几个隔离级别:
READ COMMITTED 一个语句只能看到在它开始前提交的行。这是默认值。
REPEATABLE READ 当前事务的所有语句只能看到这个事务中执行的第一个查询或者数据修改语句之前提交的行。
SERIALIZABLE 当前事务的所有语句只能看到这个事务中执行的第一个查询或者数据修改语句之前提交的行。如果并发的可序列化事务间的读写模式可能导致一种那些事务串行(一次一个)执行时不可能出现的情况,其中之一将会被回滚并且得到一个serialization_failure 错误。
SQL 标准定义了一种额外的级别:READ UNCOMMITTED。在 KingbaseES 中 READ UNCOMMITTED 被视作 READ COMMITTED。
一个事务执行了第一个查询或者数据修改语句(SELECT、INSERT、DELETE、UPDATE、FETCH 或 COPY)之后就无法更改事务隔离级别。
- 事务的访问模式决定该事务是否为读/写或者只读。读/写是默认值。
- 可延迟模式 只有事务也是 SERIALIZABLE 以及 READ ONLY 时,DEFERRABLE 事务属性才会有效。
3.2. BEGIN
BEGIN —开始一个事务块。
也就是说所有 BEGIN 命令之后的所有语句将被在一个事务中执行,直到给出一个显式的COMMIT 或者ROLLBACK 。默认情况下(没有 BEGIN),KingbaseES 在“自动提交”模式中执行事务,也就是说每个语句都在自己的事务中执行并且在语句结束时隐式地执行一次提交(如果执行成功,否则会完成一次回滚)。
BEGIN也可以指定隔离级别、读/写模式或者延迟模式,其含义与SET TRANSACTION 相同。
3.3. COMMIT
COMMIT —提交当前事务。所有由该事务所作的更改会变得对他人可见并且被保证在崩溃发生时仍 能持久。
3.4. ROLLBACK
ROLLBACK —中止当前事务。
ROLLBACK 回滚当前事务并且导致该事务所作的所有更新都被抛弃。
3.5. SAVEPOINT
SAVEPOINT —在当前事务中定义一个新的保存点
保存点是事务内的一种特殊标记,它允许所有在它被建立之后执行的命令被回滚,把该事务的状态恢复到它处于保存点时的样子。
3.6. ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT —回滚到一个保存点
回滚在该保存点被建立之后执行的所有命令。
ROLLBACK TO SAVEPOINT 隐式地销毁在所提及的保存点之后建立的所有保存点。
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 的支行的存款,或者反之。
所以事务的全做或全不做并不只体现在它们对数据库的持久影响,也体现在它们发生
时的可见性。一个事务所做的更新在它完成之前对于其他事务是不可见的,而之后所有的更新将同时变得可见。
在 KingbaseES 中,使用 SQL 命令 BEGIN 和 COMMIT 开启一个事务:
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
...
COMMIT;
在事务执行中若不想提交(若 Alice 的余额不足),可以以 ROLLBACK 命令代替 COMMIT 命令, 当前的更新即被取消。
可以利用保存点来以更细的粒度控制事务中的语句。保存点允许有选择性地放弃事务的一部分而提交剩下的部分。
例如:在银行数据库中,假设从 Alice 的账户扣款 100 美元,然后存款到 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 是重新控制一个由于错误被系统置为中断状态的事务块的唯一的途径,而不是完全回滚它并重新启动。
5. 自治事务
包含自治事务的匿名块、存储过程和函数称之为自治程序。为便于说明,将调用自治程序的事务称之为主事务。自治事务是独立于主事务的事务,本质上也是事务。它以匿名块、存储过程和函数为载体,自治程序被执行时,它所包含的所有事务就称之为自治事务。
5.1. 自治事务有如下重要的特性
5.1.1. 事务的独立性
主事务和自治事务相互独立,各自的 TCL 操作互不影响,即主、自治事务的 commit/rollback 互不干涉。
如下示例:匿名块中的 rollback 不会回滚 proc1 中的 t2 语句,proc1 中的 commit 也不会提交主事务的 t1 语句:
begin
insert into test1 values (1); --t0
commit;
insert into test1 values (2); --t1
proc1('this is my error message');
rollback;
end;
/
5.1.2. 数据的可见性
a) 主事务或其他事务仅已提交的更改对自治事务可见
b) 自治事务中已提交的更改对主事务和其他事务可见
用 1 中的例子加以说明,主事务的 t1 语句未提交,对于 proc1 不可见,t0 语句已提交, 对 proc1 可见;同理,自治事务的 t2 语句被提交后对主事务可见。
5.1.3. 数据的关联性
a) 自治程序可接受主事务传递的参数
b) 自治程序可返回结果集
c) 自治事务异常可以被主事务捕获
d) 自治程序的打印等信息可直接被主事务接收,使用透明
e) 自治程序的 OUT 参数对主事务同样生效
5.2. 自治事务示例
包中存储过程、并传递参数
create table accounts(account_id int, balance float);
insert into accounts values (7715, 5000.5);
CREATE OR REPLACE PACKAGE pac1 AS
procedure f1(id int, n float);
END;
/
CREATE OR REPLACE PACKAGE body pac1 AS
procedure f1(id int, n float) as
pragma autonomous_transaction;
begin
insert into accounts values (id, n);
update accounts set balance = balance - 100 where account_id =id;
commit;
end;
END;
/
BEGIN
call pac1.f1(7720, 5680);
rollback;
END;
/
关于事务的具体详细的使用细节请参考官方产品用户文档: