在Jonathan Lewis的Oracle Core这本书中提到了一种ACID异常的现象,在这种情况下,事务已经提交,但是redo log buffer还没有写到磁盘。
如果断电,已提交的事务会丢失数据。
实验之前,我们首先来复习一下commit这个操作。
通常情况下,commit是一个非常快的操作,commit的主要操作是更新事务表槽的SCN。这个操作本身也会产生redo,一般来说,这是一个很快的操作。具体细节如下:
1.得到一个SCN值,准备更新事务表的事务表槽。并且在redo log buffer中为这次更新操作创建一条redo 记录
2.将生成的redo记录复制到日志缓冲区。
3.对undo事务表槽,应用这条redo。事务表中的槽位的SCN等信息被更新。
4.将这条commit记录产生的redo log buffer刷新到磁盘上的在线日志文件。
在第3步和第4步中间,其他的会话能看到事务已经改变,尽管redo还没有写到磁盘,如果数据库实例在第3步和第4步中间崩溃,那么有可能这个已经提交的事务没有被恢复。演示如下:
1.会话1:使用mdb或者oradebug将lgwr挂起。
2.会话2:修改一条数据然后提交--会话会挂起。
3.会话3:查询数据,并注意到修改后的数据是可见的。
4.会话3:shutdown abort,数据库重启后,第2步产生的更改丢失。
在Solaris10+Oracle 11.2.0.1环境下测试.
1.挂起lgwr进程。
# ps -ef|grep lgwr
oracle 1063 1 0 13:37:42 ? 0:01 asm_lgwr_+ASM1
oracle 1637 1 0 13:39:33 ? 0:02 ora_lgwr_mydb1
root 22754 22544 0 20:53:21 pts/3 0:00 grep lgwr
# mdb -p 1637
Loading modules: [ ld.so.1 libc.so.1 ]
>
2.修改一条记录并提交。t1表的唯一字段x,初始值为7,将其更新为8.
SQL> select * from t1;
X
----------
7
SQL> update t1 set x=8;
1 row updated.
SQL> commit;
由于LGWR被挂起,这里的commit操作会hang.
3.查询修改的记录,我们可以看到,这条记录已经由7变成了8
SQL> select * from t1;
X
----------
8
4.shutdown abort数据库,然后重新查询这条记录。SQL> shutdown abort;
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 1570009088 bytes
Fixed Size 2211368 bytes
Variable Size 469762520 bytes
Database Buffers 1090519040 bytes
Redo Buffers 7516160 bytes
Database mounted.
Database opened.
SQL> SQL> select * from t1;
X
----------
7
我们可以看到,数据库重启之后,已经commit的操作丢失了。
由于LGWR不会经常崩溃,出现这种问题的时间窗口极短。
Oracle一直在不断的在进行改进和优化,对于commit操作,在11g中又引入了两个新参数:commit_logging和commit_wait.有兴趣的同学可以自己研究。
以上实验完全参考Oracle Core。
如果断电,已提交的事务会丢失数据。
实验之前,我们首先来复习一下commit这个操作。
通常情况下,commit是一个非常快的操作,commit的主要操作是更新事务表槽的SCN。这个操作本身也会产生redo,一般来说,这是一个很快的操作。具体细节如下:
1.得到一个SCN值,准备更新事务表的事务表槽。并且在redo log buffer中为这次更新操作创建一条redo 记录
2.将生成的redo记录复制到日志缓冲区。
3.对undo事务表槽,应用这条redo。事务表中的槽位的SCN等信息被更新。
4.将这条commit记录产生的redo log buffer刷新到磁盘上的在线日志文件。
在第3步和第4步中间,其他的会话能看到事务已经改变,尽管redo还没有写到磁盘,如果数据库实例在第3步和第4步中间崩溃,那么有可能这个已经提交的事务没有被恢复。演示如下:
1.会话1:使用mdb或者oradebug将lgwr挂起。
2.会话2:修改一条数据然后提交--会话会挂起。
3.会话3:查询数据,并注意到修改后的数据是可见的。
4.会话3:shutdown abort,数据库重启后,第2步产生的更改丢失。
在Solaris10+Oracle 11.2.0.1环境下测试.
1.挂起lgwr进程。
# ps -ef|grep lgwr
oracle 1063 1 0 13:37:42 ? 0:01 asm_lgwr_+ASM1
oracle 1637 1 0 13:39:33 ? 0:02 ora_lgwr_mydb1
root 22754 22544 0 20:53:21 pts/3 0:00 grep lgwr
# mdb -p 1637
Loading modules: [ ld.so.1 libc.so.1 ]
>
2.修改一条记录并提交。t1表的唯一字段x,初始值为7,将其更新为8.
SQL> select * from t1;
X
----------
7
SQL> update t1 set x=8;
1 row updated.
SQL> commit;
由于LGWR被挂起,这里的commit操作会hang.
3.查询修改的记录,我们可以看到,这条记录已经由7变成了8
SQL> select * from t1;
X
----------
8
4.shutdown abort数据库,然后重新查询这条记录。SQL> shutdown abort;
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 1570009088 bytes
Fixed Size 2211368 bytes
Variable Size 469762520 bytes
Database Buffers 1090519040 bytes
Redo Buffers 7516160 bytes
Database mounted.
Database opened.
SQL> SQL> select * from t1;
X
----------
7
我们可以看到,数据库重启之后,已经commit的操作丢失了。
由于LGWR不会经常崩溃,出现这种问题的时间窗口极短。
Oracle一直在不断的在进行改进和优化,对于commit操作,在11g中又引入了两个新参数:commit_logging和commit_wait.有兴趣的同学可以自己研究。
以上实验完全参考Oracle Core。