[中文]
Author:
fuyuncat
Source:
www.HelloDBA.com
Date:
2009-12-18 06:26:13
Redo log is a important data to be used to recovery and some other advanced features. A redo entry involved the information about database changing. All of the redo entries should be writen into logfile finally. To avoid disk io waits, oracle allocated a log buffer from sga to cache the redo data. A redo entry is generated in PGA, then it be copied to log buffer by server process. When reach some conditions, LGWR will write the log buffer contents to the logfiles. Log buffer is shared by all of server processes, to protect it, the server process should first request the "redo allocation latch" to allocate buffer from it. Hence, in high concurrent OLTP system, we may observer the redo allocation latch contentions. Below is the flow of the redo be writen into log buffer:
Generate Redo Entry in PGA -> Server process request Redo Copy latch (which is multiple, 2*CPU_COUNT default) -> Server process request the single redo allocation latch -> allocate log buffer -> release redo allocation latch -> Copy Redo Entrys into Log Buffer -> release Redo Copy latch
shared strand
To reduce redo allocation latch waits, oracle introduced log buffer parallelism in 9.2. It split the log buffer into multiple small buffers, which are named strands (to distinguish from the private strands that will be instroduced later, we also call it "shared strands"). Each shared strand is protected by a redo allocation latch separately. With multiple shared strands, the log buffer allocation become parallelism from sequence, and redo allocation latch contentions reduced correspondly.
Shared strands number is controlled by the parameter log_parallelism, which is undocumented parameter in 10g, and obsoloted in 11g. After 10g, new parameter _log_parallelism_max define the max shared strands number, and _log_parallelism_dynamic identify if the shared strands number could be dynamiy.
SQL代码
- HELLODBA.COM>select nam.ksppinm, val.KSPPSTVL, nam.ksppdesc
- 2 from sys.x$ksppi nam,
- 3 sys.x$ksppsv val
- 4 where nam.indx = val.indx
- 5 --AND nam.ksppinm LIKE '_%'
- 6 AND upper(nam.ksppinm) LIKE '%LOG_PARALLE%';
- KSPPINM KSPPSTVL KSPPDESC
- -------------------------- ---------- ------------------------------------------
- _log_parallelism 1 Number of log buffer strands
- _log_parallelism_max 2 Maximum number of log buffer strands
- _log_parallelism_dynamic TRUE Enable dynamic strands
Shared strand size = log_buffer/(shared strands number). We could get the strands information from x$kcrfstrand, which include shared strands and private strands in 10g later.
SQL代码
- HELLODBA.COM>select indx,strand_size_kcrfa from x$kcrfstrand where last_buf_kcrfa != '00';
- INDX STRAND_SIZE_KCRFA
- ---------- -----------------
- 0 3514368
- 1 3514368
- HELLODBA.COM>show parameter log_buffer
- NAME TYPE VALUE
- ------------------------------------ ----------- ------------------------------
- log_buffer integer 7028736
For multiple CPU host, the default shared strands number is 2 if less than/equal to 16 CPU. If observed redo allocation latch contentions, should consider increase 1 strand for every 16 CPU, but should not more than 8. Besides, _log_parallelism_max could not larger than cpu_count.
Private strand
To reduce redo allocation lacth contention further, a new mechanism is introduced in 10g, which is Private Strand. Private Strand is the buffer allicated from shared pool instead of log buffer.
SQL代码
- HELLODBA.COM>select * from V$sgastat where name like '%strand%';
- POOL NAME BYTES
- ------------ -------------------------- ----------
- shared pool private strands 2684928
- HELLODBA.COM>select indx,strand_size_kcrfa from x$kcrfstrand where last_buf_kcrfa = '00';
- INDX STRAND_SIZE_KCRFA
- ---------- -----------------
- 2 66560
- 3 66560
- 4 66560
- 5 66560
- 6 66560
- 7 66560
- 8 66560
- ...
Redo/Undo mechanism is changed much by private strands. Every private strand is protected by a redo allocation latch. As "private", each strand only servers for 1 active transaction. The transaction that using private strand generate redo entries in private strand instead of PGA. The redo entried in private strand will be batch writen into logfile when flush or commit. If a transaction failed to request a private strand, it will request the shared privated as old mechanism. We can identify the transaction used private strands or not by the 13th bit of ktcxbflg in x$ktcxb.
SQL代码
- HELLODBA.COM>select decode(bitand(ktcxbflg, 4096),0,1,0) used_private_strand, count(*)
- 2 from x$ktcxb
- 3 where bitand(ksspaflg, 1) != 0
- 4 and bitand(ktcxbflg, 2) != 0
- 5 group by bitand(ktcxbflg, 4096);
- USED_PRIVATE_STRAND COUNT(*)
- ------------------- ----------
- 1 10
- 0 1
The transactions using private strand will not request Redo Copy Latch and redo allocation latch of shared strand when generate Redo Entry, just request Redo Copy Latch before flush strand, thus reduced the contentions of those latches, also reduced the CPU workload.
Note: For those transactions failed get private strand, even though there is private strand available before transaction end, it will not request it.
Size of private strand is 65K (129k on 64 bit system). Private strands buffer size in shared is active transactions plus 65k in 10g, and 69k (additional 4k for management) in 11g.
SQL代码
- --10g:
- SQL> select * from V$sgastat where name like '%strand%';
- POOL NAME BYTES
- ------------ -------------------------- ----------
- shared pool private strands 1198080
- HELLODBA.COM>select trunc(value * KSPPSTVL / 100) * 65 * 1024
- 2 from (select value from v$parameter where name = 'transactions') a,
- 3 (select val.KSPPSTVL
- 4 from sys.x$ksppi nam, sys.x$ksppsv val
- 5 where nam.indx = val.indx
- 6 AND nam.ksppinm = '_log_private_parallelism_mul') b;
- TRUNC(VALUE*KSPPSTVL/100)*65*1024
- -------------------------------------
- 1198080
- --11g:
- HELLODBA.COM>select * from V$sgastat where name like '%strand%';
- POOL NAME BYTES
- ------------ -------------------------- ----------
- shared pool private strands 706560
- HELLODBA.COM>select trunc(value * KSPPSTVL / 100) * (65 + 4) * 1024
- 2 from (select value from v$parameter where name = 'transactions') a,
- 3 (select val.KSPPSTVL
- 4 from sys.x$ksppi nam, sys.x$ksppsv val
- 5 where nam.indx = val.indx
- 6 AND nam.ksppinm = '_log_private_parallelism_mul') b;
- TRUNC(VALUE*KSPPSTVL/100)*(65+4)*1024
- -------------------------------------
- 706560
Private strands number affected by 2 factors, size of logfile and active transaction number.
Undocumented parameter _log_private_mul define the percent of logfile space (without the space reserved for log buffer) should be pre-allocate for private strands. We could calculate the limited private strands number under current logfile size.
SQL代码
- HELLODBA.COM>select bytes from v$log where status = 'CURRENT';
- BYTES
- ----------
- 52428800
- HELLODBA.COM>select trunc(((select bytes from v$log where status = 'CURRENT') - (select to_number(value) from v$parameter where name = 'log_buffer'))*
- 2 (select to_number(val.KSPPSTVL)
- 3 from sys.x$ksppi nam, sys.x$ksppsv val
- 4 where nam.indx = val.indx
- 5 AND nam.ksppinm = '_log_private_mul') / 100 / 66560)
- 6 as "calculated private strands"
- 7 from dual;
- calculated private strands
- --------------------------
- 5
- HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';
- actual private strands
- ----------------------
- 5
After logfile switch (like checkpoint, all of private strand should be flushed into logfile before switch. We may found such information in alert log before the logfile switch entry, as "Private strand flush not complete", which could be ignored), oracle will re-allocate private strands base on current logfile size.
SQL代码
- HELLODBA.COM>alter system switch logfile;
- System altered.
- HELLODBA.COM>select bytes from v$log where status = 'CURRENT';
- BYTES
- ----------
- 104857600
- HELLODBA.COM>select trunc(((select bytes from v$log where status = 'CURRENT') - (select to_number(value) from v$parameter where name = 'log_buffer'))*
- 2 (select to_number(val.KSPPSTVL)
- 3 from sys.x$ksppi nam, sys.x$ksppsv val
- 4 where nam.indx = val.indx
- 5 AND nam.ksppinm = '_log_private_mul') / 100 / 66560)
- 6 as "calculated private strands"
- 7 from dual;
- calculated private strands
- --------------------------
- 13
- HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';
- actual private strands
- ----------------------
- 13
Parameter _log_private_parallelism_mul is used to deduce the active transactions number, which is percent of max transactions. Private strands number is limited in active transactions number.
SQL代码
- HELLODBA.COM>show parameter transactions
- NAME TYPE VALUE
- ------------------------------------ ----------- ------------------------------
- transactions integer 222
- transactions_per_rollback_segment integer 5
- HELLODBA.COM>select trunc((select to_number(value) from v$parameter where name = 'transactions') *
- 2 (select to_number(val.KSPPSTVL)
- 3 from sys.x$ksppi nam, sys.x$ksppsv val
- 4 where nam.indx = val.indx
- 5 AND nam.ksppinm = '_log_private_parallelism_mul') / 100 )
- 6 as "calculated private strands"
- 7 from dual;
- calculated private strands
- --------------------------
- 22
- HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';
- actual private strands
- ----------------------
- 22
When pre-allocate private strands, oracle will choose the less number limited by the 2 factors just mentioned. However, as the logfile size could be changed online, the total size of private strands buffer in shared pool and the redo allocation latch number is allocated as active transaction number.
Hence, if logfile is large enough, and active transactios number setting is suitable, private strands will almost eliminate redo allocation latch contentions.
--- Fuyuncat Mark ---