1.说说你是如何理解latch和enqueue的?
相同点:Latch和enqueue(Lock)都是为了保护资源的串行化访问.
不同点如下表:
Latch Lock
队列性 X O
时长 很短 可能很长
层面 数据库资源层 业务应用层
目的 保证资源的完整性 保证业务操作的完整性
--EOF--
2.模拟PPT中的例子,做一个绑定变量和非绑定变量的资源消耗对比,给出过程和结果。可以使用latch_test.txt文档中提供的脚本。
2.1 创建runstats_pkg包.
http://asktom.oracle.com/pls/asktom/ASKTOM.download_file?p_file=6551378329289980701
SQL> create global temporary table run_stats
2 ( runid varchar2(15),
3 name varchar2(80),
4 value int )
5 on commit preserve rows;
Table created.
SQL> create or replace view stats
2 as select 'STAT...' || a.name name, b.value
3 from v$statname a, v$mystat b
4 where a.statistic# = b.statistic#
5 union all
6 select 'LATCH.' || name, gets
7 from v$latch
8 union all
9 select 'STAT...Elapsed Time', hsecs from v$timer;
View created.
SQL> create or replace package runstats_pkg
2 as
3 procedure rs_start;
4 procedure rs_middle;
5 procedure rs_stop( p_difference_threshold in number default 0 );
6 end;
7 /
Package created.
SQL> create or replace package body runstats_pkg
2 as
3
4 g_start number;
5 g_run1 number;
6 g_run2 number;
7
8 procedure rs_start
9 is
10 begin
11 delete from run_stats;
12
13 insert into run_stats
14 select 'before', stats.* from stats;
15
16 g_start := dbms_utility.get_cpu_time;
17 end;
18
19 procedure rs_middle
20 is
21 begin
22 g_run1 := (dbms_utility.get_cpu_time-g_start);
23
24 insert into run_stats
25 select 'after 1', stats.* from stats;
26 g_start := dbms_utility.get_cpu_time;
27
28 end;
29
30 procedure rs_stop(p_difference_threshold in number default 0)
31 is
32 begin
33 g_run2 := (dbms_utility.get_cpu_time-g_start);
34
35 dbms_output.put_line
36 ( 'Run1 ran in ' || g_run1 || ' cpu hsecs' );
37 dbms_output.put_line
38 ( 'Run2 ran in ' || g_run2 || ' cpu hsecs' );
39 if ( g_run2 <> 0 )
40 then
41 dbms_output.put_line
42 ( 'run 1 ran in ' || round(g_run1/g_run2*100,2) ||
43 '% of the time' );
44 end if;
45 dbms_output.put_line( chr(9) );
46
47 insert into run_stats
48 select 'after 2', stats.* from stats;
49
50 dbms_output.put_line
51 ( rpad( 'Name', 30 ) || lpad( 'Run1', 12 ) ||
52 lpad( 'Run2', 12 ) || lpad( 'Diff', 12 ) );
53
54 for x in
55 ( select rpad( a.name, 30 ) ||
56 to_char( b.value-a.value, '999,999,999' ) ||
57 to_char( c.value-b.value, '999,999,999' ) ||
58 to_char( ( (c.value-b.value)-(b.value-a.value)), '999,999,999' ) data
59 from run_stats a, run_stats b, run_stats c
60 where a.name = b.name
61 and b.name = c.name
62 and a.runid = 'before'
63 and b.runid = 'after 1'
64 and c.runid = 'after 2'
65 -- and (c.value-a.value) > 0
66 and abs( (c.value-b.value) - (b.value-a.value) )
67 > p_difference_threshold
68 order by abs( (c.value-b.value)-(b.value-a.value))
69 ) loop
70 dbms_output.put_line( x.data );
71 end loop;
72
73 dbms_output.put_line( chr(9) );
74 dbms_output.put_line
75 ( 'Run1 latches total versus runs -- difference and pct' );
76 dbms_output.put_line
77 ( lpad( 'Run1', 12 ) || lpad( 'Run2', 12 ) ||
78 lpad( 'Diff', 12 ) || lpad( 'Pct', 10 ) );
79
80 for x in
81 ( select to_char( run1, '999,999,999' ) ||
82 to_char( run2, '999,999,999' ) ||
83 to_char( diff, '999,999,999' ) ||
84 to_char( round( run1/decode( run2, 0, to_number(null), run2) *100,2 ), '99,99
85 from ( select sum(b.value-a.value) run1, sum(c.value-b.value) run2,
86 sum( (c.value-b.value)-(b.value-a.value)) diff
87 from run_stats a, run_stats b, run_stats c
88 where a.name = b.name
89 and b.name = c.name
90 and a.runid = 'before'
91 and b.runid = 'after 1'
92 and c.runid = 'after 2'
93 and a.name like 'LATCH%'
94 )
95 ) loop
96 dbms_output.put_line( x.data );
97 end loop;
98 end;
99
100 end;
101 /
Package body created.
SQL>
2.2 创建测试过程.
SQL> create table tt(x int);
Table created.
SQL>
SQL> create or replace procedure p1 as
2 l_cnt number;
3 begin
4 for i in 1 .. 10000 loop
5 execute immediate 'select count(*) from tt where x = ' || i into l_cnt;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
SQL> create or replace procedure p2 as
2 l_cnt number;
3 begin
4 for i in 1 .. 10000 loop
5 select count(*) into l_cnt from tt where x = i ;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
2.3 测试与结果.
SQL> exec runStats_pkg.rs_start;
PL/SQL procedure successfully completed.
SQL> exec p1;
PL/SQL procedure successfully completed.
SQL> exec runStats_pkg.rs_middle;
PL/SQL procedure successfully completed.
SQL> exec p2;
PL/SQL procedure successfully completed.
SQL> set serveroutput on
SQL> exec runStats_pkg.rs_stop(500);
Run1 ran in 552 cpu hsecs
Run2 ran in 26 cpu hsecs
run 1 ran in 2123.08% of the time
Name Run1 Run2 Diff
STAT...recursive cpu usage 528 21 -507
STAT...bytes sent via SQL*Net 622 1,149 527
STAT...CPU used by this sessio 554 27 -527
STAT...CPU used when call star 557 28 -529
STAT...parse time elapsed 532 1 -531
LATCH.session idle bit 107 674 567
STAT...DB time 613 29 -584
STAT...Elapsed Time 1,351 2,798 1,447
STAT...session cursor cache hi 43 10,018 9,975
STAT...enqueue requests 10,010 5 -10,005
STAT...enqueue releases 10,010 5 -10,005
STAT...parse count (hard) 10,010 5 -10,005
STAT...parse count (total) 10,043 28 -10,015
STAT...consistent gets from ca 40,104 30,082 -10,022
STAT...calls to get snapshot s 50,087 40,041 -10,046
STAT...sql area evicted 10,095 3 -10,092
STAT...session logical reads 40,267 30,160 -10,107
STAT...consistent gets from ca 40,240 30,131 -10,109
STAT...consistent gets 40,240 30,131 -10,109
LATCH.transaction allocation 1 10,589 10,588
LATCH.cache buffers chains 100,493 89,335 -11,158
LATCH.enqueue hash chains 20,399 966 -19,433
LATCH.enqueues 20,204 548 -19,656
STAT...recursive calls 40,844 10,224 -30,620
LATCH.kks stats 65,341 242 -65,099
STAT...session uga memory 0 -65,408 -65,408
LATCH.shared pool simulator 97,478 906 -96,572
LATCH.row cache objects 184,921 11,855 -173,066
STAT...session pga memory 65,536 -131,072 -196,608
LATCH.DML lock allocation 187 241,131 240,944
LATCH.shared pool 422,522 12,899 -409,623
Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
925,152 384,698 -540,454 240.49%
PL/SQL procedure successfully completed.
SQL>
--EOF--
3.用示例说明表数据中出现热块的场景,并给出解决方案。
3.1 准备工作.
SQL> drop table tt purge;
Table dropped.
SQL>
SQL> create table tt(x int);
Table created.
SQL>
SQL> insert into tt values(1);
1 row created.
SQL>
SQL> insert into tt values(2);
1 row created.
SQL>
SQL> insert into tt values(3);
1 row created.
SQL>
SQL> commit;
Commit complete.
SQL>
SQL> select x,
2 dbms_rowid.ROWID_RELATIVE_FNO(rowid) file#,
3 dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block#
4 from tt;
X FILE# BLOCK#
---------- ---------- ----------
1 237 356982
2 237 356982
3 237 356982
SQL>
SQL> create or replace procedure p1 as
2 begin
3 loop
4 update tt set x=1 where x=1;
5 commit;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
SQL> create or replace procedure p2 as
2 begin
3 loop
4 update tt set x=2 where x=2;
5 commit;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
SQL> create or replace procedure p3 as
2 begin
3 loop
4 update tt set x=3 where x=3;
5 commit;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
3.2 分别开启3个session执行3个procedure, 为避免机器卡死session数最好不要超过cpu数.
SQL> select sid from v$mystat where rownum=1;
SID
----------
335
SQL> exec p1;
=================================
SQL> select sid from v$mystat where rownum=1;
SID
----------
214
SQL> exec p2;
=================================
SQL> select sid from v$mystat where rownum=1;
SID
----------
225
SQL> exec p3;
=================================
3.3 验证热块争用是否出现.
SQL> SELECT sw.sid
2 , (SELECT ds.segment_name
3 FROM dba_segments ds
4 WHERE ds.header_file = sw.p1 AND sw.event = 'buffer busy waits')
5 segment_name
6 ,sw.event
7 ,sw.p1
8 ,sw.p2
9 FROM v$session_wait sw
10 WHERE sw.sid IN (335, 214, 225);
SID SEGMENT_NAME EVENT P1 P2
------ --------------- ------------------------------ ---------- ----------
214 TT buffer busy waits 237 356982
225 TT buffer busy waits 237 356982
335 log file switch (private stran 0 0
d flush incomplete)
SQL>
=================================
3.4 解决方案.
业务上可以考虑优化流程, 避免不必要的高并发.
技术上可以考虑减少热块上的行数(alter table minimize records_per_block).
以下参考来自Asktom.
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:19119999822024
depends on why the block is hot. One needs to understand the application and how it uses the data in order to come up with a method. For example "hot index root block" -- possible solution is partitioning (you now have N index blocks) "hot right hand side of index block" -- possible solutions are reverse key indexes (if application doesn't range scan) or partitioning "hot block in a small table that gets updated lots" -- table has 10 rows, all 10 rows fit into a single block. block gets slammed. possible solution is to "alter table minimize records_per_block" and have just 2 records/block -- or set pctfree really high to go one record per block. "application level change" comes into play as well -- change the way the application accesses the data.
--EOF--
4.用示例说明索引数据块中出现热块的场景,并给出解决方案。
4.1准备工作
SQL> drop table tt purge;
Table dropped.
SQL>
SQL> create table tt(x int);
Table created.
SQL>
SQL> insert into tt values(1);
1 row created.
SQL>
SQL> insert into tt values(2);
1 row created.
SQL>
SQL> insert into tt values(3);
1 row created.
SQL>
SQL> commit;
Commit complete.
SQL>
SQL> create index idx_t_x on tt(x);
Index created.
SQL>
SQL> select x,
2 dbms_rowid.ROWID_RELATIVE_FNO(rowid) file#,
3 dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block#
4 from tt;
X FILE# BLOCK#
---------- ---------- ----------
1 237 356983
2 237 356983
3 237 356983
SQL>
SQL> create or replace procedure p1 as
2 l_x number;
3 begin
4 loop
5 select /*+ index(t) */ x into l_x from tt where x=1;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
SQL> create or replace procedure p2 as
2 l_x number;
3 begin
4 loop
5 select /*+ index(t) */ x into l_x from tt where x=2;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
SQL> create or replace procedure p3 as
2 l_x number;
3 begin
4 loop
5 select /*+ index(t) */ x into l_x from tt where x=3;
6 end loop;
7 end;
8 /
Procedure created.
SQL>
4.2 同时开启3个session进行测试
SQL> select sid from v$mystat where rownum=1;
SID
----------
214
SQL> exec p1;
=====================================
SQL> select sid from v$mystat where rownum=1;
SID
----------
237
SQL> exec p2;
=====================================
SQL> select sid from v$mystat where rownum=1;
SID
----------
225
SQL> exec p3;
=====================================
4.3 验证结果
SQL> SELECT sid
2 ,event
3 ,p1
4 ,p2
5 , (SELECT o.object_name
6 FROM sys.x_$bh bh
7 ,dba_objects o
8 WHERE o.object_id = bh.obj
9 AND o.object_type = 'INDEX'
10 AND bh.hladdr = sw.p1raw) obj
11 FROM v$session_wait sw
12 WHERE sid IN (214, 237, 225);
SID EVENT P1 P2 OBJ
---------- ------------------------------ ---------- ---------- ----------------
214 latch: cache buffers chains 3720482416 144 IDX_T_X
225 latch: cache buffers chains 3720482416 144 IDX_T_X
237 latch: cache buffers chains 3720482416 144 IDX_T_X
SQL>
=====================================
4.4 解决方案
业务上可以考虑优化流程, 避免不必要的高并发.
技术上可以考虑减少索引热块上的行数(使用较小的数据块)或者反向索引(create index reverse).
--EOF--