有的网友对我之前写的一篇技术博文中的描述提出了疑问,http://blog.itpub.net/23718752/viewspace-1436965/
其中的主要意思是:oracle中采用了undo+redo机制来作为数据恢复的基石,数据的恢复是通过前后台结合来实现的,在缓存级别,通过dbwr,能够把修改后的数据块刷入数据文件,这是一个异步的过程,不会因为发生数据变更就马上写入数据文件,同时存在log buffer,能够通过log buffer生成redo日志,最后通过lgwr把这部分变更刷到redo 日志,在这个过程中lgwr负责了保持数据完整性的任务,保证了数据不会丢失。
这句话再浓缩一下就是Oracle能够保证对于commit操作的数据都能够成功恢复。
今天可以通过两个特殊的场景来解释一下。
场景1:模拟Oracle ACID的异常情况,事务已经提交,但是redo log buffer还没有写到磁盘
第一个场景就是把lgwr写入redo的操作影响放大,操作时间延长。这个可以参考Jonathan Lewis的博客。https://jonathanlewis.wordpress.com/2011/08/19/redo-2/
这个场景被称为模拟Oracle ACID的异常情况,事务已经提交,但是redo log buffer还没有写到磁盘.我们来看看再下结论。
window #1: --打开一个窗口1,然后创建一个临时表t1
create table t1(n1 number);
insert into t1 values(1);
commit;
然后通过v$process查到对应的LGWR pid
n1@TEST11G> select v$process.pid from v$process where pname='LGWR';
PID
----------
11
window #2 --打开窗口2,开启oradebug,绑定到lgwr上
sys@TEST11G> oradebug setorapid 11
Oracle pid: 11, Unix process pid: 11767, image: oracle@oel1 (LGWR)
sys@TEST11G>oradebug suspend
Statement processed.
window #1--然后回到窗口1,做一个dml操作,commit
n1@TEST11G> update t1 set n1 = 2;
1 row updated.
n1@TEST11G> commit;
这个时候commit操作会一直hang在那儿
window #3 --开启第3个窗口,然后查看是否更新后的值已经可以成功查看。
sys@TEST11G> select count(*)from n1.t1;
COUNT(*)
----------
2
1 row selected.
这个时候我们马上做类似断电的场景,shutdown abort
sys@TEST11G> shutdown abort
ORACLE instance shut down.
这个时候如果观察第一个窗口,会发现下面的错误,可以得知对应的session已经被强制释放了。
n1@TEST11G> commit;
ERROR:
ORA-03114: not connected to ORACLE
commit
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel
Process ID: 15180
Session ID: 125 Serial number: 84
然后我们重启数据库
idle> startup
ORACLE instance started.
Total System Global Area 435224576 bytes
Fixed Size 1337044 bytes
Variable Size 272632108 bytes
Database Buffers 155189248 bytes
Redo Buffers 6066176 bytes
Database mounted.
Database opened.
这个时候再查看数据,就会发现更新后的值已经丢失了。
idle> select *from n1.t1;
N1
----------
1
1 row selected.
对于这个问题,网上大家也是各有所见,有的说commit没有成功返回,就不算是一个完整的事务,没有恢复是可以理解的。有的说,这个是Oracle对于数据恢复的一个灰色地带。
我的意见是首先这是一个测试,把整个过程放慢,影响放大了,整个过程处于一个快要提交但是还没有提交的边界。这部分内容还是没有写入redo中的。只是从缓存中完成了整个数据变更的过程。
commit在这个放慢的临界点没有完成,严格意义上应该不属于一个完整的事务。
上面这个案例是通过debug的方式来做的,我们来用另外一个场景来模拟一下,看看实际中可能碰到的场景如果出现类似问题,redo是否依旧可靠。
场景2:模拟Oracle 归档满的临界点,事务是否依然能够成功提交,成功恢复
我们来模拟在归档日志满的时候,无法再写入redo,依旧可以成功commit,但是数据是否能够成功恢复的案例。
我们在测试环境简单模拟一下归档满的临界点,查看磁盘空间,归档所在的挂载点还有6G的可用空间。
[ora11g@oel1 archivelog]$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 30G 23G 6.0G 79% /u02
none 690M 104K 690M 1% /var/lib/xenstored
我们来使用dd来创建一些dummy文件。
[ora11g@oel1 archivelog]$ time dd if=/dev/zero bs=1M count=5000 of=direct_5000M
5000+0 records in
5000+0 records out
5242880000 bytes (5.2 GB) copied, 224.21 seconds, 23.4 MB/s
real 3m44.222s
user 0m0.024s
sys 0m20.317s
创建后再逐步缩小范围。
[ora11g@oel1 archivelog]$ time dd if=/dev/zero bs=1M count=1000 of=direct_1000M
[ora11g@oel1 archivelog]$ time dd if=/dev/zero bs=1M count=100 of=direct_100M
最后发现空间都被占用完了。
[ora11g@oel1 archivelog]$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 30G 29G 0 100% /u02
直到没有空间可用我们才收手。
[ora11g@oel1 archivelog]$ time dd if=/dev/zero bs=1M count=10 of=direct_10M
dd: writing `direct_10M': No space left on device
1+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000909 seconds, 0.0 kB/s
最后生成的dd文件如下:
-rw-r----- 1 ora11g dba 33485824 May 20 15:30 1_4_879784710.dbf
-rw-r--r-- 1 ora11g dba 5242880000 May 20 15:36 direct_5000M
-rw-r--r-- 1 ora11g dba 1048576000 May 20 15:37 direct_1000M
-rw-r--r-- 1 ora11g dba 104603648 May 20 15:38 direct_100M
-rw-r--r-- 1 ora11g dba 0 May 20 15:39 direct_10M
使用sqlplus报出下面的错误,这样我们就可以开始这个临界点的测试了。
sys@TEST11G> conn n1/n1
ERROR:
ORA-00257: archiver error. Connect internal only, until freed.
sys@TEST11G> create table aa as select *from cat;
Table created.
sys@TEST11G> insert into aa select *from aa;
4856 rows created.
sys@TEST11G> commit;
Commit complete.
sys@TEST11G> insert into aa select *from aa;
9712 rows created.
sys@TEST11G> commit;
Commit complete.
sys@TEST11G> select count(*)from aa;
COUNT(*)
----------
19424
可以看到这个过程中还是能够成功commit数据的。查取更新都可以使用顺利完成。
可以再开一个窗口运行alter system switch logfile做几个日志刷新。发现这个时候日志刷新也hang住了。
看看alert日志,发现已经提示空间不够,无法生成归档日志了。
Wed May 20 15:43:59 2015
Errors in file /u02/ora11g/diag/rdbms/test11g/TEST11G/trace/TEST11G_arc1_13247.trc:
ORA-19504: failed to create file "/u02/ora11g/flash_recovery_area/TEST11G/archivelog/1_5_879784710.dbf"
ORA-27044: unable to write the header block of file
Linux Error: 28: No space left on device
Additional information: 3
ARC1: Error 19504 Creating archive log file to '/u02/ora11g/flash_recovery_area/TEST11G/archivelog/1_5_879784710.dbf'
ARCH: Archival stopped, error occurred. Will continue retrying
Non critical error ORA-00001 caught while writing to trace file "/u02/ora11g/diag/rdbms/test11g/TEST11G/trace/TEST11G_arc1_13247.trc"
Error message:
Writing to the above trace file is disabled for now on...
ORACLE Instance TEST11G -
这个时候我们继续模拟一个断电场景shutdown abort.
原有的日志刷新也会自动终止。
ERROR:
ORA-03114: not connected to ORACLE
alter system switch logfile
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel
Process ID: 9686
Session ID: 125 Serial number: 480
如果这个时候启动到open阶段就会自动停止,原因就是归档空间的问题。
我们做一个小改动。腾出一小部分空间来。
[ora11g@oel1 archivelog]$ rm direct_100M
然后再次尝试启动数据库就没有问题了
idle> alter database open;
Database altered.
这个时候查看归档路径下,会发现已经生成了3个归档文档
-rw-r--r-- 1 ora11g dba 5242880000 May 20 15:36 direct_5000M
-rw-r--r-- 1 ora11g dba 1048576000 May 20 15:37 direct_1000M
-rw-r--r-- 1 ora11g dba 0 May 20 15:39 direct_10M
-rw-r----- 1 ora11g dba 2048 May 20 15:55 1_6_879784710.dbf
-rw-r----- 1 ora11g dba 204800 May 20 15:55 1_5_879784710.dbf
-rw-r----- 1 ora11g dba 1808384 May 20 15:55 1_7_879784710.dbf
这个时候发现对于这些信息变更已经成功刷新到了归档中。这样就为数据恢复提供了强有力的基石,保证了数据在commit成功的情况下能够成功恢复。