事务的主要目的,把数据库从一个一致的状态转移到另一个状态
ACID
- 原子性,要么完全发生,要么不发生
- 一致性,从一个一致的状态一致转变到另一个状态
- 隔离性,事务提交前,其他事物察觉不到事务的影响
- 持久性,一旦事务提交,是永久的
事务控制语句
oracle中没有"开始事务"语句,隐式开始于第一条修改数据的语句(第一条得到TX的语句)
发出COMMIT或者ROLLBACK显式结束四五
- COMMIT
- COMMIT WORK
二者是等效的,分布式事务中,对commit语句有扩充,允许注释标记commit,强制提交有异议的分布式事务
- ROLLBACK,等价于ROLLBACk WORK,回滚结束事务,撤销任何没有提交的操作,通过读取存储在回滚段中的信息实现的,把数据库块恢复到事务前
- SAVEPOINT,允许在事务中创建一个"标记点",单个事务可以有多个savepoint
- ROLLBACK TO <SAVEPOINT>,回滚到事务标记点,不回滚标记点前面的工作
如果使用两条update语句,后边跟一个savepoint,然后两条delete,执行时发生错误,事务将回滚到savepoint命名的地方
- SET TRANSACTION,允许设置不同的事务属性,如隔离级别和是否只读或可读写,也可以使用此语句指示事务使用某个特定的回滚段
savepoint在Oracle内部使用最频繁,也能在应用程序中找到使用它的地方
完整性约束和事务
完整性约束检查在整条SQL语句执行完以后立即进行检查,而不是在整个存储过程中执行完后进行
从Oracle8开始,推迟约束检查
处理事务的不良习惯
面对更新多行任务,大多数程序员在循环中使用过程化方法,能逐行提交
- 经常提交一些小事务比执行和提交一个大的要快,效率高
- 没有足够的回滚段空间
SET TRANSACTION语句告诉事务使用巨大的回滚段,最后删掉回滚段,释放空间
JDBC会隐式提交,可以通过下面对连接对象的设置关闭自动提交
conn.setAutoCommit(false)
分布式事务
Oracle可以透明地处理分布式事务,关键是数据库链接
create synonym T for T@another_database;
由数据库链接ANOTHER_DATABASE定义的数据库实例的表T,隐藏了T是远程表的事实
可以像本地表一样访问表T,这样执行一个分布式事务和本地事务没有区别了
update local_table set x = 5; update remote_table@another_database set y=10 commit;
Oracle使用2PC,两阶段提交协议来完成
- 允许修改影响多个原子性提交的数据库,在提交前尽可能尝试关闭分布式失败的窗口
- 在多个数据库之间的2PC中,其中一个数据库会作为分布式事务的协调者,该site可以咨询其他site是否准备好提交
- 其他真点回返回准备状态,有一个站点报告NO,整个事务回滚,都报告YES,协调者回广播信息,永久提交
2PC,试图尽量关闭多的错误窗口,但不能全部关闭,如果协调者广播提交,由于网络故障,自站点挂起,此时无法接收通知
子站点必须保持事务打开,等待站点1通知,RECO进程用来解决这个问题
带有force的commit和rollback也起作用
分布式事务的限制
- 不能通过数据库链接使用COMMIT,只能从发起事务的站点提交
- 不能通过数据库链接执行DDL
- 不能通过数据库链接使用savepoint,即不能通过数据库链接使用任何事务控制语句
通过设置站点的提交点强度(一个init.ora参数),可以改变实际的提交站点
分布式事务中,提交点强度于服务器重要程度正相关
重做和回滚
insert into t(x,y) values (1,1); update t set x = x+1 where x=1; delete from t where x=2;
如果更新后系统出现故障会发什么什么
如果成功提交后会发生什么
如果回滚会发生什么
撤销包含许多信息,例如在X和Y的索引,它们的改变在回滚里未完成,撤销也存储在回滚段
- 回滚段保存在表空间中,由重做日志保护
- 对待回滚数据就像对待表数据或索引数据一样,回滚段的变化产生一些重做,被记录下来
- 撤销数据添加到回滚段,就像其他数据一样,被缓存到缓存区高速缓存中,撤销也包含许多条信息
假设SGA出现故障,其中没有所需的信息,重启时,事务从没有发生,含有修改信息的块没有丢失,重做信息没有丢失
假设缓冲区高速缓存被填满,DBWR必须腾出空间,必须释放刚才修改的块
DBWR要求LGWR刷新保护数据库块的重做块,在DBWR写入要改变硬盘块之前需要释放重做日志缓冲区
当第三个块填满且有提交发生时,每隔3秒就会释放一块
块本身可能在硬盘上结束,也可能未结束
在块缓冲区高速缓存中有更多心的回滚段块,为了撤销更新,可以修改缓存中的数据库表和索引块
也产生了更多的重做日志缓冲区条目,一些在硬盘山,一些在缓存中
故障恢复,系统分两步进行
- 前滚,把系统恢复到出现问题时的状态
- 回滚没有提交的事务
回滚过程中,没有涉及到redo log,只有当恢复和归档时才读取重做日志
Commit,Oracle将重做日志缓冲区刷新到硬盘
修改的块在缓冲区高速缓存中,一些可能刷新到了硬盘
重放该事务所需的全部重做(redo),信息安全地放在硬盘上,永久改变
undo log将被悬挂,知道回滚段反转并且重用这些块