Chinaunix首页 | 论坛 | 博客
  • 博客访问: 274720
  • 博文数量: 80
  • 博客积分: 2269
  • 博客等级: 大尉
  • 技术积分: 836
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-09 11:07
文章分类

全部博文(80)

文章存档

2016年(1)

2013年(8)

2012年(23)

2011年(21)

2010年(21)

2009年(6)

我的朋友

分类: Oracle

2012-04-01 16:41:41

LOG BUFFER 和LGWR

   REDO LOG的产生十分频繁,几乎每秒钟都有几百K到几M的RED LOG产生,甚至某些大型数据库每秒钟产生的REDO LOG量达到了10M以上。不过前台进程每次产生的REDO量却不大,一般在几百字节到几K,而一般来所一个事务产生的REDO 量也不过几K到几十K。基于REDO产生的这个特点,如果每次REDO产生后就必须写入REDO LOG文件,那么就会存在两个问题,一个是REDO LOG文件写入的频率过高,会导致REDO LOG文件的IO存在问题,第二个是如果由前台进程来完成REDO LOG的写入,那么会导致大量并发的前台进程产生REDO LOG文件的争用。为了解决这两个问题,Oracle在REDO LOG机制中引入了LGWR后台进程和LOG BUFFER。

   LOG BUFFER是Oracle用来缓存前台进程产生的REDO LOG信息的,有了LOG BUFFER,前台进程就可以将产生的REDO LOG信息写入LOG BUFFER,而不需要直接写入REDO LOG文件,这样就大大提高了REDO LOG产生和保存的时间,从而提高数据库在高并发情况下的性能。

   既然前台进程不将REDO LOG信息写入REDO LOG文件了,那么就必须要有一个后台进程来完成这个工作。这个后台进程就是LGWR,LGWR进程的主要工作就是将LOG BUFFER中的数据批量写入到REDO LOG文件中。对于Oracle数据库中,只要对数据库的改变写入到REDO LOG文件中了,那么就可以确保相关的事务不会丢失了。

   引入LOG BUFFER后,提高了整个数据库RDMBS写日志的性能,但是如何确保一个已经提交的事务确确实实的被保存在数据库中,不会因为之后数据库发生故障而丢失呢?实际上在前面两节中我们介绍的REDO LOG的一些基本的算法确保了这一点。首先WRITE AHEAD LOG协议确保了只要保存到REDO LOG文件中的数据库变化一定能够被重演,不会丢失,也不会产生二义性。其次是在事务提交的时候,会产生一个COMMIT的CV,这个CV被写入LOG BUFFER后,前台进程会发出一个信号,要求LGWR将和这个事务相关的REDO LOG信息写入到REDO LOG文件中,只有这个事务相关的REDO LOG信息已经确确实实被写入REDO LOG文件的时候,前台进程才会向客户端发出事务提交成功的消息,这样一个事务才算是被提交完成了。在这个协议下,只要客户端收到了提交完成的消息,那么可以确保,该事务已经存盘,不会丢失了。LGWR会绕过操作系统的缓冲,直接写入数据文件中,以确保REDO LOG的信息不会因为操作系统出现故障(比如宕机)而丢失要求确保写入REDO LOG文件的数据。

   实际上,虽然Oracle数据库使用了绕过缓冲直接写REDO LOG文件的方法,以避免操作系统故障导致的数据丢失,不过我们还是无法确保这些数据已经确确实实被写到了物理磁盘上。因为我们RDBMS使用的绝大多数存储系统都是带有写缓冲的,写缓冲可以有效的提高存储系统写性能,不过也带来了另外的一个问题,就是说一旦存储出现故障,可能会导致REDO LOG的信息丢失,甚至导致REDO LOG出现严重损坏。存储故障的概率较小,不过这种小概率事件一旦发生还是会导致一些数据库事务的丢失,因此虽然Oracle的内部算法可以确保一旦事务提交成功,事务就确认被保存完毕了,不过还是可能出现提交成功的事务丢失的现象。

   实际上,Oracle在设计REDO LOG文件的时候,已经最大限度的考虑了REDO LOG文件的安全性,REDO LOG文件的BLOCK SIZE和数据库的BLOCK SIZE是完全不同的,REDO LOG文件的BLOCK SIZE是和操作系统的IO BLOCK SZIE完全相同的,这种设计确保了一个REDO LOG BLOCK是在一次物理IO中同时写入的,因此REDO LOG BLOCK不会出现块断裂的现象。

   了解LOG BUFFER和LGWR的算法,有助于我们分析和解决相关的性能问题,因此我们需要花一点时间来了解LOG BUFFER相关的基本算法。用一句话来概括,LOG BUFFER是一个循环使用的顺序型BUFFER。这句话里包含了两个含义,一个是LOG BUFFER是一个顺序读写的BUFFER,LOG BUFFER数据的写入是顺序的;第二个含义是LOG BUFFER是一个循环BUFFER,当LOG BUFFER写满后,会回到头上来继续写入REDO LOG信息。LOG BUFFER数据的写入是由前台进程完成的,这个写入操作是并发的,每个前台进程在生成了REDO LOG信息后,需要首先在LOG BUFFER中分配空间,然后将REDO LOG信息写入到LOG BUFFER中去。在LOG BUFFER中分配空间是一个串行的操作,因此Oracle在设计这方面的算法的时候,把LOG BUFFER空间分配和将REDO LOG数据拷贝到LOG BUFFER中这两个操作分离了,一旦分配了LOG BUFFER空间,就可以释放相关的闩锁,其他前台进程就可以继续分配空间了(这里所说的前台进程只是一个泛指,是为了表述方便而已,读者一定要注意,因为后台进程也会对数据库进行修改,也需要产生REDO LOG信息,后台进程的REDO 操作和前台进程是大体一致的)。

   前台进程写入REDO 信息会使LOG BUFFER的尾部指针不停的向前推进,而LGWR这个后台进程不听的从LOG BUFFER的头部指针处开始查找还未写入REDO LOG文件的LOG BUFFER信息,并将这些信息写入REDO LOG文件中,并且将BUFFER头部指针不停的向后推进,一旦LOG BUFFER的头部指针和尾部指针重合,那么就说嘛了当前的LOG BUFFER是空的。而如果前台进程在LOG BUFFER中分配空间会使LOG BUFFER的尾部指针一直向前推进,一旦LOG BUFFER的尾部指针追上了LOG BUFFER的头部指针,那么说明目前LOG BUFFER中无法分配新的空间给后台进程了,后台进程必须要等候LGWR将这些数据写入REDO LOG文件,然后向前推进了头部指针,才可能再次获得新的可用BUFFER空间。这个时候,前台进程会等待LOG FILE SYNC事件。

为了让LGWR尽快将LOG BUFFER中的数据写入REDO LOG文件,以便于腾出更多的空闲空间,Oracle数据库设计了LGWR写的触发条件:

l 事务提交时

l LOG BUFFER中的数据超过1M

l 当LOG BUFFER中的数据超过了_log_io_size隐含参数指定的大小

l 每隔3秒钟

   前面我们多次提到了,当事务提交时,会产生一个提交的REDO RECORD,这个RECORD写入LOG BUFFER后,前台进程会触发LGWR写操作,这个时候前台进程就会等待LOG FILE SYNC等待,直到LGWR将相关的数据写入REDO LOG文件,这个等待就会结束,前台进程就会收到提交成功的消息。如果我们的系统中,每秒的事务数量较大,比如几十个或者几百个,甚至大型OLTP系统可能会达到每秒数千个事务。在这种系统中,LGWR由于事务提交而被激发的频率很高,LOG BUFFER的信息会被很快的写入REDO LOG文件中。

   而对于某些系统来说,平均每个事务的大小很大,平均每个事务生成的REDO LOG数据量也很大,比如1M甚至更高,平均每秒钟的事务数很少,比如1-2个甚至小于一个,那么这种系统中LGWR由于事务提交而被激发的频率很低,可能导致REDO LOG信息在LOG BUFFER中被大量积压,oracle设计的LOG BUFFER中数据超过1M的LGWR激发条件就是为了解决这种情况而设计的,当LOG BUFFER中的积压数据很多时,虽然没有事务提交,也会触发LGWR将BUFFER中的数据写入REDO LOG文件。

   除此之外,Oracle还通过了_LOG_IO_SIZE这个隐含参数来进一步控制LGWR写操作,当LOG BUFFER中的数据超过了这个隐含参数的规定的大小,也会触发LGWR被激发。这个参数的缺省值是LOG BUFFER大小的1/3,这个参数单位是REDO LOG BLOCK。这个参数可以控制当LOG BUFFER中有多少个数据块被占用时,就要触发LGWR写操作,从而避免LOG BUFFER被用尽。

如果一个系统很空闲,很长时间都没有事务提交,LOG BUFFER的使用也很少,就可能会导致LOG BUFFER中的数据长期没有被写入REDO LOG文件,带来丢失数据的风险,因此Oracle还设计了一个LGWR写的激发条件,设置了一个时间触发器,每隔3秒钟,这个触发器都会被激活,这个触发器被激活的时候,如果发现LOG BUFFER不是空的,并且LGWR不处于活跃状态,就会产生一个事件,激活LGWR。

   前面我们讨论了LGWR和LOG BUFFER的一些基本的算法,那么下面我们来讨论讨论LOG FILE SYNC等待事件。LOG FILE SYNC等待的含义是等待LGWR将LOG BUFFER的数据写入到REDO LOG文件中。一般情况下,如果某个事务在做提交的时候,会等待LOG FILE SYNC,而没有做提交操作的会话不需要等待LOG FILE SYNC,因为前台进程只需要将REDO LOG信息写入到LOG BUFFER中就可以了,不需要等待这些数据被写入REDO LOG文件。不过如果前台进程在分配LOG BUFFER的时候,如果发现LOG BUFFER的尾部已经追上了LOG BUFFER的头部,那么前台进程就要等待LGWR进程将头部的数据写入REDO LOG文件,然后释放LOG BUFFER空间。这个时候,没有做提交操作的前台进程都会等待LOG FILE SYNC事件。这种情况下,加大LOG BUFFER就可能可以减少大部分的LOG FILE SYNC等待了。

   加大LOG BUFFER的大小,可能会带来另外一个问题,比如LOG BUFFER从1M增加到30M(关于LOG BUFFER是否需要大于3M的问题,以前我们已经多次讨论,因此在这里不再讨论了,大家只需要记住一点就可以了,LOG BUFFER大于3M浪费空间,对性能影响不大的观点是错误的),那么_LOG_IO_SIZE自动会从300K增加到10M,在一个平均每秒事务数较少,并且每个事务的REDO SIZE较大的系统中,触发LGWR写操作的LOG BUFFER数据量会达到1M。一般来说,在一个大型的OLTP系统里,每次LGWR写入REDO LOG文件的大小在几K到几十K之间,平均LOG FILE SYNC的时间在1-10毫秒之间。如果平均每次写入的数据量过大,会导致LOG FILE SYNC的等待时间变长。因此在这种情况下,就可能需要设置_LOG_IO_SIZE参数,确保LOG FILE SYNC等待不要过长。

   如果每次写入REDO LOG文件的数据量也不大,而LOG FILE SYNC等待时间很吵,比如说超过100毫秒,那么我们就要分析一下REDO LOG文件的IO性能了,如果REDO LOG文件IO性能不佳,或者该文件所在的IO热点较大,也可能导致LOG FILE SYNC等待时间偏大,这种情况,我们可以查看后台进程的LOG FILE PARALLEL WRITE这个等待事件,这个等待事件一般的等待时间为几个毫秒,如果这个等待事件的平均等待时间较长,那么说明REDO LOG文件的IO性能不佳,需要将REDO LOG文件放到IO量较小,性能较快的磁盘上。

   在OLTP系统上,REDO LOG文件的写操作主要是小型的,比较频繁,一般的写大小在几K,而每秒钟产生的写IO次数会达到几十次,数百次甚至上千次。因此REDO LOG文件适合存放于IOPS较高的转速较快的磁盘上,IOPS仅能达到数百次的SATA盘不适合存放REDO LOG文件。另外由于REDO LOG文件的写入是串行的,因此对于REDO LOG文件所做的底层条带化处理,对于REDO LOG写性能的提升是十分有限的。
阅读(515) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~