Oracle Write List and Database Writer

Write lists通常也叫做dirty lists或LRU-W lists,是由整个dirty buffer headers组成。每个dirty bufferheader也存放在CBC结构中。Oracle有一个工作集的概念,它由LRU latch,LRU chain与write list组成。每个数据库写进程与一个或多个工作集关联。在实例启动时,Oracle将决定工作集的数据量与数据库写进程的数量(db_writer_processes,它的缺省值为1),然后设置它们的关联。当一个数据库写进程执行操作时,它将从它的写列表中获取信息。服务器进程与数据库写进程从它们瓣LRU chains中移动不被频繁访问的dirty buffers到与它们相关的写列表中。

数据库写进程的运转
多块写比单块写更有效,因此数据库写进程在执行写操作之前会构建一个dirty列表。多年以来,Oracle已经修改了实例参数与算法来控制最小的dirty列表的批量大小。事实上,Oracle使用了一种自调整的算法来计算dirty buffers的突发事件。当数据库写进程正在进行多块写操作时,它将出现db file parallel write等待事件。v$session_wait视图中的参数用来提供正在被写到磁盘的数据块数。数据库写进程的职责是将dirty buffers写入磁盘。执行下面的查询:

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       155

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       185

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       121
SQL>  select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       172

SQL>  select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       173

从上面的查询结果可以看到dirty buffer数量是呈循环性的增长与减少。循环的结果是当数据库写进程将dirty buffers写入磁盘时,这些dirty buffers将再一次变为free buffers。这种循环是正常的,也是想要看到的。如果dirty buffers的计数一直在增长,那么你就知道数据库写进程处理能力不足。对于大型Oracle系统来说通常有上千个dirty buffers存在。

我们都知道数据库写进程每3秒会被唤醒一次。通过跟踪来查看在Oracle 11g中是不是也是这样。注意休眠时间是大概3秒钟并且系统调用为semtimeodop。而当一个服务器进程在获取latch期间休眠时,因为它执行的是select系统调用。select不允许这具进程被唤醒,但信号量调用可以。这是很重要的区别,因为数据库写进程由于各种原因需要被唤醒,比如检查点操作或free buffer waits等待事件。

[root@db1 ~]# ps -eaf |grep dbw
oracle    49087      1  0  2018 ?        03:20:23 ora_dbw0_RLZY1
oracle    49089      1  0  2018 ?        03:19:18 ora_dbw1_RLZY1

[root@db1 ~]# strace -rp 49089
......
0.000298 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......
0.000482 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......
0.000336 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......
0.000608 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......

数据库写进程相关竞争的识别与解决
有各种与数据库写进程相关的等待事件。对争用情况进行分类的一种方法是理解数据库写进程是否有“push-to-disk”问题或“pull-from-write-list”问题。大多数的问题是push问题,也就是写磁盘的问题。但也有一种很常见的pull问题稍后再进行说明。与数据库写进程push-to-disk问题相关的所有等待事件都是以db file开始。与其它IO等待事件一样,在IO调用之前和之后,会执行gettimeofday调用,并且区别就是我们通过Oracle等待事件接口所看到的。这里有两个常见的数据库写进程push-to-disk等待事件:
.db file parallel write
目前最常见的数据库写进程等待事件,一个parallel写也可以简化为多块写。这是数据库写进程从写列表中获取数据并将dirty块批量写入磁盘的结果。希望这个等待事件的等待时间小于5ms,但每个单位有它自己的预算与服务要求。写操作的时间小于5ms说明写缓存工作的很好。

.db file single write
它不应该是top等待事件。当所有数据库文件头块在写入检查点操作的末尾可能会出现。这是通过一次执行多个单块写来完成的。

从需求和能力方面来查看IO问题。当存在一个IO问题时,需求已经超过能务了。只有当锁或阻塞类型出现时,比如free buffer waits事件才会出现例外。当看到数据库文件写操作出现问题时,除了锁或阻塞的原因之外,知道IO请求已经超过了IO子系统的能力。使用复杂IO管理将增加不同系统文件与数据库文件存储在相同磁盘上的机会。

IO问题可能变得非常情绪化。供应商参与进来并开始保护自己的地盘。为了帮助解决问题,从应用程序角度来看,将查找生成dirty buffers的SQL语句。将会找到一个或多个更新,插入与删除操作

从Oracle角度来分析,可以考虑任何可能增加Oracle IO写效率的参数。例如研究可以增加数据库写进程批量写大小的方法。修改数据库写进程的批量大小与数据库版本有关,例如_db_block_write_batch与_db_writer_max_writes参数。也可以考虑增加参数_db_writer_max_scan_pct(缺省值是40,例如40%)或_db_writer_max_scan_cnt,在触发数据库写进程开始执行写操作之前它们用来判断一个服务器进程将扫描多少LRU buffer headers。增加这些参数将提供更多时间来构建写列表,因此造成每个数据库写IO请求将写入更多数据块。这将有效的增加每秒写入磁盘的数据块。测试显示通过将db_writer_max_scan_pct从5增加到95,数据库写进程操作系统写调用将减少9%且db file parallel write等待减少3%,当事务活动增加14%时,每秒的块改变将增加19%。仅仅通过改变这个参数,当工作量增加时IO活动减少了。

另一种可能是遇到服务器进程不触发数据库写进程执行写操作,因此会构建写队列。当一个服务器进程,在搜索一个free buffer时,偶然发现一个不被频繁访问的dirty buffer后将其移动到相关的写列表,并且也会检查写列表是否足够长可以执行写操作了。如果写列表已经足够长了,服务器进程将触发数据库写进程执行写操作。因此这是一个有效的选择,为了允许构建写队列来导致大批量写操作,可以增加_db_large_dirty_queue(在某些系统中缺省值是25)参数。但创建太在的写队列需要小心。当dirty buffers正被写入磁盘时,它们不能被改变。任何需要改变正被写入磁盘的buffer都必须等待。相关的等待事件为write complete waits。在top等待事件中write complete waits不是很常见,但如果修改了写列队长度就可能会出现。

最后,从Oracle角度来看,增加buffer cache可以让数据库写进程减轻在短时间内强烈的数据块改变所带来的压力。一个大的buffer cache允许cache来填充dirty buffers,在不强制数据库执行写操作时,有更多的时间来创建大的且更有效的批量写。如果真的想给数据库写进程施加压力,创建一个小的buffer cache并执行一些DML操作,你将会看到数据库写进程疯狂地试图删除脏缓存区中的小缓存。

从操作系统角度来看,这应该存在IO瓶颈。这是很罕见的,如果IO写响应时间小于5ms而写缓存工作的很好,那么总的等待时间将足够大,因此db file parallel write等待事件将被推到top等待事件中。当出现这种情况时,关注IO子系统不会有什么实质性的效果。在这种情况下,关注减小应用程序IO写活动与增加Oracle写效率。这也意味着要在数据库活动高峰期来创造性的减小写操作。经验丰富的DBA已经看到了在正常业务期间存在写密集的IO活动,比如RMAN,血份与文件传输。减少非Oracle IO活动能有效地增加IO子系统的能力。

发表评论

电子邮件地址不会被公开。