log buffer 深度解析+

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 原文整理自网络 5.3  深入Log Buffer   数据库在运行过程中,不可避免地要遇到各种能够导致数据库损坏的情况。比如突然断电、Oracle或者操作系统的程序bug导致数据库内部逻辑结构损坏、磁盘介质损坏等,都有可能造成数据库崩溃,从而导致数据丢失的现象发生。

原文整理自网络

5.3  深入Log Buffer  

数据库在运行过程中,不可避免地要遇到各种能够导致数据库损坏的情况。比如突然断电、Oracle或者操作系统的程序bug导致数据库内部逻辑结构损坏、磁盘介质损坏等,都有可能造成数据库崩溃,从而导致数据丢失的现象发生。

为了避免,或者说为了修复这些状况所导致的数据丢失现象,Oracle引入了日志缓冲区和日志文件的概念。所谓日志,就是将数据库中所有改变数据块的操作,都原原本本地记录下来。这些改变数据块的操作不仅包括对数据表的DML命令或者引起数据字典内容变化的DDL命令,还包括对索引的改变、对回滚段数据块的改变等。只有将数据库中所有的变化都记录下来,当发生数据库损坏时,才能够通过重新应用这些变化,从而达到恢复数据库的目的。
既然是要记录,那就必然引出一个问题,就是如何记录这些变化?比较容易想到的有两种方式。

第一种是使用逻辑的记录方式,也就是用描述性的语句来记录整个变化过程。比如对于某个update更新操作来说来说,可以记录为两条语句:delete 旧值以及insert 新值。这种方式的优点是非常节省空间,因为对每个操作,只需要记录几条逻辑上的语句即可。但是缺点也很明显,就是一旦需要进行恢复,就会非常消耗资源。设想一下,某个update操作更新了非常多的数据块,由于buffer cache内存有限,很多脏数据块都已经写入了数据文件。但就在更新快结束时,突然发生断电,所做的更新丢失。那么重新启动实例时,Oracle需要应用日志文件里的记录,于是重新发出delete旧值以及insert新值的语句。这个过程需要重新查找数据文件中符合条件的数据块,然后再挑出来进行更新。这个过程将非常消耗时间,而且会占用大量的buffer cache。

第二种方式是使用物理的记录方式,也就是将每个数据块改变前的镜像和改变后的镜像都记录下来。这种方式优点就是恢复起来速度非常快,直接根据日志文件里所记录的数据块地址和内容更新数据文件中对应的数据块。但是缺点也很明显,就是非常占用磁盘空间。

而Oracle在记录日志的方式上,采用了逻辑和物理相结合的方式。也就是说,Oracle针对每个数据块,记录了插入某个值或者删除某个值的描述语句。假如某个update更新了100个数据块,则Oracle会针对每个数据块记录一对delete 旧值和insert 新值的语句,共有100对这样的描述语句。在每一对描述语句中,都记录了相关数据块的物理地址。通过这种逻辑与物理相结合的方式,Oracle在记录变化时能够尽量节省空间,同时在应用变化时,又能比较快速。

为了临时存放所产生的日志信息,Oracle在SGA中开辟了一块内存区域。这块区域就叫做日志缓冲区(log buffer),当满足一定条件以后,Oracle会使用名为LGWR的后台进程将log buffer中的日志信息写入联机日志文件里。


可以使用初始化参数log_buffer来设置日志缓冲区的大小,单位是字节。日志缓冲区会进一步细分为多个块,每个块的尺寸与操作系统的一个块的尺寸相同,基本都是512字节。我们可以用如下方式来获得日志缓冲区的块尺寸。

SQL> select distinct lebsz as redo_block_size from x$kccle;
REDO_BLOCK_SIZE
---------------
512

也可以用下面的方式来计算出日志缓冲区的块尺寸。

SQL> select round((a.redosize+b.redowast)/c.redoblks) + 16 as
redo_block_size from
2  (select value redosize from v$sysstat where name='redo
size') a,
3  (select value redowast from v$sysstat where name='redo
wastage') b,
4  (select value redoblks from v$sysstat where name='redo
blocks written') c;
REDO_BLOCK_SIZE
---------------
512

日志缓冲区只是日志信息临时存放的区域,这块区域是有限的,而且其中的每个块都是能够循环使用的。这也就说明,日志缓冲区中的内容必须要写入磁盘的文件里,才能永久保留下来,才能在数据库崩溃时能够用来进行恢复。这个文件就叫做联机日志文件。在每个日志缓冲区中的日志块被重用之前,其内容必然已经被写入了磁盘上的联机日志文件中。

联机日志文件就是日志缓冲区的完全副本,组成日志文件的每个日志块的内容都来自于日志缓冲区的日志块。每个日志缓冲区中的日志块都对应到日志文件中的一个日志块。日志缓冲区中的日志块按照发生的先后顺序,放入联机日志文件。

由于日志文件在故障恢复中的重要性,建议至少使用两个日志文件组成一个日志文件组。同一个日志文件组中的日志文件内容一模一样,因为日志缓冲区中的日志块同时会写入日志文件组中的每个日志文件中。每个数据库都必须至少拥有两个日志文件组。这是由于只要数据库一天不停止运行,就会不断产生日志信息,就会不断写入联机日志文件,联机日志文件总会有写满的时候。我们不可能让联机日志文件无限大,也不可能放无限多的联机日志文件,所以联机日志文件必须是循环使用的,在若干个日志文件中轮流的进行写入。一个日志文件写满以后转换到另外一个日志文件继续写的过程叫做日志切换(log switch)。

当一个联机日志文件写满时,可以选择将其归档为脱机日志文件,通常叫做归档日志文件。归档也就是副本,归档的过程也就是将写满的联机日志文件复制到预先指定的目录的过程。只有当一个联机日志文件完成归档以后,该联机日志文件才能够被再次循环使用。强烈建议在生产库中选择这种归档方式,只有在测试环境中可以选择不归档。
可以说,日志缓冲区和日志文件存在的唯一目的就是为了保证被修改的数据不会被丢失。反过来说,也就是为了能够在数据库崩溃的时候,可以用来将数据库恢复到崩溃的那个时间点上。这也就是说,只有将被修改的数据块的日志信息写入了联机日志文件以后,该被修改的数据块才可以说是安全的。如果日志信息在没有被写入日志文件时发生实例崩溃,这时对数据的修改仍将丢失。由此我们可以看出,将日志缓冲区中的日志信息写入日志文件是一个非常重要的过程,这个过程是由一个名为LGWR的后台进程完成的。LGWR 承担了维护系统数据完整性的任务,它保证了数据在任何情况下都不会丢失。

触发LGWR进程将日志缓冲区中的日志信息写入联机日志文件条件包括以下几种。


 
前台进程触发,包括两种情况。

最显而易见的一种情况就是用户发出commit或rollback语句进行提交时,需要触发LGWR将内存里的日志信息写入联机日志文件,因为提交的数据必须被保护而不被丢失;

另外一种情况就是在日志缓冲区中找不到足够的内存来放日志信息时,也会触发LGWR进程将一些日志信息写入联机日志文件以后,从而释放一些空间。
 每隔三秒钟,LGWR启动一次。
 在DBWn启动时,如果发现脏数据块所对应的重做条目还没有写入联机日志文件,则DBWn触发LGWR进程并等待LRWR写完以后才会继续。
 日志信息的数量达到整个日志缓冲区的1/3时,触发LGWR。
 日志信息的数量达到1MB时,触发LGWR。

5.3.1  log buffer的内存结构 

我们已经知道,日志缓冲区用来存放事务对数据块的变化的日志信息。那么这里的日志信息到底包含哪些内容,是由哪些结构组成的呢?

Oracle记录数据库变化(也就是记录日志信息)的最小单位是改动向量(change vector)改动向量用来描述对数据库中任何单个数据块所做的一次改动。改动向量的内容包括:被改动的数据块的版本号事务操作代码被改动的数据块的地址等。这里的版本号非常重要,它能够帮助数据块始终能够体现当前最新的状态。Oracle在建立改动向量时,会从数据块中复制其版本号。而当恢复期间,Oracle读取改动向量并将改动应用于相应的数据块以后,被恢复的数据块的版本号加1。这里的数据块可以属于表或数据索引,也可以属于回滚段。但是对于临时表空间里的临时段,不会生成改动向量。

当多个改动向量按照先后顺序组合在一起,从而完成对数据库的一次改动时,Oracle称这组改动向量为重做记录(redo record)。重做记录用来描述对数据库的一个原子改动。所谓原子改动,就是说,当应用改动中的改动向量时,要么全部成功,要么全部失败,不存在部分成功部分失败的情况。重做记录能够帮助整个数据库体现当前最新的状态。

一个事务至少产生一个重做记录,也可能产生多个重做记录。而Oracle在应用日志记录进行恢复的过程中,以事务作为恢复的最小单位。要么恢复整个事务,要么回滚整个事务。也就是说,要么运用事务重做记录里的所有改动向量,要么一个改动向量都不运用。

因此,日志缓冲区就是许多重做记录按照发生的先后顺序组成的。同时,日志文件也就是由许多重做记录按照先后顺序排列在一起而组成的文件。

我们举个实例来说明重做记录和改动向量产生的过程。比如我们发出如下更新语句(假设表redo_test的NAME列上没有建立索引):

SQL> select * from redo_test;
ID     NAME
----- ----------
1      abc
2      abc
SQL> update redo_test set name='cdf' where id=1;

该语句发出以后,会产生一个重做记录,用来描述对表中数据块进行的修改。包括下面三个改动向量。


 
对回滚段(也就是undo段)事务表的改动,这发生在回滚段段头。事务表中包含被修改的数据块的地址该事务的状态(commit或active)以及存有该事务所使用的回滚段的地址。如果事务表被修改,就会产生针对它的改动向量。
 对回滚段数(undo段)据块的改动。将修改前的旧值(abc)存放到回滚段的数据块里。这时回滚段发生改变,于是产生改动向量。
 对redo_test表的数据块所做的改动。将修改后的新值(cdf)存放到表的数据块里。这时数据块发生改变,于是产生改动向量。

从这个过程可以看到,对于这个update事务,重做记录中会有三个改动向量。当然可能还有其他情况会产生新的重做记录,比如修改的列如果有索引,则必须修改索引。这时就会产生第二个重做记录,用来描述对索引数据块的修改。这时候的重做记录还是和第一个重做记录一样,包含多个改动向量。

此外,在事务完成之后运行commit或rollback语句时,就会产生第三个重做记录。该重做记录只有一个改动向量,用来记录对回滚段事务表的更改,因为commit或rollback时,需要更新事务表里记录的该事务的状态。

5.3.2  log buffer的内部管理机制  

日志缓冲区的内部管理分为两部分,一部分是生成重做记录,另一部分就是重做记录写入联机日志文件。这两部分不是孤立的,没有关联的。在生成重做记录的过程中,可能会触发LGWR将重做记录写入联机日志文件。

我们先用一个例子来说明在日志缓冲区中的操作过程,并使用[file# , blk#]来表示某个数据块;file#表示文件号;blk#表示数据块号。

假设session 1发出更新语句:update redo_test set name='cdf' where id=1;

Oracle首先找出id=1所在的数据块(假设为[file#4,blk#120])放入buffer cache,然后找出一个可用的回滚段数据块(假设为[file#2,blk#19]),将旧值'abc'放入该块,同时生成重做记录。然后将'cdf'放入表的数据块,再生成重做记录。这时日志缓冲区的结构可以简单地表示为下面的形式(我们在前面描述日志缓冲区的内存结构时,知道重做记录中最重要的就下面列的这几列内容。同时,下面的一行就表示一个重做记录):

行号  事务id file#  block# row  column  value
1   T1   2       19   -   -    abc
2   T1   4       120   1   2    cdf

这时假设session 2发出其他更新语句:update t set c1=10 where c1=9;

同样的道理,Oracle找到该数据块(假设为[file#5,blk#200])放入buffer cache,并找到回滚段数据块(假设为[file#2,blk#30])存放旧值,生成重做记录,更新表的数据块,再次生成重做记录。这时日志缓冲区的结构类似如下形式:

行号  事务id file#  block# row  column  value
1    T1   2      19   -   -    abc
2   T1   4      120   1   2    cdf
3   T20   2      30   -   -    9
4   T20   5      200   20   1    10

这时,session 1又发出更新语句:update redo_test set name='xyz1' where id=2,并提交(commit)。同样的方式处理回滚段和数据块,并生成重做记录。假设这时生成日志缓冲区为:

行号  事务id file#  block# row  column  value
1   T1   2       19   -   -    abc
2   T1   4       120  1   2    cdf
3   T20   2       30   -   -    9
4   T20   5       200  20   1    10
5   T1   2       19   -   -    abc
6   T1   4       120  2   2    xyz1
7   T1  commit   SCN  timestamp

这时我们可以注意到,提交标记也被记录到了重做记录中。每次提交时,都会生成一个SCN号,SCN号越小,说明发生得越早,其所属的重做记录就越排在前面。一旦用户发出commit语句,系统就会触发LGWR进程。这时,LGWR进程会将上面所显示的所有重做记录都写入联机日志文件中。注意,其中也包括尚未提交的事务T20。

SCN号就是Oracle数据库内部的原子钟,可以认为是精确到秒后9位小数的时间信息。SCN号记录了数据库内部各个事件发生的先后顺序,比如DML、commit、DBWn写脏块等都会引起SCN号的增加。

在LGWR写这些重做记录的过程中,又有其他session 发出更新语句,并提交。这时的日志缓冲区假设如下所示:

行号  事务id file#  block# row  column  value
1   T1   2       19   -   -    abc
2   T1   4       120  1   2    cdf
3   T20   2       30   -   -    9
4   T20   5       200  20   1    10
5   T1   2       19   -   -    abc
6   T1   4       120  2   2    xyz1
7   T1  commit  SCN  timestamp
以上的重做日志正在由LGWR写入, 在LGWR写时生成以下的重做日志
-----------------------------------------------------
--------------
8   T20   2    39   -   -    289  
9   T20   5    498  220   3    190
10   T9   2    90   -   -    hhh
11   T9   9    100  20   9    xxx
12   T9   commit   SCN  timestamp
13   T18   2    189  -   -    18
14   T18   10    29   300   10    20
15   T18  commit   SCN  timestamp

当LGWR写完第一批重做记录(第1到第7行)以后,就会立即开始写第二批重做记录(第8行到第15行)。注意,第二批重做记录中,存在两个commit,但LGWR不会分成两次来写,而是一次就将它们全部写入。当LGWR在写完第1到第7行的改动向量以后,这部分的日志缓冲区内存就被释放了,可以被新生成的重做记录所覆盖。

5.3.3  log buffer的设置 

对于日志缓冲区来说,设置过小,容易引起log buffer space等待事件。但也不是说设置得越大就越好的,设置过大,由于LGWR进程会不断启动刷新日志缓冲区从而释放内存,所以可能会根本用不上多余的内存,从而浪费内存。

设置合适的日志缓冲区大小,目的是为了能够让LGWR进程合理地触发。理想情况下是,一方面,在LGWR进程向联机日志文件中写重做记录时,日志缓冲区中还是有剩余的可用空间以供其他进程所使用;另一方面,当LGWR进程完成时,日志缓冲区中的剩余可用空间不要很多,因为这时由LGWR所写入日志文件的日志块就可以释放出来了,成为新的剩余可用空间。然后,LGWR可以再次启动刷新脏的日志块。如此良性循环,就能在满足性能的前提下,充分利用日志缓冲区。没必要盲目地把日志缓冲区设置得很大,完全可以把节省下来的内存交给比如数据块缓冲区(buffer cache)等这样更需要内存的组件。

我们已经知道,当重做记录达到日志缓冲区的1/3或1MB时,就会触发LGWR进程。也就是说,Oracle默认认为LGWR进程在写日志缓冲区大小的1/3或1MB的重做记录的过程中,剩下的日志缓冲区可以供新的重做记录的需要。当LGWR写完以后,那么这1/3或1MB的日志缓冲区就又可以成为可用的日志块以容纳新的重做记录了。由此,我们可以很容易推导出,当我们设置日志缓冲区达到3MB(3×1MB)以上时,这时多余出来的日志缓冲区实际上并不能用得上,换句话说,多余出来的内存就被我们浪费了。

在设置日志缓冲区时,可以参考下面这个建议的公式来计算:1.5×(平均每个事务所产生的重做记录大小×每秒提交的事务数量)。

首先先找到总事务量是多少:

select a.value as trancount from v$sysstat a,v$statname b
where a.statistic# = b.statistic# and b.name = 'user commits';

然后,找到系统总共的运行时间:

select trunc(sysdate - startup_time)*24*60*60 as
seconds from v$instance;

第三,找到产生的所有重做记录大小:

select value as redoblocks from v$sysstat where name =
'redo blocks written';

最后,我们可以分别计算公式中的值:平均每个事务所产生的重做记录大小= redoblocks/trancount;每秒提交的事务数量=trancount/seconds。这样,最后所建议的日志缓冲区的大小可以写为:1.5×(redoblocks/trancount)×(trancount/seconds)。



原文整理自网络

5.3  深入Log Buffer  

数据库在运行过程中,不可避免地要遇到各种能够导致数据库损坏的情况。比如突然断电、Oracle或者操作系统的程序bug导致数据库内部逻辑结构损坏、磁盘介质损坏等,都有可能造成数据库崩溃,从而导致数据丢失的现象发生。

为了避免,或者说为了修复这些状况所导致的数据丢失现象,Oracle引入了日志缓冲区和日志文件的概念。所谓日志,就是将数据库中所有改变数据块的操作,都原原本本地记录下来。这些改变数据块的操作不仅包括对数据表的DML命令或者引起数据字典内容变化的DDL命令,还包括对索引的改变、对回滚段数据块的改变等。只有将数据库中所有的变化都记录下来,当发生数据库损坏时,才能够通过重新应用这些变化,从而达到恢复数据库的目的。
既然是要记录,那就必然引出一个问题,就是如何记录这些变化?比较容易想到的有两种方式。

第一种是使用逻辑的记录方式,也就是用描述性的语句来记录整个变化过程。比如对于某个update更新操作来说来说,可以记录为两条语句:delete 旧值以及insert 新值。这种方式的优点是非常节省空间,因为对每个操作,只需要记录几条逻辑上的语句即可。但是缺点也很明显,就是一旦需要进行恢复,就会非常消耗资源。设想一下,某个update操作更新了非常多的数据块,由于buffer cache内存有限,很多脏数据块都已经写入了数据文件。但就在更新快结束时,突然发生断电,所做的更新丢失。那么重新启动实例时,Oracle需要应用日志文件里的记录,于是重新发出delete旧值以及insert新值的语句。这个过程需要重新查找数据文件中符合条件的数据块,然后再挑出来进行更新。这个过程将非常消耗时间,而且会占用大量的buffer cache。

第二种方式是使用物理的记录方式,也就是将每个数据块改变前的镜像和改变后的镜像都记录下来。这种方式优点就是恢复起来速度非常快,直接根据日志文件里所记录的数据块地址和内容更新数据文件中对应的数据块。但是缺点也很明显,就是非常占用磁盘空间。

而Oracle在记录日志的方式上,采用了逻辑和物理相结合的方式。也就是说,Oracle针对每个数据块,记录了插入某个值或者删除某个值的描述语句。假如某个update更新了100个数据块,则Oracle会针对每个数据块记录一对delete 旧值和insert 新值的语句,共有100对这样的描述语句。在每一对描述语句中,都记录了相关数据块的物理地址。通过这种逻辑与物理相结合的方式,Oracle在记录变化时能够尽量节省空间,同时在应用变化时,又能比较快速。

为了临时存放所产生的日志信息,Oracle在SGA中开辟了一块内存区域。这块区域就叫做日志缓冲区(log buffer),当满足一定条件以后,Oracle会使用名为LGWR的后台进程将log buffer中的日志信息写入联机日志文件里。


可以使用初始化参数log_buffer来设置日志缓冲区的大小,单位是字节。日志缓冲区会进一步细分为多个块,每个块的尺寸与操作系统的一个块的尺寸相同,基本都是512字节。我们可以用如下方式来获得日志缓冲区的块尺寸。

SQL> select distinct lebsz as redo_block_size from x$kccle;
REDO_BLOCK_SIZE
---------------
512

也可以用下面的方式来计算出日志缓冲区的块尺寸。

SQL> select round((a.redosize+b.redowast)/c.redoblks) + 16 as
redo_block_size from
2  (select value redosize from v$sysstat where name='redo
size') a,
3  (select value redowast from v$sysstat where name='redo
wastage') b,
4  (select value redoblks from v$sysstat where name='redo
blocks written') c;
REDO_BLOCK_SIZE
---------------
512

日志缓冲区只是日志信息临时存放的区域,这块区域是有限的,而且其中的每个块都是能够循环使用的。这也就说明,日志缓冲区中的内容必须要写入磁盘的文件里,才能永久保留下来,才能在数据库崩溃时能够用来进行恢复。这个文件就叫做联机日志文件。在每个日志缓冲区中的日志块被重用之前,其内容必然已经被写入了磁盘上的联机日志文件中。

联机日志文件就是日志缓冲区的完全副本,组成日志文件的每个日志块的内容都来自于日志缓冲区的日志块。每个日志缓冲区中的日志块都对应到日志文件中的一个日志块。日志缓冲区中的日志块按照发生的先后顺序,放入联机日志文件。

由于日志文件在故障恢复中的重要性,建议至少使用两个日志文件组成一个日志文件组。同一个日志文件组中的日志文件内容一模一样,因为日志缓冲区中的日志块同时会写入日志文件组中的每个日志文件中。每个数据库都必须至少拥有两个日志文件组。这是由于只要数据库一天不停止运行,就会不断产生日志信息,就会不断写入联机日志文件,联机日志文件总会有写满的时候。我们不可能让联机日志文件无限大,也不可能放无限多的联机日志文件,所以联机日志文件必须是循环使用的,在若干个日志文件中轮流的进行写入。一个日志文件写满以后转换到另外一个日志文件继续写的过程叫做日志切换(log switch)。

当一个联机日志文件写满时,可以选择将其归档为脱机日志文件,通常叫做归档日志文件。归档也就是副本,归档的过程也就是将写满的联机日志文件复制到预先指定的目录的过程。只有当一个联机日志文件完成归档以后,该联机日志文件才能够被再次循环使用。强烈建议在生产库中选择这种归档方式,只有在测试环境中可以选择不归档。
可以说,日志缓冲区和日志文件存在的唯一目的就是为了保证被修改的数据不会被丢失。反过来说,也就是为了能够在数据库崩溃的时候,可以用来将数据库恢复到崩溃的那个时间点上。这也就是说,只有将被修改的数据块的日志信息写入了联机日志文件以后,该被修改的数据块才可以说是安全的。如果日志信息在没有被写入日志文件时发生实例崩溃,这时对数据的修改仍将丢失。由此我们可以看出,将日志缓冲区中的日志信息写入日志文件是一个非常重要的过程,这个过程是由一个名为LGWR的后台进程完成的。LGWR 承担了维护系统数据完整性的任务,它保证了数据在任何情况下都不会丢失。

触发LGWR进程将日志缓冲区中的日志信息写入联机日志文件条件包括以下几种。


 
前台进程触发,包括两种情况。

最显而易见的一种情况就是用户发出commit或rollback语句进行提交时,需要触发LGWR将内存里的日志信息写入联机日志文件,因为提交的数据必须被保护而不被丢失;

另外一种情况就是在日志缓冲区中找不到足够的内存来放日志信息时,也会触发LGWR进程将一些日志信息写入联机日志文件以后,从而释放一些空间。
 每隔三秒钟,LGWR启动一次。
 在DBWn启动时,如果发现脏数据块所对应的重做条目还没有写入联机日志文件,则DBWn触发LGWR进程并等待LRWR写完以后才会继续。
 日志信息的数量达到整个日志缓冲区的1/3时,触发LGWR。
 日志信息的数量达到1MB时,触发LGWR。

5.3.1  log buffer的内存结构 

我们已经知道,日志缓冲区用来存放事务对数据块的变化的日志信息。那么这里的日志信息到底包含哪些内容,是由哪些结构组成的呢?

Oracle记录数据库变化(也就是记录日志信息)的最小单位是改动向量(change vector)改动向量用来描述对数据库中任何单个数据块所做的一次改动。改动向量的内容包括:被改动的数据块的版本号事务操作代码被改动的数据块的地址等。这里的版本号非常重要,它能够帮助数据块始终能够体现当前最新的状态。Oracle在建立改动向量时,会从数据块中复制其版本号。而当恢复期间,Oracle读取改动向量并将改动应用于相应的数据块以后,被恢复的数据块的版本号加1。这里的数据块可以属于表或数据索引,也可以属于回滚段。但是对于临时表空间里的临时段,不会生成改动向量。

当多个改动向量按照先后顺序组合在一起,从而完成对数据库的一次改动时,Oracle称这组改动向量为重做记录(redo record)。重做记录用来描述对数据库的一个原子改动。所谓原子改动,就是说,当应用改动中的改动向量时,要么全部成功,要么全部失败,不存在部分成功部分失败的情况。重做记录能够帮助整个数据库体现当前最新的状态。

一个事务至少产生一个重做记录,也可能产生多个重做记录。而Oracle在应用日志记录进行恢复的过程中,以事务作为恢复的最小单位。要么恢复整个事务,要么回滚整个事务。也就是说,要么运用事务重做记录里的所有改动向量,要么一个改动向量都不运用。

因此,日志缓冲区就是许多重做记录按照发生的先后顺序组成的。同时,日志文件也就是由许多重做记录按照先后顺序排列在一起而组成的文件。

我们举个实例来说明重做记录和改动向量产生的过程。比如我们发出如下更新语句(假设表redo_test的NAME列上没有建立索引):

SQL> select * from redo_test;
ID     NAME
----- ----------
1      abc
2      abc
SQL> update redo_test set name='cdf' where id=1;

该语句发出以后,会产生一个重做记录,用来描述对表中数据块进行的修改。包括下面三个改动向量。


 
对回滚段(也就是undo段)事务表的改动,这发生在回滚段段头。事务表中包含被修改的数据块的地址该事务的状态(commit或active)以及存有该事务所使用的回滚段的地址。如果事务表被修改,就会产生针对它的改动向量。
 对回滚段数(undo段)据块的改动。将修改前的旧值(abc)存放到回滚段的数据块里。这时回滚段发生改变,于是产生改动向量。
 对redo_test表的数据块所做的改动。将修改后的新值(cdf)存放到表的数据块里。这时数据块发生改变,于是产生改动向量。

从这个过程可以看到,对于这个update事务,重做记录中会有三个改动向量。当然可能还有其他情况会产生新的重做记录,比如修改的列如果有索引,则必须修改索引。这时就会产生第二个重做记录,用来描述对索引数据块的修改。这时候的重做记录还是和第一个重做记录一样,包含多个改动向量。

此外,在事务完成之后运行commit或rollback语句时,就会产生第三个重做记录。该重做记录只有一个改动向量,用来记录对回滚段事务表的更改,因为commit或rollback时,需要更新事务表里记录的该事务的状态。

5.3.2  log buffer的内部管理机制  

日志缓冲区的内部管理分为两部分,一部分是生成重做记录,另一部分就是重做记录写入联机日志文件。这两部分不是孤立的,没有关联的。在生成重做记录的过程中,可能会触发LGWR将重做记录写入联机日志文件。

我们先用一个例子来说明在日志缓冲区中的操作过程,并使用[file# , blk#]来表示某个数据块;file#表示文件号;blk#表示数据块号。

假设session 1发出更新语句:update redo_test set name='cdf' where id=1;

Oracle首先找出id=1所在的数据块(假设为[file#4,blk#120])放入buffer cache,然后找出一个可用的回滚段数据块(假设为[file#2,blk#19]),将旧值'abc'放入该块,同时生成重做记录。然后将'cdf'放入表的数据块,再生成重做记录。这时日志缓冲区的结构可以简单地表示为下面的形式(我们在前面描述日志缓冲区的内存结构时,知道重做记录中最重要的就下面列的这几列内容。同时,下面的一行就表示一个重做记录):

行号  事务id file#  block# row  column  value
1   T1   2       19   -   -    abc
2   T1   4       120   1   2    cdf

这时假设session 2发出其他更新语句:update t set c1=10 where c1=9;

同样的道理,Oracle找到该数据块(假设为[file#5,blk#200])放入buffer cache,并找到回滚段数据块(假设为[file#2,blk#30])存放旧值,生成重做记录,更新表的数据块,再次生成重做记录。这时日志缓冲区的结构类似如下形式:

行号  事务id file#  block# row  column  value
1    T1   2      19   -   -    abc
2   T1   4      120   1   2    cdf
3   T20   2      30   -   -    9
4   T20   5      200   20   1    10

这时,session 1又发出更新语句:update redo_test set name='xyz1' where id=2,并提交(commit)。同样的方式处理回滚段和数据块,并生成重做记录。假设这时生成日志缓冲区为:

行号  事务id file#  block# row  column  value
1   T1   2       19   -   -    abc
2   T1   4       120  1   2    cdf
3   T20   2       30   -   -    9
4   T20   5       200  20   1    10
5   T1   2       19   -   -    abc
6   T1   4       120  2   2    xyz1
7   T1  commit   SCN  timestamp

这时我们可以注意到,提交标记也被记录到了重做记录中。每次提交时,都会生成一个SCN号,SCN号越小,说明发生得越早,其所属的重做记录就越排在前面。一旦用户发出commit语句,系统就会触发LGWR进程。这时,LGWR进程会将上面所显示的所有重做记录都写入联机日志文件中。注意,其中也包括尚未提交的事务T20。

SCN号就是Oracle数据库内部的原子钟,可以认为是精确到秒后9位小数的时间信息。SCN号记录了数据库内部各个事件发生的先后顺序,比如DML、commit、DBWn写脏块等都会引起SCN号的增加。

在LGWR写这些重做记录的过程中,又有其他session 发出更新语句,并提交。这时的日志缓冲区假设如下所示:

行号  事务id file#  block# row  column  value
1   T1   2       19   -   -    abc
2   T1   4       120  1   2    cdf
3   T20   2       30   -   -    9
4   T20   5       200  20   1    10
5   T1   2       19   -   -    abc
6   T1   4       120  2   2    xyz1
7   T1  commit  SCN  timestamp
以上的重做日志正在由LGWR写入, 在LGWR写时生成以下的重做日志
-----------------------------------------------------
--------------
8   T20   2    39   -   -    289  
9   T20   5    498  220   3    190
10   T9   2    90   -   -    hhh
11   T9   9    100  20   9    xxx
12   T9   commit   SCN  timestamp
13   T18   2    189  -   -    18
14   T18   10    29   300   10    20
15   T18  commit   SCN  timestamp

当LGWR写完第一批重做记录(第1到第7行)以后,就会立即开始写第二批重做记录(第8行到第15行)。注意,第二批重做记录中,存在两个commit,但LGWR不会分成两次来写,而是一次就将它们全部写入。当LGWR在写完第1到第7行的改动向量以后,这部分的日志缓冲区内存就被释放了,可以被新生成的重做记录所覆盖。

5.3.3  log buffer的设置 

对于日志缓冲区来说,设置过小,容易引起log buffer space等待事件。但也不是说设置得越大就越好的,设置过大,由于LGWR进程会不断启动刷新日志缓冲区从而释放内存,所以可能会根本用不上多余的内存,从而浪费内存。

设置合适的日志缓冲区大小,目的是为了能够让LGWR进程合理地触发。理想情况下是,一方面,在LGWR进程向联机日志文件中写重做记录时,日志缓冲区中还是有剩余的可用空间以供其他进程所使用;另一方面,当LGWR进程完成时,日志缓冲区中的剩余可用空间不要很多,因为这时由LGWR所写入日志文件的日志块就可以释放出来了,成为新的剩余可用空间。然后,LGWR可以再次启动刷新脏的日志块。如此良性循环,就能在满足性能的前提下,充分利用日志缓冲区。没必要盲目地把日志缓冲区设置得很大,完全可以把节省下来的内存交给比如数据块缓冲区(buffer cache)等这样更需要内存的组件。

我们已经知道,当重做记录达到日志缓冲区的1/3或1MB时,就会触发LGWR进程。也就是说,Oracle默认认为LGWR进程在写日志缓冲区大小的1/3或1MB的重做记录的过程中,剩下的日志缓冲区可以供新的重做记录的需要。当LGWR写完以后,那么这1/3或1MB的日志缓冲区就又可以成为可用的日志块以容纳新的重做记录了。由此,我们可以很容易推导出,当我们设置日志缓冲区达到3MB(3×1MB)以上时,这时多余出来的日志缓冲区实际上并不能用得上,换句话说,多余出来的内存就被我们浪费了。

在设置日志缓冲区时,可以参考下面这个建议的公式来计算:1.5×(平均每个事务所产生的重做记录大小×每秒提交的事务数量)。

首先先找到总事务量是多少:

select a.value as trancount from v$sysstat a,v$statname b
where a.statistic# = b.statistic# and b.name = 'user commits';

然后,找到系统总共的运行时间:

select trunc(sysdate - startup_time)*24*60*60 as
seconds from v$instance;

第三,找到产生的所有重做记录大小:

select value as redoblocks from v$sysstat where name =
'redo blocks written';

最后,我们可以分别计算公式中的值:平均每个事务所产生的重做记录大小= redoblocks/trancount;每秒提交的事务数量=trancount/seconds。这样,最后所建议的日志缓冲区的大小可以写为:1.5×(redoblocks/trancount)×(trancount/seconds)。


相关文章
|
18天前
|
缓存 NoSQL Java
千万级电商线上无阻塞双buffer缓冲优化ID生成机制深度解析
【11月更文挑战第30天】在千万级电商系统中,ID生成机制是核心基础设施之一。一个高效、可靠的ID生成系统对于保障系统的稳定性和性能至关重要。本文将深入探讨一种在千万级电商线上广泛应用的ID生成机制——无阻塞双buffer缓冲优化方案。本文从概述、功能点、背景、业务点、底层原理等多个维度进行解析,并通过Java语言实现多个示例,指出各自实践的优缺点。希望给需要的同学提供一些参考。
40 7
|
2月前
|
存储 监控 安全
深入解析Sysmon日志:增强网络安全与威胁应对的关键一环
在不断演进的网络安全领域中,保持对威胁的及时了解至关重要。Sysmon日志在这方面发挥了至关重要的作用,通过提供有价值的见解,使组织能够加强其安全姿态。Windows在企业环境中是主导的操作系统,因此深入了解Windows事件日志、它们的独特特性和局限性,并通过Sysmon进行增强,变得至关重要。
|
2月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。
|
2月前
|
存储 关系型数据库 MySQL
MySQL中的Redo Log、Undo Log和Binlog:深入解析
【10月更文挑战第21天】在数据库管理系统中,日志是保障数据一致性和完整性的关键机制。MySQL作为一种广泛使用的关系型数据库管理系统,提供了多种日志类型来满足不同的需求。本文将详细介绍MySQL中的Redo Log、Undo Log和Binlog,从背景、业务场景、功能、底层实现原理、使用措施等方面进行详细分析,并通过Java代码示例展示如何与这些日志进行交互。
192 0
|
3月前
|
存储 缓存 关系型数据库
redo log 原理解析
redo log 原理解析
51 0
redo log 原理解析
|
4月前
|
存储 运维 监控
深入Linux核心:文件系统与日志解析
【8月更文挑战第20天】
83 2
|
4月前
|
Java Shell Linux
【Linux入门技巧】新员工必看:用Shell脚本轻松解析应用服务日志
关于如何使用Shell脚本来解析Linux系统中的应用服务日志,提供了脚本实现的详细步骤和技巧,以及一些Shell编程的技能扩展。
64 0
【Linux入门技巧】新员工必看:用Shell脚本轻松解析应用服务日志
|
4月前
|
数据库 Java 监控
Struts 2 日志管理化身神秘魔法师,洞察应用运行乾坤,演绎奇幻篇章!
【8月更文挑战第31天】在软件开发中,了解应用运行状况至关重要。日志管理作为 Struts 2 应用的关键组件,记录着每个动作和决策,如同监控摄像头,帮助我们迅速定位问题、分析性能和使用情况,为优化提供依据。Struts 2 支持多种日志框架(如 Log4j、Logback),便于配置日志级别、格式和输出位置。通过在 Action 类中添加日志记录,我们能在开发过程中获取详细信息,及时发现并解决问题。合理配置日志不仅有助于调试,还能分析用户行为,提升应用性能和稳定性。
61 0
|
4月前
|
SQL 安全 测试技术
【数据守护者必备】SQL数据备份与恢复策略全解析:从全量到日志备份,手把手教你确保企业信息万无一失的实战技巧!
【8月更文挑战第31天】数据库是企业核心业务数据的基石,为防止硬件故障、软件错误或人为失误导致的数据丢失,制定可靠的备份与恢复策略至关重要。本文通过一个在线购物平台的案例,详细介绍了使用 SQL Server 进行全量备份、差异备份及事务日志备份的方法,并演示了如何利用 SQL Server Agent 实现自动化备份任务。此外,还提供了数据恢复的具体步骤和测试建议,确保数据安全与业务连续性。
205 0
|
5月前
|
数据采集 分布式计算 DataWorks
DataWorks产品使用合集之任务工作流中遇到了日志信息显示参数值没有正确解析的问题,该如何处理
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。

推荐镜像

更多