Oracle Redo log并行机制详解

Oracle的数据库日志称为Redo log,所有数据改变都记录Redo log,可以用于修复受损的数据库。Redo log 是用于恢复和一个高级特性的重要数据,一个redo条目包含了相应操作导致的数据库变化的所有信息,所有redo条目最终都要被写入redo文件中去。

Redo log buffer是为了避免Redo文件IO导致性能瓶颈而在sga中分配出的一块内存。一个redo条目首先在用户内存(PGA)中产生,然后由oracle服务进程拷贝到log buffer中,当满足一定条件时,再由LGWR进程写入redo文件。由于log buffer是一块“共享”内存,为了避免冲突,它是受到redo allocation latch保护的,每个服务进程需要先获取到该latch才能分配redo buffer。因此在高并发且数据修改频繁的oltp系统中,我们通常可以观察到redo allocation latch的等待。Redo写入redo buffer的整个过程如下:

在PGA中生产Redo Enrey -> 服务进程获取Redo Copy latch(存在多个—CPU_COUNT*2) -> 服务进程获取redo allocation latch(仅1个) -> 分配log buffer -> 释放redo allocation latch -> 将Redo Entry写入Log Buffer -> 释放Redo Copy latch;

shared strand

为了减少redo allocation latch等待,在oracle 9.2中,引入了log buffer的并行机制。其基本原理就是,将log buffer划分为多个小的buffer,这些小的buffer被成为strand(为了和之后出现的private strand区别,它们被称之为shared strand)。每一个strand受到一个单独redo allocation latch的保护。多个shared strand的出现,使原来序列化的redo buffer分配变成了并行的过程,从而减少了redo allocation latch等待。

shared strand的初始数据量是由参数log_parallelism控制的;在10g中,该参数成为隐含参数,并新增参数_log_parallelism_max控制shared strand的最大数量;_log_parallelism_dynamic则控制是否允许shared strand数量在_log_parallelism和_log_parallelism_max之间动态变化。

  1. HELLODBA.COM>select  nam.ksppinm, val.KSPPSTVL, nam.ksppdesc      
  2.   2  from    sys.x$ksppi nam,      
  3.   3          sys.x$ksppsv val      
  4.   4  where nam.indx = val.indx      
  5.   5  --AND   nam.ksppinm LIKE '_%'      
  6.   6  AND   upper(nam.ksppinm) LIKE '%LOG_PARALLE%';      
  7.      
  8. KSPPINM                    KSPPSTVL   KSPPDESC      
  9. -------------------------- ---------- ------------------------------------------      
  10. _log_parallelism           1          Number of log buffer strands      
  11. _log_parallelism_max       2          Maximum number of log buffer strands      
  12. _log_parallelism_dynamic   TRUE       Enable dynamic strands  

每一个shared strand的大小 = log_buffer/(shared strand数量)。strand信息可以由表x$kcrfstrand查到(包含shared strand和后面介绍的private strand,10g以后存在)。 

  1. HELLODBA.COM>select indx,strand_size_kcrfa from x$kcrfstrand where last_buf_kcrfa != '00';      
  2.      
  3.       INDX STRAND_SIZE_KCRFA      
  4. ---------- -----------------      
  5.          0           3514368      
  6.          1           3514368      
  7.      
  8. HELLODBA.COM>show parameter log_buffer      
  9.      
  10. NAME                                 TYPE        VALUE      
  11. ------------------------------------ ----------- ------------------------------      
  12. log_buffer                           integer     7028736    

关于shared strand的数量设置,16个cpu之内最大默认为2,当系统中存在redo allocation latch等待时,每增加16个cpu可以考虑增加1个strand,最大不应该超过8。并且_log_parallelism_max不允许大于cpu_count。

注意:在11g中,参数_log_parallelism被取消,shared strand数量由_log_parallelism_max、_log_parallelism_dynamic和cpu_count控制。

Private strand

为了进一步降低redo buffer冲突,在10g中引入了新的strand机制——Private strand。Private strand不是从log buffer中划分的,而是在shared pool中分配的一块内存空间。

  1. HELLODBA.COM>select * from V$sgastat where name like '%strand%';  
  2.  
  3.   POOL NAME BYTES  
  4.  
  5.   ------------ -------------------------- ----------  
  6.  
  7.   shared pool private strands 2684928  
  8.  
  9.   HELLODBA.COM>select indx,strand_size_kcrfa from x$kcrfstrand where last_buf_kcrfa = '00';  
  10.  
  11.   INDX STRAND_SIZE_KCRFA  
  12.  
  13.   ---------- -----------------  
  14.  
  15.   2 66560  
  16.  
  17.   3 66560  
  18.  
  19.   4 66560  
  20.  
  21.   5 66560  
  22.  
  23.   6 66560  
  24.  
  25.   7 66560  
  26.  
  27.   8 66560  
  28.  
  29.   ... 

Private strand的引入为Oracle的Redo/Undo机制带来很大的变化。每一个Private strand受到一个单独的redo allocation latch保护,每个Private strand作为“私有的”strand只会服务于一个活动事务。获取到了Private strand的用户事务不是在PGA中而是在Private strand生成Redo,当flush private strand或者commit时,Private strand被批量写入log文件中。如果新事务申请不到Private strand的redo allocation latch,则会继续遵循旧的redo buffer机制,申请写入shared strand中。事务是否使用Private strand,可以由x$ktcxb的字段ktcxbflg的新增的第13位鉴定:

  1. HELLODBA.COM>select decode(bitand(ktcxbflg, 4096),0,1,0) used_private_strand, count(*)  
  2.  
  3.   2 from x$ktcxb  
  4.  
  5.   3 where bitand(ksspaflg, 1) != 0  
  6.  
  7.   4 and bitand(ktcxbflg, 2) != 0  
  8.  
  9.   5 group by bitand(ktcxbflg, 4096);  
  10.  
  11.   USED_PRIVATE_STRAND COUNT(*)  
  12.  
  13.   ------------------- ----------  
  14.  
  15.   1 10  
  16.  
  17.   0 1 

对于使用Private strand的事务,无需先申请Redo Copy Latch,也无需申请Shared Strand的redo allocation latch,而是flush或commit是批量写入磁盘,因此减少了Redo Copy Latch和redo allocation latch申请/释放次数、也减少了这些latch的等待,从而降低了CPU的负荷。过程如下:

事务开始 -> 申请Private strand的redo allocation latch (申请失败则申请Shared Strand的redo allocation latch) -> 在Private strand中生产Redo Enrey -> Flush/Commit -> 申请Redo Copy Latch -> 服务进程将Redo Entry批量写入Log File -> 释放Redo Copy Latch -> 释放Private strand的redo allocation latch 。#p#

注意:对于未能获取到Private strand的redo allocation latch的事务,在事务结束前,即使已经有其它事务释放了Private strand,也不会再申请Private strand了。

每个Private strand的大小为65K。10g中,shared pool中的Private strands的大小就是活跃会话数乘以65K,而11g中,在shared pool中需要为每个Private strand额外分配4k的管理空间,即:数量*69k。

  1. --10g:  
  2.  
  3.   SQL> select * from V$sgastat where name like '%strand%';  
  4.  
  5.   POOL NAME BYTES  
  6.  
  7.   ------------ -------------------------- ----------  
  8.  
  9.   shared pool private strands 1198080  
  10.  
  11.   HELLODBA.COM>select trunc(value * KSPPSTVL / 100) * 65 * 1024  
  12.  
  13.   2 from (select value from v$parameter where name = 'transactions') a,  
  14.  
  15.   3 (select val.KSPPSTVL  
  16.  
  17.   4 from sys.x$ksppi nam, sys.x$ksppsv val  
  18.  
  19.   5 where nam.indx = val.indx  
  20.  
  21.   6 AND nam.ksppinm = '_log_private_parallelism_mul') b;  
  22.  
  23.   TRUNC(VALUE*KSPPSTVL/100)*65*1024  
  24.  
  25.   -------------------------------------  
  26.  
  27.   1198080  
  28.  
  29.   --11g:  
  30.  
  31.   HELLODBA.COM>select * from V$sgastat where name like '%strand%';  
  32.  
  33.   POOL NAME BYTES  
  34.  
  35.   ------------ -------------------------- ----------  
  36.  
  37.   shared pool private strands 706560  
  38.  
  39.   HELLODBA.COM>select trunc(value * KSPPSTVL / 100) * (65 + 4) * 1024  
  40.  
  41.   2 from (select value from v$parameter where name = 'transactions') a,  
  42.  
  43.   3 (select val.KSPPSTVL  
  44.  
  45.   4 from sys.x$ksppi nam, sys.x$ksppsv val  
  46.  
  47.   5 where nam.indx = val.indx  
  48.  
  49.   6 AND nam.ksppinm = '_log_private_parallelism_mul') b;  
  50.  
  51.   TRUNC(VALUE*KSPPSTVL/100)*(65+4)*1024  
  52.  
  53.   -------------------------------------  
  54.  
  55.   706560 

Private strand的数量受到2个方面的影响:logfile的大小和活跃事务数量。

参数_log_private_mul指定了使用多少logfile空间预分配给Private strand,默认为5。我们可以根据当前logfile的大小(要除去预分配给log buffer的空间)计算出这一约束条件下能够预分配多少个Private strand:

  1. HELLODBA.COM>select bytes from v$log where status = 'CURRENT';  
  2.  
  3.   BYTES  
  4.  
  5.   ----------  
  6.  
  7.   52428800  
  8.  
  9.   HELLODBA.COM>select trunc(((select bytes from v$log where status = 'CURRENT') - (select to_number(value) from v$parameter where name = 'log_buffer'))*  
  10.  
  11.   2 (select to_number(val.KSPPSTVL)  
  12.  
  13.   3 from sys.x$ksppi nam, sys.x$ksppsv val  
  14.  
  15.   4 where nam.indx = val.indx  
  16.  
  17.   5 AND nam.ksppinm = '_log_private_mul') / 100 / 66560)  
  18.  
  19.   6 as "calculated private strands" 
  20.  
  21.   7 from dual;  
  22.  
  23.   calculated private strands  
  24.  
  25.   --------------------------  
  26.  
  27.   5  
  28.  
  29.   HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';  
  30.  
  31.   actual private strands  
  32.  
  33.   ----------------------  
  34.  
  35.   5 

当logfile切换后(和checkpoint一样,切换之前必须要将所有Private strand的内容flush到logfile中,因此我们在alert log中可能会发现日志切换信息之前会有这样的信息:”Private strand flush not complete”,这是可以被忽略的),会重新根据切换后的logfile的大小计算对Private strand的限制:

  1. HELLODBA.COM>alter system switch logfile;  
  2.  
  3.   System altered.  
  4.  
  5.   HELLODBA.COM>select bytes from v$log where status = 'CURRENT';  
  6.  
  7.   BYTES  
  8.  
  9.   ----------  
  10.  
  11.   104857600  
  12.  
  13.   HELLODBA.COM>select trunc(((select bytes from v$log where status = 'CURRENT') - (select to_number(value) from v$parameter where name = 'log_buffer'))*  
  14.  
  15.   2 (select to_number(val.KSPPSTVL)  
  16.  
  17.   3 from sys.x$ksppi nam, sys.x$ksppsv val  
  18.  
  19.   4 where nam.indx = val.indx  
  20.  
  21.   5 AND nam.ksppinm = '_log_private_mul') / 100 / 66560)  
  22.  
  23.   6 as "calculated private strands" 
  24.  
  25.   7 from dual;  
  26.  
  27.   calculated private strands  
  28.  
  29.   --------------------------  
  30.  
  31.   13  
  32.  
  33.   HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';  
  34.  
  35.   actual private strands  
  36.  
  37.   ----------------------  
  38.  
  39.   13 

参数_log_private_parallelism_mul用于推算活跃事务数量在最大事务数量中的百分比,默认为10。Private strand的数量不能大于活跃事务的数量。

  1. HELLODBA.COM>show parameter transactions  
  2.  
  3.   NAME TYPE VALUE  
  4.  
  5.   ------------------------------------ ----------- ------------------------------  
  6.  
  7.   transactions integer 222  
  8.  
  9.   transactions_per_rollback_segment integer 5  
  10.  
  11.   HELLODBA.COM>select trunc((select to_number(value) from v$parameter where name = 'transactions') *  
  12.  
  13.   2 (select to_number(val.KSPPSTVL)  
  14.  
  15.   3 from sys.x$ksppi nam, sys.x$ksppsv val  
  16.  
  17.   4 where nam.indx = val.indx  
  18.  
  19.   5 AND nam.ksppinm = '_log_private_parallelism_mul') / 100 )  
  20.  
  21.   6 as "calculated private strands" 
  22.  
  23.   7 from dual;  
  24.  
  25.   calculated private strands  
  26.  
  27.   --------------------------  
  28.  
  29.   22  
  30.  
  31.   HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';  
  32.  
  33.   actual private strands  
  34.  
  35.   ----------------------  
  36.  
  37.   22 

注:在预分配Private strand时,会选择上述2个条件限制下最小一个数量。但相应的shared pool的内存分配和redo allocation latch的数量是按照活跃事务数预分配的。

因此,如果logfile足够大,_log_private_parallelism_mul与实际活跃进程百分比基本相符的话,Private strand的引入基本可以消除redo allocation latch的争用问题。

【编辑推荐】

  1. 浅谈在Oracle中如何利用REDO实现故障恢复
  2. 简析REDO LOGFILE
  3. 详解Oracle中数字与大写交换
  4. 浅析Oracle对象和数据的导入导出
  5. 分析探讨Oracle Data Guard

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/233635.html<

(0)
管理的头像管理
上一篇2025-04-20 17:30
下一篇 2025-04-20 17:31

相关推荐

  • 骨干网络体系结构能干什么?骨干网络体系结构的作用

    骨干网络体系结构是现代信息社会的“超级高速公路网”,它通过分层设计、冗余备份和智能调度,确保海量数据在全球范围内高速、稳定、安全地传输,是支撑云计算、物联网及人工智能应用的底层基石,想象一下,如果你把互联网比作一个巨大的城市交通系统,那么骨干网络就是连接各个城市的主干道和立交桥,没有它,你的每一次微信发送、每一……

    2026-06-18
    0
  • 高io数据库可以干什么用?高io数据库适合什么场景

    高IO数据库的核心价值在于通过极高的读写吞吐量,解决海量数据场景下的性能瓶颈,是支撑高并发交易、实时分析及大规模内容分发的关键基础设施,在数字化转型的深水区,数据不再仅仅是静态的记录,而是流动的资产,传统的机械硬盘或普通SSD早已无法满足现代应用对速度的极致追求,高IO(Input/Output)数据库,就是那……

    2026-06-18
    0
  • 高io服务器性能如何?高io服务器适合什么场景

    高IO服务器并非单纯指代某种硬件,而是指在随机读写、高并发连接及小文件处理场景下,具备极致IOPS(每秒输入输出操作次数)和低延迟特性的计算资源,它是支撑现代高并发应用稳定运行的核心基石,在2026年的数字化浪潮中,业务负载早已从简单的静态页面展示演变为复杂的实时数据处理,许多开发者在排查系统瓶颈时,往往忽略了……

    2026-06-18
    0
  • 隔离网络空间哪里便宜?国内隔离网络空间价格

    隔离网络空间并没有统一的“便宜”标准,其成本高度取决于物理隔离等级、带宽需求及安全合规要求,通常物理网闸方案初期投入较高但长期运维成本低,而逻辑隔离方案虽初期便宜但存在潜在安全风险,建议根据业务敏感度选择混合隔离架构以平衡成本与安全,在数字化时代,企业构建独立网络环境的需求日益增长,但“隔离网络空间哪里便宜”这……

    2026-06-18
    0
  • 骨干网络体系结构设备为何故障?常见原因有哪些

    骨干网络体系结构设备故障的核心原因通常归结为硬件老化、配置错误、物理链路中断及外部攻击四大类,其中电源模块失效与光模块性能衰减是占比最高的隐性故障源,骨干网作为数字经济的“大动脉”,其稳定性直接关乎国计民生,当核心路由器或交换机出现丢包、震荡甚至宕机时,运维人员往往面临巨大的压力,很多人第一反应是检查软件配置……

    2026-06-18
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注