在事务定义中,COMMIT操作和ROLLBACK操作的作用是什么?
A 答案如下所示:
COMMIT即提交,表示这个事务的所有操作都执行成功,COMMIT告诉系统,数据库要进入一个新的正确状态,该事务对数据库的所有更新都要确保不因数据库的宕机而丢失。ROLLBACK即回退或回滚,表示事务中有执行失败的操作,这些操作必须被撤销,ROLLBACK告诉系统,已发生错误,数据库可能处在不正确的状态,该事务对数据库的部分或所有更新必须被撤销。
在Oracle数据库中,COMMIT和ROLLBACK都属于事务控制语言(Transactional Control Language,TCL),TCL用于维护数据的一致性,包括COMMIT、ROLLBACK、SAVEPOINT、ROLLBACK TO SAVEPOINT、SET TRANSACTION、SET CONSTRAINT等语句。其中,COMMIT语句用于确认和提交已经进行的数据库改变;ROLLBACK用于撤销已经进行的数据库改变;SAVEPOINT语句则用于设置保存点,以取消部分数据库改变,ROLLBACK命令会结束一个事务,但ROLLBACK TO SAVEPOINT不会;SET TRANSACTION设定一个事务的属性;SET CONSTRAINT指定是在每个DML语句之后、还是在事务提交后,执行可延迟完整性约束检查。
对于保存点(SAVEPOINT),若有如下一段程序,则最终表EMPLOYEE中的数据如何呢?
CREATE TABLE EMPLOYEE(FIRST_NAME VARCHAR2(20),LAST_NAME VARCHAR2(25),SALARY NUMBER(8,2));
BEGIN
INSERT INTO EMPLOYEE(SALARY,LAST_NAME,FIRST_NAME) VALUES(35000,'WANG','FRED');
SAVEPOINT SAVE_A;
INSERT INTO EMPLOYEE(SALARY,LAST_NAME,FIRST_NAME) VALUES(40000,'WOO','DAVID');
SAVEPOINT SAVE_B;
INSERT INTO EMPLOYEE(SALARY,LAST_NAME,FIRST_NAME) VALUES(50000,'LDD','FRIK');
SAVEPOINT SAVE_C;
INSERT INTO EMPLOYEE(SALARY,LAST_NAME,FIRST_NAME) VALUES(45000,'LHR','DAVID');
INSERT INTO EMPLOYEE(SALARY,LAST_NAME,FIRST_NAME) VALUES(25000,'LEE','BERT');
ROLLBACK TO SAVEPOINT SAVE_C;
INSERT INTO EMPLOYEE(SALARY,LAST_NAME,FIRST_NAME) VALUES(32000,'CHUNG','MIKE');
ROLLBACK TO SAVEPOINT SAVE_B;
COMMIT;
END;
保存点(SAVEPOINT)是事务处理过程中的一个标志,与回滚命令(ROLLBACK)结合使用。其主要用途是允许用户将某一段处理进行回滚而不必回滚整个事务,以上程序的处理过程为:
1)执行SAVEPOINT SAVE_A的时候创建了一个保存点SAVE_A;
2)执行SAVEPOINT SAVE_B的时候创建了一个保存点SAVE_B;
3)执行SAVEPOINT SAVE_C的时候创建了一个保存点SAVE_C;
4)在执行ROLLBACK TO SAVEPOINT SAVE_C后,SAVEPOINT SAVE_C到当前语句之间所有的操作都被回滚;也就是说回滚到了3)的状态;
5)在执行ROLLBACK TO SAVEPOINT SAVE_B后,SAVEPOINT SAVE_B到当前语句之间所有的操作都被回滚;也就是说回滚到了2)的状态;
6)在执行COMMIT后,只有SAVEPOINT SAVE_B之前的操作会被提交从而永久保存到数据库,所以表EMPLOYEE中的数据只有SALARY为35000和40000这两条数据。
那么,Oracle中的COMMIT操作都做了哪些事情呢?当完成事务操作,发出COMMIT命令之后,随后会收到一个反馈为“Commit complete.”,如下:
lhr@lhrdb> INSERT INTO EMP SELECT * FROM EMP;
14 rows created.
lhr@lhrdb> COMMIT;
Commit complete.
提交完成(Commit complete),这个提示意味着Oracle已经将此时间点之前的该事务产生的Redo日志从Redo Log Buffer写入了联机Redo日志文件(这个动作由后台进程LGWR完成),等这个日志写完成之后,Oracle就可以释放用户去执行其它任务。如果此后发生数据库崩溃,那么Oracle可以从Redo日志文件中恢复这些提交过的数据,从而保证提交成功的数据不会丢失。
最后再来解释一下,在Oracle中,无论事务大小,为什么COMMIT的响应时间都相当“平”(即提交操作所花费时间都非常短)?这是因为,在Oracle数据库中执行COMMIT之前,很多困难的、花费时间的工作都已经做完了。例如,已经完成了以下操作:
l 已经在SGA中生成了Undo块。
l 已经在SGA中生成了已修改数据块。
l 已经在SGA中生成了对于前两项的缓存Redo。
l 取决于前三项的大小,以及这些工作花费的时间,前面的每个数据(或某些数据)可能已经刷新输出到磁盘。
l 已经得到了所需的全部锁。
所以,在执行COMMIT时,余下的工作只是:
l 为事务生成一个SCN。
l 后台进程LGWR将所有余下的缓存Redo日志条目写到磁盘,并把SCN记录到联机Redo日志文件中。这一步就是真正的COMMIT。如果出现了这一步,即已�������,��������经提交,那么事务条目会从V$TRANSACTION中被删除,这说明该事务已经提交完成。
l V$LOCK中记录的会话所持有的锁,这些锁都将被释放,而排队等待这些锁的每一个其它会话都会被唤醒,可以继续完成它们的工作。
l 如果事务修改的某些块还在Buffer Cache中,那么会以一种快速的模式访问并“清理”,即快速块清除(Fast Commit Cleanout)。块清除(Block cleanout)是指清除存储在数据库块首部的与锁相关的信息,其实质是在清除块上的事务信息。
所以,在Oracle中,COMMIT操作可以确保提交成功的数据不丢失,而这个保证正是通过Redo来实现的。由此可以看到日志文件对于Oracle的重要,为了保证日志文件的安全,Oracle建议对Redo日志文件进行镜像。从Oracle 10g开始,如果设置了闪回恢复区(Flash Recovery Area),那么Oracle缺省的就会对日志文件进行镜像。镜像的好处是当某个日志出现问题,另外一个日志仍然可用,可以保证数据不丢失,而且通常镜像存储于不同的硬盘,当某个存储出现故障时,另外的存储可以用于保证镜像日志的安全。需要注意的是,在Oracle中,COMMIT操作可以确保提交成功的数据不丢失,但是并不说明,提交了的数据都已经成功写入了磁盘数据文件中。
& 说明:
有关COMMIT的更多内容可以参考我的BLOG:http://blog.itpub.net/26736162/viewspace-2141922/
http://mp.weixin.qq.com/s/Vm5PqNcDcITkOr9cQg6T7w
About Me:小麦苗● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用
● 作者博客地址:http://blog.itpub.net/26736162/abstract/1/
● 本系列题目来源于作者的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
● QQ:646634621 QQ群:230161599
● 题目解答若有不当之处,还望各位朋友批评指正,共同进步
长按下图识别二维码或微信扫描下图二维码来关注小麦苗的微信公众号:xiaomaimiaolhr,学习最实用的数据库技术。
本文分享自微信公众号 - DB宝(lhrdba)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。