本帖最后由 jayli426 于 2013-2-14 01:52 编辑
因为从前我只是写程序的人,也不是计算机专业出身,所以对Oracle的checkpoint有过很多误解
现在想来,有些甚至是让人哭笑不得。
现在我来描述一下我从前的误解,我相信这也是很多初学者的误解。
我在这里描述一下,避免后来的初学者踏入同一条河流
1)第一个误解,redo log只是理解成log,没有理解redo
这是什么一回事呢?
大家做应用系统的都知道,日志只是记录系统中的变化历史。
其中最重要的目的,主要是当系统报错时,能够查看具体的报错信息和当时的上下文
这样能够方便我们定位问题和排错
可是,如果带着这个思路去理解redo log,显然是南辕北辙。
oracle中redo log某种程度上来讲,是实际数据的一部分。
更确切的说如果没有redo log,就无法保证数据的一致性
其实Oracle也有类似(我刚刚提到的)应用系统的日志,例如alert.log。
所以建议初学者 看到redo log的时候,不要只看到log,而忽视了redo
而且这个redo 是真正会redo的,而不是简单记录操作历史的。
2)第二个误解:写入数据文件的只会是commit过数据
程序员最初接触Oracle,自然都是从SQL语句开始的,
例如执行
insert table_A values('XX','YY',...);
commit;
update table_A set column_1='ZZ';
commit;
总而言之看见commit,就知道之前执行(在同一个事务中)的insert 或者update,或者delete涉及到的数据 已经完成了持久化。
OK
当看到DBWR负责将数据写入Datafile这样一个描述后
我的第一感觉DBWR 只会将 commit过的数据写入data file
(这是误解二)
实际却是 DBWR 会写一切被修改的数据块 (说一切被修改过的数据,可能不妥)。
同时还以为执行commit动作的时候,就会把对应数据写入到数据文件
(这是误解三)
其实是不会,是先写redo log,然后再找时机往数据文件中写 (具体什么时机,则要看参数配置了)。
为何会这样呢?
因为写数据文件时随机写,代价太高。
而写redolog 是顺序写,相对代价低很多
是否又有初学者问;什么是随机写
我建议可以先了解harddisk的原理出发,去了解随机写和顺序写
不过可以简单理解为,假设现在有1万个房间,每一层有100个房间,假设现在要访问801,1001,3003这3个房间,
分别放上桔子,苹果,香蕉
我们得跑上8楼,10楼,30楼,然后再找对应的房间号
这就是DBWR写数据文件做的动作
而redo log只是记录我们要去801号房间去放桔子,1001号房间去放苹果,3003号房间去放香蕉
这么几个需要做的动作。
至于什么时候放?那当然等DBWR有空了再放。
这个技术有个专有名词,叫write head logging
即日志优先写,或者优先写日志。
回到误解二
说实话,我敢担保初学者看到这里依然会有疑惑的 DBWR 怎么会写一切会修改的数据块呢?
那么岂不是未提交过的数据也被写入了数据文件了?
可事实却是如此
那为何未提交过的数据也被写入数据文件呢?那么岂不是数据的一致性发生问题了?
(
这就是Oracle 知识的特点
我们不能只关注一个知识点,而是需要将各个知识点串起来,才会有对Oracle真正理解。
因为只关注一个的时候,就会容易走入死胡同。
所以我们要做得真的就是如白鳝大师所言“像Oracle一样思考”,而且是全盘和系统的思考
)
言归正传
那为何未提交过的数据也会被写入数据文件呢
注意:Oracle读写的最小原子单位是数据块,不是记录
性急的兄弟又会立马出来打断,这个我知道啊,但是这和我的问题有什么关系呢?
哈,关系大着呢?
因为DBWR只是按数据块被更新的顺序的依次写入到数据文件中
那么存在这么一种可能,就是这个数据块上既有commit过记录,也有正在被某个事物处理的数据,
那么当这个数据块被写入到数据文件的时候,很明显,此时被修改但是未被提交过的数据也被写入了数据文件。
3)第4个误解:redo log只记录了commit过的数据变化
前面提到write head logging,也就是说commit过的数据,应当会被redo log 记录下来。
那么从前我的理解是redo log只会记录commit过的数据,而不记录未提交的数据
现在回想,这个理解其实还颇有些道理的
是啊,那些没有提交过但是被修改过的数据,为何要被写入到日志里面呢?
让他好好在内存里呆着,等到事务rollback的时候,自然将这些数据给扔掉算了(其实就是不care,不管就得了)。
我为何有这个思路呢?
就是一直是以SQL的逻辑处理方式来理解oracle的内部处理机制
SQL处理数据的单位是基于记录的
而Oracle 处理数据,DBWR是基于数据块的,后台进程写redo log record是基于redo log block的
好像处理control file 的单位也不同的
但是总而言之,都不是基于记录的。
考虑一下之前提到过的场景
当一个数据块上既有commit过记录,也有正在被某个事物处理的数据,
那么当这个数据块被写入到数据文件的时候,很明显,被修改但是未被提交过的数据也被写入了数据文件。
那么Oracle 怎么知道这个数据块里面的数据是提交了还是没有提交的呢?
数据块里面虽然是记录是事务编号,但是并没有记录哪个数据是和哪个事务相关,即我们只能知道这个数据块和哪些事务关联
但是我们无法知道这些事务和这个数据块中的哪个数据关联。
这个关联靠什么, 靠的是redo log record
所以只有把未commit过的记录相关联的redo log record记录也写入到redo log file中。
我们才能在数据库crash后,恢复数据的时候(确切的说是instance recovery),知道哪些数据是commit过的,哪些数据是未commit过的,需要rollback的
4)redo log记录数据变化是基于记录的
这个误解其实还是源于 SQL处理数据的单位是基于记录的
例如执行了一条sql
insert table_A values('XX','YY',...);
redo log里面就记录这条SQL
insert table_A values('XX','YY',...);
可是是这样的吗?
想一想,redo log可是要负责recovery的
试想
如果我们执行了
delete from table_A
但是我们没有提交,
而相应的数据块也被写入了,如果这个时候数据库crash
那么再次启动恢复的时候,如何找回之前的数据呢?
(
这里我有疑问,感觉解释不下去了
貌似这个时候也可以通过undo 表空间找回啊?
因为这个时候对应undo 表空间的数据也是写入了的啊
因为修改实际业务数据块的时候,也修改了undo 表空间的数据块
这个时候SCN应当是相同的,那么实际业务数据库写入了的时候,undo表空间也是写入了的。
)