+++++buffer cache 深度解析( 八 )


随着内存的不断增加,1个DBWR进程可能不够用了 。所以从8i起,我们可以为系统配置多个DBWR进程 。初始化参数:决定了启动多少个DBWR进程 。每个DBWR进程都会分配一个lru latch,也就是说每个DBWR进程对应一个 set 。因此建议配置的DBWR进程的数量应该等于lru latch的数量,同时应该小于CPU的数量 。系统启动时,就确定好了 set与DBWR进程的对应关系,每个DBWR进程只会将分配给自己的 set上的脏数据块写入数据文件 。
DBWR作为一个后台进程,只有在某些条件满足了才会触发 。这些条件包括:
1) 当进程在辅助LRU链表和主LRU链表上扫描以查找可以覆盖的 时,如果已经扫描的 的数量到达一定的限度(由隐藏参数:ct决定)时,触发DBWR进程 。ct表示已经扫描的 的个数占整个LRU链表上 总数的百分比 。这时,搜索可用 的进程挂起,在v$中表现为等待“freewait”事件,同时增加v$中的“dirty”的值 。
2) 当DBWR在主LRUW链表上查找已经更新完而正在等待被写入数据文件的 时,如果找到的 的数量超过一定限度(由隐藏参数:h_pct决定)时,DBWR就不再继续往下扫描了,而转到辅助LRUW链表上将其上的脏数据块写入数据文件 。h_pct表示已经扫描的脏数据块的个数占整个主LRUW链表上 总数的百分比 。
3) 如果主LRUW链表和辅助LRUW链表上的脏数据块的总数超过一定限度,也将触发DBWR进程 。该限度由隐藏参数:e决定 。
4) 发生增量检查点( )或完全检查点( )时触发DBWR 。
5) 每隔三秒钟启动一次DBWR 。
6) 将表空间设置为离线()状态时触发DBWR 。
7) 发出命令:alter… begin ,从而将表空间设置为热备份状态时触发DBWR 。
8) 将表空间设置为只读状态时,触发DBWR 。
9) 删除对象时(比如删除某个表)会触发DBWR 。
当DBWR要写脏数据块时,并不是说立即将所有的脏数据块都同时写入磁盘 。为了尽量减少物理的
I/O的次数,DBWR会将要写的脏数据块所对应的 拷贝到一个名为批量写(write batch)的结构中 。每个 set所对应的DBWR进程都可以向该结构里拷贝。当write batch的 的个数达到一定限额时,才会发生实际的I/O,从而将脏数据块写入磁盘 。这个限额为硬件平台所能支持的同时并发的异步I/O的最大数量 。8i之前是可以用隐藏参数(h)来控制这个限额的 。但是8i以后,取消了该参数,而由自己来计算 。
3.2.5 DBWR、CKPT、LGWR进程之间的合作
将内存数据块写入数据文件实在是一个相当复杂的过程,在这个过程中,首先要保证安全 。所谓安全,就是在写的过程中,一旦发生实例崩溃,要有一套完整的机制能够保证用户已经提交的数据不会丢失;其次,在保证安全的基础上,要尽可能的提高效率 。众所周知,I/O操作是最昂贵的操作,所以应该尽可能的将脏数据块收集到一定程度以后,再批量写入磁盘中 。
直观上最简单的解决方法就是,每当用户提交的时候就将所改变的内存数据块交给DBWR,由其写入数据文件 。这样的话,一定能够保证提交的数据不会丢失 。但是这种方式效率最为低下,在高并发环境中,一定会引起I/O方面的争用 。当然不会采用这种没有扩展性的方式 。引入了CKPT和LGWR这两个后台进程,这两个进程与DBWR进程互相合作,提供了既安全又高效的写脏数据块的解决方法 。
用户进程每次修改内存数据块时,都会在日志缓冲区(redo )中构造一个相应的重做条目(redo entry),该重做条目描述了被修改的数据块在修改之前和修改之后的值 。而LGWR进程则负责将这些重做条目写入联机日志文件 。只要重做条目进入了联机日志文件,那么数据的安全就有保障了,否则这些数据都是有安全隐患的 。LGWR 是一个必须和前台用户进程通信的进程 。LGWR 承担了维护系统数据完整性的任务,它保证了数据在任何情况下都不会丢失 。