首页 > 代码库 > 事务处理和并发控制
事务处理和并发控制
什么是事务
是一种机制、一个操作序列、它包含了一组数据库操作命令,并且所有的命令作为一个整体,一起向系统提交或撤销操作请求。由一个或多个完成一种相关行为的SQL语句组成。是一个不可分割的工作逻辑单元。
在事务处理中,一旦某个操作发生异常,则整个事务都会重新开始,数据库也会返回到事务开始之前的状态,在事务中对数据库所做的一切操作都会取消。事务要是成功的话,事务中所有的操作都会执行。
事务控制语句:COMMIT:提交事务,即把事务中对数据库的修改进行永久保存。
ROLLBACK:回滚事务,即取消对数据库所做的任何修改。
事务的特性:1、原子性(Atomicity):事务是一个完整的操作。事务的各步操作是不可分的(原子的),要么都执行,要么都不执行。2、一致性(Consistency):在事务操作前后,数据必须处于一致状态。3、隔离性(Isolation):对数据进行修改的所有并发事务彼此隔离的,这表明事务必须是独立的,他不应该以任何方式依赖于或影响其他事务。4、持久性(Durability):事务完成后,他对数据的修改被永久保持。
示例:ACCOUNT_BALANCE表示用户余额ACCOUTN_ID表示用户id。
BEGIN UPDATE ACCOUNT SETACCOUNT_BALANCE=ACCOUNT_BALANCE-4000; WHERE ACCOUNT_ID=’1001’; UPDATE ACCOUNT SETACCOUNT_BALANCE=ACCOUNT_BALANCE+4000; WHERE ACCOUNT_ID=’1002’; COMMIT; EXCEPTION WHERE OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE(‘转账异常,停止转账!’); END;
事务一致性要求:在事务处理开始之前,数据库的所有数据都满足业务规则约束;当事务处理结束后,数据库中的所有数据仍然满足业务规则约束。
示例:
DECLARE account_a ACCOUNT.ACCOUNT_BALANCE%TYPE; account_b ACCOUNT.ACCOUNT_BALANCE%TYPE; BEGIN SELECT ACCOUNT_BALANCE INTO account_aFROM ACCOUNT WHERE ACCOUNT_ID=’1001’; SELECT ACCOUNT_BALANCE INTO account_bFROM ACCOUNT WHERE ACCOUNT_ID=’1002’; dbms_output.put_line(‘转账前A金额:’); dbms_output.put_line(account_a); dbms_output.put_line(‘转账前B的金额:’); dbms_output.put_line(account_b); dbms_output.put_line(‘转账前总金额:’); dbms_output.put_line(account_a+account_b); UPDATE ACCOUNT SETACCOUNT_BALANCE=ACCOUNT_BALANCE-2000 WHERE ACCOUNT_ID=’1001’; UPDATE ACCOUNT SETACCOUNT_BALANCE=ACCOUNT_BALANCE+2000 WHERE ACCOUNT_ID=’1002’; COMMIT; dbms_output.put_line(‘成功转账!’); SELECT ACCOUNT_BALANCE INTO account_aFROM ACCOUNT WHERE ACCOUNT_ID=’1001’; SELECT ACCOUNT_BALANCE INTO account_bFROM ACCOUNT WHERE ACCOUNT_ID=’1002’; dbms_output.put_line(‘转账后A金额:’); dbms_output.put_line(account_a); dbms_output.put_line(‘转账后B金额:’); dbms_output.put_line(account_b); dbms_output.put_line(‘转账后总金额:’); dbms_output.put_line(account_a+account_b); EXCEPTION WHEN OTHERS THEN ROLLBACK; dbms_output.put_line(‘转账异常,停止转账!’); SELECT ACCOUNT_BALANCE INTO account_aFROM ACCOUNT WHERE ACCOUNT_ID=’1001’; SELECT ACCOUNT_BALANCE INTO account_bFROM ACCOUNT WHERE ACCOUNT_ID=’1002’; dbms_output.put_line(‘停止转账后A金额:’); dbms_output.put_line(account_a); dbms_output.put_line(‘停止转账后B金额:’); dbms_output.put_line(account_b); dbms_output.put_line(‘停止转账后总金额:’); dbms_output.put_line(account_a+account_b); END;
读取异常的情况:1、脏读:一个事务读取了另一个事务未提交的数据。2、不可重复读,一个事务在次读取之前曾读取过的数据时,发现该数据已经被另一个已提交的事务修改。3、幻读:一个事务根据相同的查询条件,重新执行查询,返回的记录中包含与前一次执行查询返回的记录不同的行。
ANSISQL-92标准中定义的事务隔离级别:
ReadUncommitted最低等的事务隔离,它仅仅保证了读取过程中不会读取到非法数据,
ReadCommitted,此级别的书屋隔离保证了一个事务不会读到另一个并行事务已修改但未提交的数据,也就是说此级别的事务级别避免了“脏读”。
RepeatableRead,此级别的事务隔离避免了“脏读”和“不可重复读”异常现象的出现。这也意味着,一个事务不可能更新已经由另一个事务读取但未提交的数据。可能引发幻读。
Serializable,最高等级的隔离级别,提供了最高等级的隔离机制,三种异常情况都能避免。
隔离等级 | 脏读 | 不可重复读 | 幻读 |
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committde | 不可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 |
Oracle的事务隔离级别:1、ReadCommitted。2、Serializable;
3、Read Only是Serialzable的子集,但事务中不能有任何修改数据库中数据的语句(DML),以及修改数据库结构的语句(DDL);
Oracle中不需要专门的语句来开始事务。隐含的,事务会在修改数据的第一条鱼具处开始。
结束事务:1、COMMIT语句显式终止一个事务。当执行COMMIT语句时,在事务中对数据的修改都会保存到数据库中。2、ROLLBACK语句回滚事务,当执行ROLLBACK语句时,将取消在事务中对数据库所做的任何修改。3、执行一条DDL语句,如果DDL语句前已经有DML语句,则Oracle会把前面的DML语句作为一个事务提交。4、用户断开与Oracle的链接,用户当前事务将被自动提交。5、用户进程意外被终止,这时用户当前的事务被回滚。
事务控制语句:1、COMMIT:提交事务,即把事务中对数据库的修改进行永久保存。2、ROLLBACK:回滚事务,即取消对数据库所做的任何修改。3、SAVEPOINT:在事务中创建存储点。4、ROLLBACKTO<SAVEPOINT_NAME>:将事务回滚到存储点。5、SET TRANSACTION:设置事务的属性。
SAVEPOINT:在事务中创建存储点。语法:SAVEPOINT[SavePoint_Name];
ROLLBACKTO<SavePoint_Name>:将事务回滚到存储点。语法:ROLLBACK TO[SavePoint_Name];
示例:
BEGIN UPDATE ACCOUNT SETACCOUNT_BALANCE=ACCOUNT_BALANCE+2000 WHEN ACCOUNT_ID=’1001’; SAVEPOINT Add_Account_A; UPDATE ACCOUNT SET ACCOUNT_BALANCE= ACCOUNT_BALANCE-6000 WHEN ACCOUNT_ID=’1001’; UPDATE ACCOUNT SETACCOUNT_BALANCE=ACCOUNT_BALANCE+6000 WHEN ACCOUNT_ID=’1002’; COMMIT; EXCEPTION WHENOTHERS THEN dbms_output.put_line(‘转账异常!’); ROLLBACK TO Add_Account_A; END;
SETTRANSACTION语句必须是事务的第一条语句,他可以指定事务的隔离级别、规定回滚事务时所使用的存储空间、对事务命名。
设置访问级别的方法:SET TRANSACTION READ ONLY; SET TRANSACTION ISOLATION LEVEL READCOMMITED; SET TRANASCTION ISOLATION LEVEL SERIALIZABLE;
并发控制:指用正确的方式实现事务的并发操作,避免造成数据的不一致。
并发控制带来的问题:1、丢失更新,一个事务修改某行数据时,另一个事务同时修改了该行数据,使第一个事务对数据的修改丢失。2、脏读。3、不可重复读。4、幻读。
锁的基本概念:锁,用来共享资源控制并发访问的一种机制。锁由Oracle自动管理,锁持续的时间等于被提交事务处理的时间。
锁的类型:1、共享锁(使用共享锁的数据对象可以被其他事务读取,但不能修改),也称s锁。2、排他锁,也称x锁。按锁保护的内容分类:DML锁,用来保护数据的完整性和一致性;DDL锁,用来保护数据对象结构定义;内部锁和闩,用来保护数据库内部数据结构。
死锁:两个事务(会话)都进入了彼此等候对方锁定的资源时的一种停止状态。
解决死锁:“牺牲”一个会话,回滚一个会话事务,使另一个会话的事务继续执行。
在发生死锁时Oracle数据库会在服务器上创建一个跟踪文件记录死锁。
注意:不要在开发过程中人为的提供条件使Oracle产生死锁。
阻塞:如果一个会话持有某个资源的锁,而另一个会话在请求这个资源就造成了阻塞。
锁机制问题:1、悲观锁,是指在读取数据后马上锁定相关资源。语法:SELECT…………FORUPDATE[OF column_list][WAIT n|NOWAIT] OF子句用于指定即将更新的列,即锁定行上的特定列;WAIT子句指定等待其他用户释放的秒数,防止无限期的等待,NOWAIT表示不等待。
示例:
SELECT * FROM ACCOUNT WHEREACCOUNT .ID=’1001’ FOR UPDATE; UPDATE ACCOUNT SETBALANCE=BALANCE-500 WHERE ID=’1001’;
乐观锁:把所有锁定都延迟到即将执行更新之前。
语法:
UPDATE Table_Name SETColumn_Name1=NewValue1,Column_Name2=NewValue2…… WHERE Column_Name1=OldValue1 ANDColumn_Name2=OldValue2……
示例:
DECLARE account_a ACCOUNT.BALANCE%TYPE; BEGIN SELECT balance INTO account_a FROMACCOUNT WHERE ID=’1001’; UPDATE ACCOUNT SET balance=balance-500WHERE ID=’1001’ AND BALANCE=account_a; END;
锁的分类:DML锁用于确保一次只有一个用户能修改一行,而且正在处理一个表时,别人也不能删除这张表。
DML锁主要包括TX锁、TM锁,其中TX锁是事务锁或行级锁,TM锁成为表级锁。
TX锁:事务发起第一个修改数据的语句时会自动得到TX锁,而且会一直持有这个锁,知道事务提交或回滚。TX锁用作一种排队机制,使得其他会话可以等待这个事务执行。事务中修改或通过悲观锁定选择的每一行都会指向该事务的一个相关TX锁。
TM锁:用于确保在修改表的内容是,表的结构不会改变当一个会话开始更新一个表时,会自动获得这个表的TM锁,这样能够防止,另外一个在该表上执行DROP或者ALTER语句删除该表或更改该表的结构。
DDL锁:用来保护数据对象结构定义,DDL操作会自动为数据库对象加DDL锁。
注意:Oracle中,DDL语句包装在隐式提交(回滚)中来执行操作。