Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5351595
  • 博文数量: 1144
  • 博客积分: 11974
  • 博客等级: 上将
  • 技术积分: 12312
  • 用 户 组: 普通用户
  • 注册时间: 2005-04-13 20:06
文章存档

2017年(2)

2016年(14)

2015年(10)

2014年(28)

2013年(23)

2012年(29)

2011年(53)

2010年(86)

2009年(83)

2008年(43)

2007年(153)

2006年(575)

2005年(45)

分类: Oracle

2011-06-27 07:48:54

揭示检查点的秘密

oracle 8以前都是完全检查点,每次触发,则记录当前数据库SCN号,并将内存里所有截至到该SCN号的脏数据块都刷新到数据文件里。从而同步数据文件。

随着内存越来越便宜,以及控制实例恢复的需要,完全检查点已经不适合实际情况了。于是从oracle8开始,oracle引入了增量检查点,通过增量检查点来减少实例恢复的时间。为此,oracle引入了checkpointqueue,按照数据块第一次被修改的先后顺序,将脏数据块挂到该队列上。同时还在该队列上记录了每个脏数据块第一次被修改时产生的redoentry的地址,这叫LRBA。

增量检查点的启动时机由以下参数控制:
·fast_start_mttr_target:应用从CKPT position到最后一个redo entry之间所有redo entry所花的时间。也就是实例前滚所花费的时间。
·LOG_CHECKPOINT_INTERVAL:如果自从上一次检查点启动以来所累积的日志块个数达到该参数,则触发增量检查点,从而触发DBWn写脏块。
·LOG_CHECKPOINT_TIMEOUT:两次检查点启动的时间间隔。实际也就是控制脏块在内存里待多长时间的参数。
·90%*(SUM(log_size) – MAX(log_size)):如果产生的redo块的量达到该值,则启动增量检查点。

只要满足上面条件中的任何一个,就会启动增量检查点。增量检查点一旦启动,则会记录当前CKPT SCN。
然后通知DBWn进程去写checkpoint queue上所挂载的脏块,至于写到哪个脏块为止,则不超过到当前CKPT SCN。
DBWn进程是批量写脏块的,脏块个数不到一定程度,DBWn不会真的去写。而每次写多少个脏块的个数则由一个隐藏参数控制。

每隔3秒钟(这叫heartbeat,心跳),“增量检查点”也还会启动一次,只不过这时增量检查点启动只是查看一下checkpointqueue,将该队列上的第一个脏块所对应的redo block的地址写入控制文件,也就是将CKPTPosition写入控制文件,并不会触发DBWn进程写脏块。所以是打引号的增量检查点。

有关检查点更详细的信息,google、baidu等都可以搜到,这里不再赘述。

10g版本中,完全检查点也存在,其触发时机:
1、发出命令:alter system checkpoint;
2、正常关闭数据库

有关检查点(包括完全检查点和增量检查点)的视图主要有两个:
1、v$instance_recovery。其中主要的字段包括:
·actual_redo_blks:最近一次检查点所对应的要写入的脏块所对应的redo block到写入了日志文件的最后一个redo block之间的redo blocks数量;
·target_redo_blks:所有检查点条件中最小的条件所导致的在内存里的redo blocks数量;
·log_file_size_redo_blks:其字段值为90%*(SUM(log_size) – MAX(log_size))的计算结果。
·log_chkpt_timeout_redo_blks:由log_checkpoint_timeout参数所得到的redo blocks数量。
·target_mttr:由fast_start_mttr_target参数所计算出来的实例恢复所需要的时间。
·estimated_mttr:根据当前最后一次检查点与日志尾所差的redo blocks数量估算出来的实例恢复所需要的时间。

2、X$KCCCP。该内存表的命名含义为:
[K]ernel [C]ache [C]ontrolfile management [c]heckpoint [p]rogress。
其中主要的字段包括:
·CPLRBA_SEQ:最后一次增量检查点对应LRBA的第一部分--日志序列号;
·CPLRBA_BNO:最后一次增量检查点对应LRBA的第二部分--日志块数;
·CPLRBA_BOF:最后一次增量检查点对应LRBA的第三部分--日志偏移量;
·CPODR_SEQ:已写入日志文件的最后一个RBA的第一部分--日志序列号;
·CPODR_BNO:已写入日志文件的最后一个RBA的第二部分--日志块数;
·CPODR_BOF:已写入日志文件的最后一个RBA的第二部分--日志偏移量;
·CPHBT:每三秒更新一次,并写入控制文件的heartbeat部分。表示这时的SCN号。
换句话所,当进行实例恢复时,需要应用的redo entry从CPLRBA_*到CPODR_*为止。

一、完全检查点
SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        37      97550         37      97778

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

  COUNT(*)
----------
        63

SQL> alter system checkpoint;

系统已更改。

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

  COUNT(*)
----------
         0

很明显,发出该命令以后,内存里所有脏块都被写入了数据文件,于是内存里就没有脏块了。
打开跟踪文件,可以看到:
Beginning global checkpoint up to RBA [0x25.17df2.10], SCN: 662364
Completed checkpoint up to RBA [0x25.17df2.10], SCN: 662364

SQL> select to_number('25','xx') from dual;

TO_NUMBER('25','XX')
--------------------
                  37

SQL> select to_number('17df2','xxxxxxxx') from dual;

TO_NUMBER('17DF2','XXXXXXXX')
-----------------------------
                        97778

将其与x$kcccp的查询结果比较一下,也可以看到,在完全检查点中,截至写入数据文件的脏块所对应的redo entry对应到STOP_SEQ#和STOP_BLK#所指明的redo entry为止。

二、增量检查点
测试增量检查点,主要通过v$instance_recovery视图。
我们通常不会去修改log_checkpoint和fast_start参数,那些参数的默认值都设置的很大,所以还没到那些参数所指定的上限的时候,90%这个条件就生效了。因为增量检查点在任何一个条件满足就会被触发。
按照前面的公式:90%*(SUM(log_size) – MAX(log_size)),我们来计算日志量是多大。
SQL> select 0.90*(sum(bytes)/1024/1024-max(bytes/1024/1024)) from v$log;

0.90*(SUM(BYTES)/1024/1024-MAX(BYTES/1024/1024))
------------------------------------------------
                                             180
可以看到,只要日志量达到180M,就会发生增量检查点。然后来验证:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
              89           368640     368640                        0             27
这里368640表示redo block的个数,至于每个redo block多大,则通常为500 bytes。也可以通过下面的sql来验证:
SQL> select distinct lebsz as redo_block_size from x$kccle where lebsz>0;

REDO_BLOCK_SIZE
---------------
            512

于是,我们得到:368640*512/1024/1024=180M。很明显,与前面的公式吻合。
同时,我们看到当前内存里的脏块所对应的redo block有89个。而其他控制增量检查点的参数都保持缺省值:
SQL> show parameter log_checkpoint

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_checkpoint_interval              integer     0
log_checkpoint_timeout               integer     1800
log_checkpoints_to_alert             boolean     TRUE
SQL> show parameter fast_start

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fast_start_io_target                 integer     0
fast_start_mttr_target               integer     0
fast_start_parallel_rollback         string      LOW

假设当前所在的session为sess #1。然后再启动一个session,假设其session为sess #2。发出大量的DML语句。
SQL> connect hr/hr
已连接。
SQL> create table test as select * from dba_objects;

表已创建。

回到sess #1,查看v$instance_recovery:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
             308           368640     368640                        0             22
可以看到,只产生了大约300多个redo block。于是回到sess #2,不断的做insert+delete:
SQL> insert into test select * from test;

已创建50320行。

SQL> commit;

提交完成。

SQL> delete test;

已删除100640行。

SQL> commit;

提交完成。

大概做3、4次左右,回到sess #1,查看v$instance_recovery。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
          341604           368640     368640                        0            105

这时内存里的脏数据块所对应的redo block已经接近上限了。于是再次回到sess #2里做DML,结束以后回到sess #1查看。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
          378157           368640     368640                        0            184
已经超过上限,于是触发增量检查点。这时可以去查看alert log:
Sun Nov 23 12:47:47 2008
Beginning log switch checkpoint up to RBA [0x26.2.10], SCN: 667801
Thread 1 advanced to log sequence 38
  Current log# 6 seq# 38 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO06.LOG
Sun Nov 23 12:48:47 2008
Beginning log switch checkpoint up to RBA [0x27.2.10], SCN: 677714
Thread 1 advanced to log sequence 39
  Current log# 4 seq# 39 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO04.LOG
很明显,可以看到触发了2次增量检查点,第一次增量检查点要写到[0x26.2.10]为止,第二次要写到[0x27.2.10]为止。同时这两次增量检查点都还没有完成,因为在checkpoint queue上,截至到该RBA的redoentry所对应的脏块还没有被写入数据文件。增量检查点只是通知DBWn写到这个位置并不等DBWn写完就返回了。这里的26对应到十进制就是:2*16+6=38,也就是写到序列号为38为止。而27则对应到39号日志文件。
SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        37     108095         39      85497

SQL> select group#,sequence#,status from v$log;

    GROUP#  SEQUENCE# STATUS
---------- ---------- ----------------
         4         39 CURRENT
         5         37 ACTIVE
         6         38 ACTIVE

可以看到37、38这两个日志所对应的脏块还没有被写入数据文件,所以其状态为active。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
          368599           368640     368640                        0            161

同时也发现,内存里的脏块所对应的redo block个数从378157下降到了368599,也就是只减少了很少的redoblock,换句花说,这时DBWn进程只写了很少的几个脏块。因为写的脏块个数只需要满足不触发增量检查点即可,这里的条件也就是内存里的脏块所对应的redo block不要超过368640即可。

然后再次的在sess #2里做DML。然后回到sess #1。
SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        38      22297         39     195257
可以看到发生变化了,checkpoint position从37号日志向前推进到了38号日志。也就是说,完成了一次检查点。
马上去看alert log,发现下面这一段:
Thread 1 advanced to log sequence 39
  Current log# 4 seq# 39 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO04.LOG
Sun Nov 23 13:00:56 2008
Completed checkpoint up to RBA [0x26.2.10], SCN: 667801
Sun Nov 23 13:01:57 2008
Beginning log switch checkpoint up to RBA [0x28.2.10], SCN: 685552
Thread 1 advanced to log sequence 40
  Current log# 5 seq# 40 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO05.LOG
很明显,第一次增量检查点结束了,也就是说,前面active的37号日志文件里的redo entry所对应的脏块已经被
写入了数据文件,其状态肯定变为inactive。同时第二次增量检查点还没有结束,同时又启动了第三次增量检查点。
SQL> select group#,sequence#,status from v$log;

    GROUP#  SEQUENCE# STATUS
---------- ---------- ----------------
         4         39 CURRENT
         5         37 INACTIVE
         6         38 ACTIVE

SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        38      22297         39     195257

整个过程是很容易理解的。这里可以很容易得出结论:通常所说的logfile switch 触发检查点,实际上就是触发增量检查点而不是完全检查点,并标记要写到哪个脏块为止。同时不等DBWn写完这些脏块就返回。当DBWn写完所标记的脏块以后,该增量检查点结束。

然后来修改log_checkpoint_timeout参数,从而调整增量检查点的频率。该参数表示脏数据块留在内存里的时间长度,一旦超过该时间,就会触发增量检查点,从而确定要写到的redo entry,从而确定了要写到的脏块地址,然后触发DBWn进程写脏块。将该参数设置为60秒。
SQL> alter system set log_checkpoint_timeout=60;

系统已更改。

SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
               6               40     368640           40           0             23

可以看到,修改为60秒以后,TARGET_REDO_BLKS为40,表示内存里只能容纳40个redo block所保护的脏数据块。然后在sess #2里进行DML以后,回到sess #1里查看相关视图。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
           22791            22791     368640        22791           0             25
等待1分钟左右,再次查询该视图,会发现其结果为:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
              73               75     368640           75           0             22
很明显,1分钟以后,大部分脏块被增量检查点触发DBWn写入数据文件。

然后来测试fast_start_mttr_target参数。
SQL> alter system set fast_start_mttr_target=40;

系统已更改。

SQL> show parameter log_checkpoint

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_checkpoint_interval              integer     0
log_checkpoint_timeout               integer     0
log_checkpoints_to_alert             boolean     TRUE
SQL> show parameter fast_start

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fast_start_io_target                 integer     0
fast_start_mttr_target               integer     40
fast_start_parallel_rollback         string      LOW

设置完fast_start_mttr_target=40以后,立刻查看相关视图:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
             104           368640     368640                       40             22

可以看到,TARGET_MTTR就是我们设置的40秒,而ESTIMATED_MTTR则表示由于当前内存里脏块很少,因此如果当前实例崩溃,则只需要22秒就能完成实例的恢复。

在sess #2里进行DML以后,马上到sess #1里查看相关视图。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
           67141           368640     368640                       40             42

可以看到这时sess #2产生了67141个redo block,这将触发增量检查点。然后我们间隔很短的时间,比如30秒以后再次查询该视图。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
             132           368640     368640                       40             42

会发现,由于我们设置了40秒的实例恢复时间,因此马上就完成了增量检查点,减少了内存里脏数据块的个数。

阅读(855) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~