本文内容
- Latch 和等待
- 共享池中的 Latch 争用
Latch 和等待
假设,一个数据块正在被一个会话从磁盘中读如到内存中,请注意,是正在读取中,此时,如果另外一个会话也正需要这个数据块,他该怎么办?
此时,丈等待这个数据块被读取到内存中,这样就可以只有一个数据块在内存中,不会导致数据的不一致性。那么,正在读取数据块的会话如何能阻止别的会话继续读取这个数据块呢?方法是它需要获得一种像锁一样的资源,以便阻止其他的会话来做相同的事情,这种资源就叫 Latch。Latch 是 Oracle 为了保护内存结构而产生。
Latch 不会造成阻塞,只会导致等待。导致 Latch 争用而等待的原因很多,内存中很多资源都可能存在争用。最常见的两类 Latch 争用:
- 共享池中的 Latch 争用
- 数据缓冲池中的 Latch 争用
共享池中的 Latch 争用
创建测试环境
用 sys 用户登录,查看 Latch 争用情况:
SQL> select * from v$latchname where name like 'library cache%';
LATCH# NAME HASH
---------- -------------------------------------------------- ----------
278 library cache load lock 2952162927
SQL>
在分析系统性能是,如果看到 library cache 这样的 Latch 争用,就可以断定是共享池中出现了问题,这基本上是有 SQL 语句导致的,比如没有绑定变量或一些存储过程被反复分析。
下面是来自 asktom.oracle.com 上给出的一个在有绑定变量和无绑定变量的情况下,Latch 资源争用的对比测试。该测试被很多 Oracle 书籍引用。测试让一条 SQL 语句执行1万次,然后给出各自的执行过程中产生的资源使用情况。
用 sys 用户新建用户 test 并授权。
SQL> create user test identified by test default tablespace users;
用户已创建。
SQL> grant dba to test;
授权成功。
SQL> grant select on sys.v_$statname to test;
授权成功。
SQL> grant select on sys.v_$mystat to test;
授权成功。
SQL> grant select on sys.v_$latch to test;
授权成功。
SQL> grant select on sys.v_$timer to test;
授权成功。
SQL>
已新建用户登录。
用 test 用户登录,执行如下操作:
SQL> conn test/test
已连接。
SQL> CREATE GLOBAL TEMPORARY TABLE run_stats
2 (
3 runid VARCHAR2 (15),
4 name VARCHAR2 (80),
5 VALUE INT
6 )
7 ON COMMIT PRESERVE ROWS;
表已创建。
SQL> CREATE OR REPLACE VIEW stats
2 AS
3 SELECT 'STAT...' || a.name name, b.VALUE
4 FROM SYS.v_$statname a, SYS.v_$mystat b
5 WHERE a.statistic# = b.statistic#
6 UNION ALL
7 SELECT 'LATCH.' || name, gets FROM SYS.v_$latch
8 UNION ALL
9 SELECT 'STAT...Elapsed Time', hsecs FROM SYS.v_$timer;
视图已创建。
SQL> CREATE OR REPLACE PACKAGE runstats_pkg
2 AS
3 PROCEDURE rs_start;
4
5 PROCEDURE rs_middle;
6
7 PROCEDURE rs_stop (p_difference_threshold IN NUMBER DEFAULT 0);
8 END;
9 /
程序包已创建。
SQL> CREATE OR REPLACE PACKAGE BODY runstats_pkg
2 AS
3 g_start NUMBER;
4 g_run1 NUMBER;
5 g_run2 NUMBER;
6
7 PROCEDURE rs_start
8 IS
9 BEGIN
10 DELETE FROM run_stats;
11
12 INSERT INTO run_stats
13 SELECT 'before', stats.* FROM stats;
14
15 g_start := DBMS_UTILITY.get_cpu_time;
16 END;
17
18 PROCEDURE rs_middle
19 IS
20 BEGIN
21 g_run1 := (DBMS_UTILITY.get_cpu_time - g_start);
22
23 INSERT INTO run_stats
24 SELECT 'after 1', stats.* FROM stats;
25
26 g_start := DBMS_UTILITY.get_cpu_time;
27 END;
28
29 PROCEDURE rs_stop (p_difference_threshold IN NUMBER DEFAULT 0)
30 IS
31 BEGIN
32 g_run2 := (DBMS_UTILITY.get_cpu_time - g_start);
33
34 DBMS_OUTPUT.put_line ('Run1 ran in ' || g_run1 || ' cpu hsecs');
35 DBMS_OUTPUT.put_line ('Run2 ran in ' || g_run2 || ' cpu hsecs');
36
37 IF (g_run2 <> 0)
38 THEN
39 DBMS_OUTPUT.put_line (
40 'run 1 ran in '
41 || ROUND (g_run1 / g_run2 * 100, 2)
42 || '% of the time');
43 END IF;
44
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)
52 || LPAD ('Run1', 12)
53 || LPAD ('Run2', 12)
54 || LPAD ('Diff', 12));
55
56 FOR x
57 IN ( SELECT RPAD (a.name, 30)
58 || TO_CHAR (b.VALUE - a.VALUE, '999,999,999')
59 || TO_CHAR (c.VALUE - b.VALUE, '999,999,999')
60 || TO_CHAR (
61 ( (c.VALUE - b.VALUE) - (b.VALUE - a.VALUE)),
62 '999,999,999')
63 data
64 FROM run_stats a, run_stats b, run_stats c
65 WHERE a.name = b.name
66 AND b.name = c.name
67 AND a.runid = 'before'
68 AND b.runid = 'after 1'
69 AND c.runid = 'after 2'
70 AND ABS ( (c.VALUE - b.VALUE) - (b.VALUE - a.VALUE))
>
71 p_difference_threshold
72 ORDER BY ABS ( (c.VALUE - b.VALUE) - (b.VALUE - a.VALUE)))
73 LOOP
74 DBMS_OUTPUT.put_line (x.data);
75 END LOOP;
76
77 DBMS_OUTPUT.put_line (CHR (9));
78 DBMS_OUTPUT.put_line (
79 'Run1 latches total versus runs -- difference and pct');
80 DBMS_OUTPUT.put_line (
81 LPAD ('Run1', 12)
82 || LPAD ('Run2', 12)
83 || LPAD ('Diff', 12)
84 || LPAD ('Pct', 10));
85
86 FOR x
87 IN (SELECT TO_CHAR (run1, '999,999,999')
88 || TO_CHAR (run2, '999,999,999')
89 || TO_CHAR (diff, '999,999,999')
90 || TO_CHAR (
91 ROUND (
92 run1
93 / DECODE (run2, 0, TO_NUMBER (0), run2)
94 * 100,
95 2),
96 '99,999.99')
97 || '%'
98 data
99 FROM (SELECT SUM (b.VALUE - a.VALUE) run1,
100 SUM (c.VALUE - b.VALUE) run2,
101 SUM ( (c.VALUE - b.VALUE) - (b.VALUE - a.VALUE)
)
102 diff
103 FROM run_stats a, run_stats b, run_stats c
104 WHERE a.name = b.name
105 AND b.name = c.name
106 AND a.runid = 'before'
107 AND b.runid = 'after 1'
108 AND c.runid = 'after 2'
109 AND a.name LIKE 'LATCH%'))
110 LOOP
111 DBMS_OUTPUT.put_line (x.data);
112 END LOOP;
113 END;
114 END;
115 /
程序包体已创建。
SQL>
测试对比
创建一个表 t。
SQL> CREATE TABLE t (x INT);
表已创建。
SQL>
创建不使用变量绑定方式执行 SQL 10000 的存储过程 p1:
SQL> CREATE OR REPLACE PROCEDURE p1
2 AS
3 l_cnt NUMBER;
4 BEGIN
5 FOR i IN 1 .. 10000
6 LOOP
7 EXECUTE IMMEDIATE 'select count(*) from t where x=' || i INTO l_cnt;
8 END LOOP;
9 END;
10 /
过程已创建。
SQL>
创建使用变量绑定方式执行 SQL 10000 的存储过程 p2:
SQL> CREATE OR REPLACE PROCEDURE p2
2 AS
3 l_cnt NUMBER;
4 BEGIN
5 FOR i IN 1 .. 10000
6 LOOP
7 SELECT COUNT(*)
8 INTO l_cnt
9 FROM t
10 WHERE x = i;
11 END LOOP;
12 END;
13 /
过程已创建。
SQL>
开始测试
SQL> exec runstats_pkg.rs_start
PL/SQL 过程已成功完成。
SQL> exec p1
PL/SQL 过程已成功完成。
SQL> exec runstats_pkg.rs_middle
PL/SQL 过程已成功完成。
SQL> exec p2
PL/SQL 过程已成功完成。
SQL> exec runstats_pkg.rs_stop(1000)
PL/SQL 过程已成功完成。
SQL> set serveroutput on
SQL> exec runstats_pkg.rs_stop(1000)
Run1 ran in 379 cpu hsecs
Run2 ran in 26 cpu hsecs
run 1 ran in 1457.69% of the time
Name Run1 Run2 Diff
LATCH.channel operations paren 1,622 516 -1,106
STAT...bytes sent via SQL*Net 279 1,443 1,164
LATCH.JS queue state obj latch 1,770 540 -1,230
LATCH.channel operations paren 1,622 245 -1,377
LATCH.JS queue state obj latch 1,770 270 -1,500
LATCH.checkpoint queue latch 2,323 782 -1,541
LATCH.messages 2,642 1,005 -1,637
LATCH.checkpoint queue latch 2,323 314 -2,009
LATCH.messages 2,642 380 -2,262
STAT...undo change vector size 2,736 5,376 2,640
STAT...redo size 3,704 7,252 3,548
LATCH.SQL memory manager worka 6,784 2,109 -4,675
LATCH.SQL memory manager worka 6,784 1,018 -5,766
STAT...physical read bytes 0 8,192 8,192
STAT...physical read total byt 0 8,192 8,192
LATCH.transaction allocation 14,349 4,784 -9,565
STAT...session logical reads 40,074 30,488 -9,586
STAT...consistent gets 40,020 30,422 -9,598
STAT...consistent gets from ca 40,020 30,422 -9,598
STAT...consistent gets from ca 40,018 30,333 -9,685
STAT...calls to get snapshot s 50,009 40,092 -9,917
LATCH.MinActiveScn Latch 10,096 157 -9,939
STAT...parse count (total) 10,009 29 -9,980
STAT...parse count (hard) 10,003 13 -9,990
STAT...enqueue releases 10,010 18 -9,992
STAT...enqueue requests 10,011 18 -9,993
STAT...consistent gets from ca 40,020 30,022 -9,998
STAT...consistent gets 40,020 30,022 -9,998
STAT...parse count (hard) 10,003 4 -9,999
STAT...session cursor cache hi 4 10,004 10,000
STAT...consistent gets from ca 40,018 30,018 -10,000
STAT...parse count (total) 10,009 9 -10,000
STAT...calls to get snapshot s 50,009 40,008 -10,001
STAT...enqueue releases 10,010 6 -10,004
STAT...enqueue requests 10,011 6 -10,005
STAT...session logical reads 40,074 30,054 -10,020
STAT...session cursor cache hi 4 10,031 10,027
LATCH.MinActiveScn Latch 10,096 4 -10,092
LATCH.transaction allocation 14,349 2,391 -11,958
LATCH.cache buffers chains 48,608 34,131 -14,477
STAT...session uga memory 16,424 0 -16,424
STAT...sorts (rows) 0 17,422 17,422
LATCH.cache buffers chains 48,608 30,643 -17,965
STAT...session uga memory max 13,600 32,072 18,472
LATCH.kks stats 20,013 28 -19,985
LATCH.kks stats 20,013 9 -20,004
STAT...Elapsed Time 29,521 9,331 -20,190
LATCH.enqueue hash chains 24,596 1,966 -22,630
LATCH.enqueues 24,106 1,392 -22,714
LATCH.enqueues 24,106 634 -23,472
LATCH.enqueue hash chains 24,596 684 -23,912
STAT...Elapsed Time 29,521 4,567 -24,954
STAT...recursive calls 40,064 10,361 -29,703
STAT...recursive calls 40,064 10,044 -30,020
STAT...table scan rows gotten 0 34,776 34,776
LATCH.DML lock allocation 63,719 21,430 -42,289
LATCH.DML lock allocation 63,719 10,597 -53,122
LATCH.shared pool simulator 56,264 1,025 -55,239
LATCH.shared pool simulator 56,264 222 -56,042
STAT...session pga memory -171,272 -65,536 105,736
STAT...session uga memory max 13,600 157,536 143,936
STAT...session pga memory -171,272 0 171,272
LATCH.row cache objects 182,579 1,796 -180,783
LATCH.row cache objects 182,579 384 -182,195
LATCH.shared pool 334,118 11,762 -322,356
LATCH.shared pool 334,118 10,499 -323,619
STAT...session pga memory max -1,490,300 0 1,490,300
STAT...session pga memory max -1,490,300 2,293,760 3,784,060
Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
1,603,990 147,130 -1,456,860 1,090.19%
PL/SQL 过程已成功完成。
SQL>
性能采集包 runstats_pkg 分别在测试开始、存储过程 p1 运行结束、存储过程 p2运行结束,这3个时间点采集性能数据,最后在报告中给出了2个存储过程各自的 Latch资源使用情况以及对比情况。
从这个结果中可以清楚地看到不绑定变量的 Latch 争用是非常严重的,注意输出结果中几个关于 Latch 列 的 Latch 在数量的差异,可以看到,绑定变量要比不绑定,Latch 争用小得多。