Wait Event - Latch: Row Cache Objects - DC - Objects
Wait Event - Latch: Row Cache Objects - DC - Objects
The following two experiments focus on wait event latch: row cache objects, on child latch# 9 dc_objects for database version 11.2.0.3 (In 10.2.0.4, this latch is dc_object_ids). The first experiment shows how concurrent sessions of hard parsing/optimizing with high number of dc_objects gets will cause wait event latch: row cache objects. The second experiment demonstrates the relationship between number of indexes in a table and number of gets on dc_objects when hard parsing/optimizing a sql.
William Tang
Page 1
insert into t2 select c1,c1+1,c1+2,c1+3,c1+4,c1+5,c1+6,c1+7,c1+8,c1+9 from ( select trunc(1+(100000-1)*dbms_random.value) c1 from (select rownum from all_objects where rownum <= 1000) k1, (select rownum from all_objects where rownum <= 100) k2 where rownum <=100000); 100000 rows created. commit; Commit complete. /* Generate creation of 100 indexes */ SQL> @gen_cr_t2_idx.sql with t2_cols as (select column_name from dba_tab_columns where table_name='T2'order by column_ID) select 'create index t2_idx'||rownum||' on t2('|| case when (tc1.column_name = tc2.column_name) then tc1.column_name else tc1.column_name||','||tc2.column_name end ||');' from t2_cols tc1, t2_cols tc2; create index t2_idx1 on t2(C1); create index t2_idx2 on t2(C1,C2); create index t2_idx3 on t2(C1,C3); create index t2_idx4 on t2(C1,C4); create index t2_idx5 on t2(C1,C5); create index t2_idx6 on t2(C1,C6); create index t2_idx7 on t2(C1,C7); create index t2_idx8 on t2(C1,C8); create index t2_idx9 on t2(C1,C9); create index t2_idx10 on t2(C1,C10); create index t2_idx11 on t2(C2,C1); create index t2_idx12 on t2(C2); ............ William Tang Page 2
create index t2_idx91 on t2(C10,C1); create index t2_idx92 on t2(C10,C2); create index t2_idx93 on t2(C10,C3); create index t2_idx94 on t2(C10,C4); create index t2_idx95 on t2(C10,C5); create index t2_idx96 on t2(C10,C6); create index t2_idx97 on t2(C10,C7); create index t2_idx98 on t2(C10,C8); create index t2_idx99 on t2(C10,C9); create index t2_idx100 on t2(C10); SQL> @cr_t2_idx.sql ..... Index created. SQL> Create table t3 same structure as t2(copied and modified t2 scripts). SQL> @cr_t3.sql Table created. /* execute dbms_random.seed(1); for t3 */ SQL> @gen_data_t3.sql PL/SQL procedure successfully completed.
Commit complete. SQL> @cr_t3_idx.sql ..... Index created. execute DBMS_STATS.gather_table_stats(ownname => 'WILLIAM', tabname => 'T2',estimate_percent => 100, DEGREE => 2); execute DBMS_STATS.gather_table_stats(ownname => 'WILLIAM', tabname => 'T3',estimate_percent => 100, DEGREE => 2); William Tang Page 3
--------------------------------------------------------------------------------------Generate hard parsing from 2 sessions to produce latch contention: Session 1: SQL> @gen_hp_t2.sql begin for i in 1 .. 29280 loop execute immediate 'SELECT count(*) FROM t2 WHERE rownum < '||i; end loop; end; / --------------------------------------------------------------------------------------Session 2: SQL> @gen_hp_t3.sql begin for i in 1 .. 29280 loop execute immediate 'SELECT count(*) FROM t3 WHERE rownum < '||i; end loop; end; / --------------------------------------------------------------------------------------Contention noticed in v$session_wait view: SID EVENT 12 latch: row cache objects 92 latch: row cache objects P1TEXT P1 P1RAW WAIT_CLASS address 6834553448 00000001975F0268 Concurrency address 6834553448 00000001975F0268 Concurrency
With child latch address 00000001975F0268, find out which child. SELECT child# FROM v$latch_children WHERE addr= '00000001975F0268'; CHILD# ---------9
William Tang
Page 4
/* Find out what row cache for child latch 9 */ select s.kqrstcln latch#, s.kqrstcid cache#, kqrsttxt name from x$kqrst s where s.kqrstcln=9; LATCH# CACHE# NAME ---------- ---------- -------------------------------9 8 dc_objects 9 8 dc_object_grants AWR report shows high gets on dc_objects. Why optimization could have cause high number of gets on this child latch? This leads us to second experiment which show number of indexes in a table contribute to high number of gets in dc_objects.
CREATE TABLE ROW_CACHE_GETS ( SDATE DATE, LGETS NUMBER, NINDEXES NUMBER ); Table created. Generate sql dc_objects_stat.sql (sql is in Addendum) and run it to capture statistics of dc_objects gets. SQL> @gen_stat_drop_idx.sql SQL> select count(*) from dba_indexes where table_name = 'T2'; COUNT(*) ---------100 SQL> alter system flush shared_pool; System altered. SQL> @dc_objects_stat.sql PL/SQL procedure successfully completed. SQL> select count(*) from dba_indexes where table_name = 'T2'; COUNT(*) ---------0 Run this sql to look at the statistics captured and create a graph with the data.
select rownum-1 "Interval(1 sec)", RC.* from (select sdate, case when (lgets >= nvl(lag(lgets) over (order by sdate), lgets)) then (lgets - nvl(lag(lgets) over (order by sdate), lgets)) else (lgets + 4294967296 - nvl(lag(lgets) over (order by sdate), lgets)) end "Gets", nindexes "Index counts" from row_cache_gets order by sdate ) RC;
William Tang
Page 6
This graph in figure 1 shows number of dc_objects gets during hard parse is directly proportional to number of indexes in table T2. As number of indexes decreased, so is number of gets. Figure 1:
7000 6000 5000 4000 3000 2000 1000 0 100 99 95 91 87 83 79 75 71 67 63 59 55 51 47 43 39 35 31 27 23 19 15 11 7 Index counts) Gets 3 0
So the more indexes in a table, higher number of dc_objects gets when hard parsing a sql. This will increase the likelihood of row cache latch contention when many sessions are hard parsing concurrently. But the graph cannot accurately represented by best fit line gets(x)=ax+b where x is index counts in a table. It seems at different range of index counts, we have different proportionality constants. Something else must contribute to dc_objects gets during optimization.
SQL> alter session set events '10089 trace name context forever, level 1'; Session altered. SQL> @read_event.sql Event 10089 set at level 1 PL/SQL procedure successfully completed. SQL> select count(*) from dba_indexes where table_name = 'T2'; COUNT(*) ---------100 SQL> alter system flush shared_pool; System altered. SQL> @dc_objects_stat.sql PL/SQL procedure successfully completed. SQL> select count(*) from dba_indexes where table_name = 'T2'; COUNT(*) ---------0 And the graph of statistics captured shows an accurate best fit line can be applied to it after disable CBO index sorting. Also, the number of dc_objects gets decreased substantially.
William Tang
Page 8
3000 2500 2000 1500 1000 500 0 100 98 93 88 83 78 73 68 63 58 53 48 43 38 33 28 23 18 13 Index counts Gets 8 3 0
Summary
We can limit latch contention on dc_object by minimize number of hard parse. In addition, minimize number of indexes created in a table, since number of dc_objects gets during hard parse or optimization is directly proportional to number of indexes in a table. Consider disable CBO index sorting, event 10089 to reduce number of dc_objects gets. If the sort order of indexes with CBO index sorting disable is different than default setting, setting event 10089 will affect sql plans using indexes with equal costs.
Addendum
/*gen_stat_drop_idx.sql*/ set feedback off set newpage none set termout off set head off set ver off set term off set line 100000 spool dc_objects_stat.sql select 'begin' from dual William Tang Page 9
union all select 'dbms_lock.sleep(1);'||chr(10)|| 'insert into row_cache_gets'||chr(10)|| '(SDATE,LGETS,NINDEXES)'||chr(10)|| 'select sysdate, a.gets,'||chr(10)|| 'i.idxcnt'||chr(10)|| 'from v$latch_children a,'||chr(10)|| '(select count(*) as idxcnt from dba_indexes where table_name = ''T2'') i'||chr(10)|| 'where a.name=''row cache objects'''||chr(10)|| 'and a.child#=9;'||chr(10)|| 'commit;'||chr(10)|| 'execute immediate ''SELECT count(*) FROM t2 '||chr(10)|| 'WHERE rownum <= '||rownum||''';' from dba_indexes where rownum <=3 union all select 'dbms_lock.sleep(1);'||chr(10)|| 'insert into row_cache_gets'||chr(10)|| 'select sysdate, a.gets,'||chr(10)|| 'i.idxcnt'||chr(10)|| 'from v$latch_children a,'||chr(10)|| '(select count(*) as idxcnt from dba_indexes where table_name = ''T2'') i'||chr(10)|| 'where a.name=''row cache objects'''||chr(10)|| 'and a.child#=9;'||chr(10)|| 'commit;'||chr(10)|| 'execute immediate ''drop index '||owner||'.'||index_name||''';'||chr(10)|| 'execute immediate ''SELECT count(*) FROM t2 '||chr(10)|| 'WHERE rownum <= '||rownum||'+3'';' from dba_indexes where table_name = 'T2' --and uniqueness != 'UNIQUE' --and index_name not like 'SYS_%' union all select 'dbms_lock.sleep(1);'||chr(10)|| 'insert into row_cache_gets'||chr(10)|| 'select sysdate, a.gets,'||chr(10)|| 'i.idxcnt'||chr(10)|| 'from v$latch_children a,'||chr(10)|| '(select count(*) as idxcnt from dba_indexes where table_name = ''T2'') i'||chr(10)|| 'where a.name=''row cache objects'''||chr(10)|| 'and a.child#=9;'||chr(10)|| 'commit;'||chr(10)|| 'execute immediate ''SELECT count(*) FROM t2 '||chr(10)|| 'WHERE rownum <= '||rownum||'+1000'';' William Tang Page 10
from dba_indexes where rownum <=3 union all select 'dbms_lock.sleep(1);'||chr(10)|| 'insert into row_cache_gets'||chr(10)|| 'select sysdate, a.gets,'||chr(10)|| 'i.idxcnt'||chr(10)|| 'from v$latch_children a,'||chr(10)|| '(select count(*) as idxcnt from dba_indexes where table_name = ''T2'') i'||chr(10)|| 'where a.name=''row cache objects'''||chr(10)|| 'and a.child#=9;'||chr(10)|| 'commit;'||chr(10)|| 'end;'||chr(10)||'/' from dual / spool off exit
William Tang
Page 11