时间:2022-07-20 10:33:53 | 栏目:Oracle | 点击:次
因为Oracle中支持多个事务并发执行,所以会出现下面的数据异常。
事务中遇到的这些异常与事务的隔离性设置有关,事务的隔离性设置越多,异常就出现的越少,但并发效果就越低,事务的隔离性设置越少,异常出现的越多,并发效果越高。
针对读取数据时可能产生的不一致现象,在SQL92标准中定义了4个事务的隔离级别:
Oracle默认的隔离级别是read committed。
Oracle支持SQL92标准的READ_COMMITED、SERIALIZABLE,自身特有的Read only和Read write隔离级别。
设置一个事务的隔离级别:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; SET TRANSACTION READ ONLY; SET TRANSACTION READ WRITE;
注意:这些语句是互斥的,不能同时设置两个或两个以上的选项。
设置单个会话的隔离级别:
ALTER SESSION SET TRANSACTION ISOLATION LEVEL READ COMMITTED; ALTER SESSION SET TRANSACTION ISOLATION SERIALIZABLE;
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。
为了保证性能:乐观锁,悲观锁
在Oracle中最主要的锁是DML锁(data locks,数据锁),DML锁的目的在于保证并发情况下的数据完整性。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。
锁有“共享锁”、“排它锁”,“共享排它锁”等多种类型,而且每种类型又有“行级锁” (一次锁住一条记录),“页级锁” (一次锁住一页,即数据库中存储记录的最小可分配单元),“表级锁” (锁住整个表)。
(1)共享锁(S锁)
添加该S锁。在该锁定模式下,不允许任何用户更新表。但是允许其他用户发出select …from for update命令对表添加RS锁。
lock table in share mode
(2)排他锁(X锁)
添加X锁。在该锁定模式下,其他用户不能对表进行任何的DML和DDL操作,该表上只能进行查询。
lock table in exclusive mode
(3)行级共享锁(RS锁)
通常是通过语句添加的,同时该方法也是我们用来手工锁定某些记录的主要方法。比如,当我们在查询某些记录的过程中,不希望其他用户对查询的记录进行更新操作,则可以发出这样的语句。当数据使用完毕以后,直接发出rollback命令将锁定解除。当表上添加了RS锁定以后,不允许其他事务对相同的表添加排他锁,但是允许其他的事务通过DML语句或lock命令锁定相同表里的其他数据行。
select … from for update
(4)行级排他锁(RX锁)
当进行DML操作时会自动在被更新的表上添加RX锁,或者也可以通过执行lock命令显式的在表上添加RX锁。在该锁定模式下,允许其他的事务通过DML语句修改相同表里的其他数据行,或通过lock命令对相同表添加RX锁定,但是不允许其他事务对相同的表添加排他锁(X锁)。
(5)共享行级排他锁(SRX锁)
添加SRX锁。该锁定模式比行级排他锁和共享锁的级别都要高,这时不能对相同的表进行DML操作,也不能添加共享锁。
lock table in share row exclusive mode
上述几种锁模式中,RS锁是限制最少的锁,X锁是限制最多的锁。当程序对所做的修改进行提交(Commit)或回滚(Rollback)后,锁住的资源便会得到释放,从而允许其他用户进行操作。如果两个事务,分别锁定一部分数据,而都在等待对方释放锁才能完成事务操作,这种情况下就会发生死锁。
下图列出产生锁定模式的SQL语句:
一条或者多条DML,[一条DDL]和一条DCL。
在执行使用COMMIT
语句可以提交事务,当执行了COMMIT语句后,会确认事务的变化,结束事务,删除保存点,释放锁。当使用COMMIT语句结束事务之后,其他会话将可以查看到事务变化后的新数据。
保存点(savepoint):是事务中的一点,用于取消部分事务,当结束事务时,会自动的删除该事务所定义的所有保存点。当执行ROLLBACK时,通过指定保存点可以回退到指定的点。
设置保存点:
Savepoint a;
删除保存点:
Release Savepoint a;
回滚部分事务:
Rollback To a;
回滚全部事务:
Rollback;
银行转帐的例子是最经典的事务示例:
-- 从账户一向账户二转账 DECLARE v_money NUMBER(8, 2); -- 转账金额 v_balance account.balance%TYPE; -- 账户余额 BEGIN v_money := &转账金额; -- 输入转账金额 -- 从账户一减钱 UPDATE account SET balance = balance - v_money WHERE id = &转出账户 RETURNING balance INTO v_balance; IF SQL%notfound THEN raise_application_error(-20001, '没有该账户:' || &转出账户); END IF; IF v_balance < 0 THEN raise_application_error(-20002, '账户余额不足'); END IF; -- 向账户二加钱 UPDATE account SET balance = balance + v_money WHERE id = &转入账户; IF SQL%notfound THEN raise_application_error(-20001, '没有该账户:' || &转入账户); END IF; -- 如果没有异常,则提交事务 COMMIT; dbms_output.put_line('转账成功'); EXCEPTION WHEN OTHERS THEN ROLLBACK; -- 出现异常则回滚事务 dbms_output.put_line('转账失败:'); dbms_output.put_line(sqlerrm); END;