1.说说你是如何理解latch和enqueue的?<br>
2.模拟PPT中的例子,做一个绑定变量和非绑定变量的资源消耗对比,给出过程和结果。可以使用latch_test.txt文档中提供的脚本。<br>
3.用示例说明表数据中出现热块的场景,并给出解决方案。<br>
4.用示例说明索引数据块中出现热块的场景,并给出解决方案。<br>
==================================================================================================
1。说说你是如何理解latch和enqueue的?
答:
可以简单理解为内存中资源的锁叫latch,数据库对象(表,索引等)的锁叫Lock。
enqueue 是LOCK中的一种,enqueue是一种保护共享资源的等待机制。
Latch是对内存数据结构提供互斥访问的一种机制。
Latch是瞬间的占用、释放,enqueue 的释放需要等到事务正确的结束,他占用的时间长短由事务大小决定。
----------------------------------------------------------------------------------------------------------
2.模拟PPT中的例子,做一个绑定变量和非绑定变量的资源消耗对比,给出过程和结果。可以使用latch_test.txt文档中提供的脚本。<br>
答:
进行测试:
SQL> exec runstats_pkg.rs_start;
PL/SQL procedure successfully completed
SQL> insert into t2 select * from t1;
75940 rows inserted
SQL> exec runStats_pkg.rs_middle;
PL/SQL procedure successfully completed
SQL>
begin
for x in (select * from t1 )
loop
insert into t3 values x;
end loop;
commit;
end;
/
SQL> alter session set sql_trace=false;
Session altered
SQL> set serveroutput on;
SQL> exec runStats_pkg.rs_stop(100000);
Run1 ran in 4673 hsecs
Run2 ran in 19254 hsecs
run 1 ran in 24.27% of the time
Name Run1 Run2 Diff
LATCH.shared pool 6,121 107,549 101,428
STAT...session uga memory 130,976 0 -130,976
STAT...session pga memory max 245,088 0 -245,088
STAT...undo change vector size 291,280 6,440 -284,840
STAT...undo change vector size 291,280 3,216 -288,064
STAT...session pga memory 245,088 -131,072 -376,160
LATCH.cache buffers chains 258,977 853,810 594,833
STAT...session pga memory 245,088 -720,896 -965,984
STAT...session pga memory max 245,088 1,310,720 1,065,632
STAT...session uga memory max 130,976 1,561,496 1,430,520
LATCH.cache buffers chains 258,977 1,704,809 1,445,832
STAT...redo size 8,861,664 8,544 -8,853,120
STAT...redo size 8,861,664 4,268 -8,857,396
STAT...logical read bytes from 117,669,888 3,727,360-113,942,528
STAT...logical read bytes from 117,669,888 548,864-117,121,024
Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
637,504 3,165,229 2,527,725 20.14%
PL/SQL procedure successfully completed
SQL>
----------------------------------------------------------------------------------------------------------
3.用示例说明表数据中出现热块的场景,并给出解决方案。<br>
1.当前会话ID
SQL> select distinct sid from v$mystat;
SID
----------
157
2.建立一个临时测试表
SQL> create table latch_table1 as select * from dba_objects;
Table created
SQL> select count(0) from latch_table1;
COUNT(0)
----------
75946
3.对表进行一次分析
SQL>
SQL> execute dbms_stats.gather_table_stats('TANG','LATCH_TABLE1');
PL/SQL procedure successfully completed
4.查询 表中每数据块存储的数据量。
SQL> SELECT DBMS_ROWID.rowid_block_number(ROWID),COUNT(0) BLOCK_SUM_ROWS FROM LATCH_TABLE1
2 GROUP BY DBMS_ROWID.rowid_block_number(ROWID) ORDER BY BLOCK_SUM_ROWS;
DBMS_ROWID.ROWID_BLOCK_NUMBER( BLOCK_SUM_ROWS
------------------------------ --------------
2394448 56
2394442 58
2394450 58
2394449 60
2394451 61
................
DBMS_ROWID.ROWID_BLOCK_NUMBER( BLOCK_SUM_ROWS
------------------------------ --------------
2393403 82
2393406 82
2393345 82
2394275 82
2392060 83
2393355 84
2392059 88
2393402 88
2393401 89
2393397 89
2393398 90
2393399 90
1082 rows selected
看到每个数据块存储的记录从56 到90行记录。
再查询 存储行数为90行的数据块有多少。
可以看到以下结果。保存67,68,69行的数据块最多。
select 'LATCH_TABLE1',block_sum_rows,count(0) con_rows_sum_blocks from
(
SELECT DBMS_ROWID.rowid_block_number(ROWID),COUNT(0) BLOCK_SUM_ROWS FROM LATCH_TABLE1
GROUP BY DBMS_ROWID.rowid_block_number(ROWID) ORDER BY BLOCK_SUM_ROWS
)
group by block_sum_rows order by con_rows_sum_blocks ;
'LATCH_TABLE1' BLOCK_SUM_ROWS CON_ROWS_SUM_BLOCKS
1 LATCH_TABLE1 83 1
2 LATCH_TABLE1 62 1
3 LATCH_TABLE1 56 1
4 LATCH_TABLE1 84 1
5 LATCH_TABLE1 60 1
6 LATCH_TABLE1 90 2
7 LATCH_TABLE1 63 2
8 LATCH_TABLE1 89 2
9 LATCH_TABLE1 88 2
10 LATCH_TABLE1 58 2
11 LATCH_TABLE1 61 3
12 LATCH_TABLE1 64 4
13 LATCH_TABLE1 82 4
14 LATCH_TABLE1 81 9
15 LATCH_TABLE1 80 12
16 LATCH_TABLE1 79 19
17 LATCH_TABLE1 65 22
18 LATCH_TABLE1 78 31
19 LATCH_TABLE1 77 31
20 LATCH_TABLE1 76 33
21 LATCH_TABLE1 74 34
22 LATCH_TABLE1 72 35
23 LATCH_TABLE1 73 35
24 LATCH_TABLE1 75 39
25 LATCH_TABLE1 71 60
26 LATCH_TABLE1 66 84
27 LATCH_TABLE1 70 85
28 LATCH_TABLE1 67 132
29 LATCH_TABLE1 69 151
30 LATCH_TABLE1 68 244
建立一个存储过程,以循环访问数据块
create or replace procedure p1
as
v_a pls_integer;
begin
for i in 1..8000
loop
select count(0) into v_a from latch_table1 where obJect_type='TABLE';
end loop;
end;
多个会话调用
SQL> select distinct sid from v$mystat;
SID
----------
2
SQL> EXECUTE P1;
PL/SQL procedure successfully completed
SQL> select distinct sid from v$mystat;
SID
----------
25
SQL> EXECUTE P1;
PL/SQL procedure successfully completed
SQL> select distinct sid from v$mystat;
SID
----------
190
SQL> EXECUTE P1;
PL/SQL procedure successfully completed
SQL> select distinct sid from v$mystat;
SID
----------
157
SQL> EXECUTE P1;
PL/SQL procedure successfully completed
执行的同时,查询 V$LATCHHOLDER 可以从会话级别定位到LATCH的消耗情况。
查询了几次,看到下面情况。可以看到,不是按顺序来持有的。是一种无序状态。
因持有时间很短暂,每次查询都不一样。
SELECT * FROM V$LATCHHOLDER;
PID SID LADDR NAME GETS
1 41 190 00000002B1874150 cache buffers chains 2425552
2 48 2 00000002B18CFD48 cache buffers chains 2545553
PID SID LADDR NAME GETS
1 38 157 00000002B18FBDF8 cache buffers chains 3006737
2 41 190 00000002B18892D0 cache buffers chains 2648858
PID SID LADDR NAME GETS
1 38 157 00000002B17D25D0 cache buffers chains 3008922
2 41 190 00000002AC7881B8 cache buffers chains 2960810
3 48 2 00000002AC657398 cache buffers chains 3490037
查询 一下表中的数据中OBJECT_TYPE='TABLE','INDEX','VIEW'
select object_type,count(0) sum_object from latch_table1 group by object_type
order by sum_object;
OBJECT_TYPE SUM_OBJECT
------------------------------
1 EDITION 1
2 RULE 1
3 MATERIALIZED VIEW 1
4 DESTINATION 2
5 JAVA SOURCE 2
6 SCHEDULE 3
7 SCHEDULER GROUP 4
8 DIRECTORY 4
9 DATABASE LINK 6
10 CONTEXT 7
11 LOB PARTITION 9
12 WINDOW 9
13 INDEXTYPE 9
14 CLUSTER 10
15 RESOURCE PLAN 10
16 UNDEFINED 11
17 EVALUATION CONTEXT 13
18 JOB CLASS 14
19 PROGRAM 19
20 RULE SET 19
21 CONSUMER GROUP 25
22 JOB 30
23 TABLE SUBPARTITION 32
24 QUEUE 36
25 XML SCHEMA 53
26 OPERATOR 55
27 LIBRARY 186
28 PROCEDURE 215
29 TYPE BODY 237
30 INDEX PARTITION 282
31 TABLE PARTITION 306
32 FUNCTION 311
33 JAVA DATA 328
34 SEQUENCE 348
35 TRIGGER 744
36 JAVA RESOURCE 837
37 PACKAGE BODY 1266
38 LOB 1293
39 PACKAGE 1328
40 TYPE 2876
41 TABLE 3118
42 VIEW 5161
43 INDEX 5419
44 JAVA CLASS 23165
45 SYNONYM 28141
现在对 OBJECT_TYPE='TABLE','INDEX','VIEW'的数据进行UPDATE操作,以看产生BUFFER BUSY WAITS
情况。
再增加包含 UPDATE 语句的存储 过程,即可以查询 到BUFFER BUSY WAITS 的争用情况
create or replace procedure p2 as
l_cnt pls_integer;
begin
for i in 1 .. 100
loop
update latch_table1 set object_type = 'TABLE' where object_type = 'TABLE';
end loop;
DBMS_OUTPUT.PUT_LINE('SUCCESSFULLY');
end;
create or replace procedure p3 as
l_cnt pls_integer;
begin
for i in 1 .. 100
loop
update latch_table1 set object_type = 'INDEX' where object_type = 'INDEX';
end loop;
DBMS_OUTPUT.PUT_LINE('SUCCESSFULLY');
end;
create or replace procedure p4 as
l_cnt pls_integer;
begin
for i in 1 .. 100
loop
update latch_table1 set object_type = 'VIEW' where object_type = 'VIEW';
end loop;
DBMS_OUTPUT.PUT_LINE('SUCCESSFULLY');
end;
在多个窗口中同时执行,
查询LATCH表链,可看到
SELECT S1.SID,S2.EVENT FROM V$SESSION S1,V$SESSION S2
WHERE S1.STATUS='ACTIVE' AND S2.EVENT LIKE 'latch%';
SID EVENT
1 2 latch: cache buffers chains
2 23 latch: cache buffers chains
3 25 latch: cache buffers chains
4 26 latch: cache buffers chains
5 34 latch: cache buffers chains
6 37 latch: cache buffers chains
7 45 latch: cache buffers chains
8 46 latch: cache buffers chains
9 48 latch: cache buffers chains
10 57 latch: cache buffers chains
11 67 latch: cache buffers chains
12 78 latch: cache buffers chains
13 80 latch: cache buffers chains
14 89 latch: cache buffers chains
15 100 latch: cache buffers chains
16 111 latch: cache buffers chains
17 114 latch: cache buffers chains
18 122 latch: cache buffers chains
19 133 latch: cache buffers chains
20 144 latch: cache buffers chains
21 155 latch: cache buffers chains
22 156 latch: cache buffers chains
23 157 latch: cache buffers chains
24 166 latch: cache buffers chains
25 177 latch: cache buffers chains
26 188 latch: cache buffers chains
27 190 latch: cache buffers chains
28 199 latch: cache buffers chains
29 222 latch: cache buffers chains
30 256 latch: cache buffers chains
这时我们查询看到WAITS,CHAINS都已出来了。
SELECT S1.SID,S2.EVENT FROM V$SESSION S1,V$SESSION S2
WHERE S1.STATUS='ACTIVE' AND S2.EVENT LIKE '%buffer%';
SID EVENT
1 2 buffer busy waits
2 2 latch: cache buffers chains
3 2 buffer busy waits
4 2 buffer busy waits
5 23 buffer busy waits
6 23 latch: cache buffers chains
7 23 buffer busy waits
8 23 buffer busy waits
9 25 buffer busy waits
10 25 latch: cache buffers chains
11 25 buffer busy waits
12 25 buffer busy waits
13 26 buffer busy waits
14 26 latch: cache buffers chains
15 26 buffer busy waits
16 26 buffer busy waits
17 34 buffer busy waits
18 34 latch: cache buffers chains
19 34 buffer busy waits
20 34 buffer busy waits
21 37 buffer busy waits
22 37 latch: cache buffers chains
解决方案:
现在我们已经知道LATCH 是由于热块引起的。是因为多会话同时/频繁的访问
一些相同的数据块。为了减少同时访问同一数据块。我们可以减少同一数据块存储
的数据的方法,来减少争用。
创建一张新测试表
CREATE TABLE LATCH_TABLE2 AS SELECT * FROM DBA_OBJECTS WHERE ROWNUM<=4;
对表进行分析
execute dbms_stats.gather_table_stats('TANG','LATCH_TABLE2');
设置每个数据块的存储记录数(就是以现有记录4条进行最小设置)
ALTER TABLE LATCH_TABLE2 MINIMIZE RECORDS_PER_BLOCK;
再插入数据
INSERT INTO LATCH_TABLE2 SELECT * FROM DBA_OBJECTS ;
这时我们查询每个数据块的存储记录数可以看到,全部都是4了。
SELECT DBMS_ROWID.rowid_block_number(ROWID),COUNT(0) BLOCK_SUM_ROWS FROM LATCH_TABLE2
GROUP BY DBMS_ROWID.rowid_block_number(ROWID) ORDER BY BLOCK_SUM_ROWS;
DBMS_ROWID.ROWID_BLOCK_NUMBER( BLOCK_SUM_ROWS
1 2413210 1
2 2393471 4
3 2394497 4
4 2394499 4
5 2394503 4
6 2394517 4
7 2394523 4
8 2394528 4
9 2394548 4
10 2394555 4
11 2394557 4
12 2394561 4
13 2394566 4
14 2394572 4
15 2394574 4
16 2394576 4
17 2394590 4
18 2394594 4
19 2394603 4
20 2394606 4
SQL> alter system flush shared_pool;
System altered.
SQL> set autotrace traceonly;
SQL> select * from latch_table1;
75946 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3200799752
--------------------------------------------------------------------------------
--
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
--
| 0 | SELECT STATEMENT | | 75946 | 7194K| 304 (1)| 00:00:04
|
| 1 | TABLE ACCESS FULL| LATCH_TABLE1 | 75946 | 7194K| 304 (1)| 00:00:04
|
--------------------------------------------------------------------------------
--
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
85104 consistent gets
7332 physical reads
14872 redo size
8715964 bytes sent via SQL*Net to client
56216 bytes received via SQL*Net from client
5065 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
75946 rows processed
SQL>
SQL> select * from latch_table2;
75953 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3844951557
--------------------------------------------------------------------------------
--
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
--
| 0 | SELECT STATEMENT | | 4 | 292 | 3 (0)| 00:00:01
|
| 1 | TABLE ACCESS FULL| LATCH_TABLE2 | 4 | 292 | 3 (0)| 00:00:01
|
--------------------------------------------------------------------------------
--
Statistics
----------------------------------------------------------
40 recursive calls
1 db block gets
23100 consistent gets
19193 physical reads
0 redo size
8716584 bytes sent via SQL*Net to client
56216 bytes received via SQL*Net from client
5065 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
75953 rows processed
SQL>
从上面两个的全表搜索执行计划中看到,LATCH_TABLE2
23100 consistent gets
19193 physical reads
75953 rows processed
LATCH_TABLE1
85104 consistent gets
7332 physical reads
75946 rows processed
TABLE2 的物理读比TABLE1多了12K左右。一致性读取即少了60K左右。
更多的数据块读取,减少了争用的同时,性能也有下降。
----------------------------------------------------------------------------------------------------------
4.用示例说明索引数据块中出现热块的场景,并给出解决方案。<br>
建立测试表,插入数据
create table ltb(id number, name varchar2(100));
declare
l_cnt number;
l_rows number := &1;
begin
insert /*+ append */
into ltb
select rownum, object_name
from all_objects a
where rownum <= &1;
l_cnt := sql%rowcount;
commit;
while (l_cnt < l_rows)
loop
insert /*+ APPEND */ into ltb
select rownum+l_cnt,
name
from ltb
where rownum <= l_rows-l_cnt;
l_cnt := l_cnt + sql%rowcount;
commit;
end loop;
end;
/
--对表进行一次分析
SQL> execute dbms_stats.gather_table_stats(user,'LTB');
PL/SQL procedure successfully completed
--查询插入数据总量
SQL> select count(0) from ltb;
COUNT(0)
----------
1000000
SQL> create index inx_ltb_id on ltb(id);
--创建一个反复扫描特定块儿的存储过程,然后开启多个会话同时执行。
create or replace procedure ltb_select (lf in number, lt in number)
is
begin
for idx in 1 .. 500000 loop
for x in (select id from ltb where id between lf and lt) loop
null;
end loop;
end loop;
end;
/
打开一个测试窗口同时执行50个读取工作。
-- Created on 2013/10/20 by ADMINISTRATOR
declare
-- Local variables here
i integer;
-- Test statements here
var job_no number;
begin
for idx in 1 .. 50 loop
ltb_select(10000, 10010);
commit;
end loop;
end;
查询系统当前的等待
select *
from (
select event, total_waits, time_waited
from v$system_event
where wait_class <> 'Idle'
order by 3 desc
)
where rownum <= 100
cursor: pin S 1949 842045
latch: cache buffers chains 986 202788
job scheduler coordinator slave wait 177 70674
os thread startup 445 51176
read by other session 205522 45353
ASM file metadata operation 3698 35497
db file sequential read 71690 16486
library cache: mutex X 83 15643
cursor: pin S wait on X 363 15637
select * from
(select addr, child#, gets, sleeps
from v$latch_children
where name='cache buffers chains'
order by sleeps desc
) where rownum <= 20;
ADDR CHILD# GETS SLEEPS
---------------- ---------- ---------- ----------
000000006BB9BA18 113 50027408 264
000000006BB91E58 57 10019 101
000000006BBE33F8 525 17846 100
000000006BBC9858 377 11128080 88
000000006AC4D230 855 25131 87
000000006AC45708 810 14218 87
000000006AC562D0 907 11623 85
000000006BB8B170 20 10689 82
000000006BB88428 3 20920 81
-- 我们可以看到某几个子latch相应的GETS和SLEEPS严重倾斜,
这意味着相应latch所管的Chain上有热块儿。
select hladdr, obj, (select object_name from dba_objects
where (data_object_id is null and object_id=x.obj)
or data_object_id=x.obj and rownum=1) as object_name,
dbarfil, dbablk, tch from x$bh x
where hladdr in ('000000006BB9BA18','000000006BBC9858')
order by hladdr, obj;
HLADDR OBJ OBJECT_NAME DBARFIL DBABLK TCH
---------------- ---------- ------------------------------ ---------- ---------- ----------
000000006BB9BA18 225 IDL_UB1$ 1 23449 4
000000006BB9BA18 226 IDL_CHAR$ 1 1526 9
000000006BB9BA18 234 I_ARGUMENT2 1 23216 4
000000006BB9BA18 6293 SYS_IL0000006291C00036$$ 2 4466 1
000000006BB9BA18 14847 SYS_LOB0000014844C00005$$ 2 14074 1
000000006BB9BA18 15631 SYS_IL0000015573C00055$$ 2 17946 1
000000006BB9BA18 20821 REPORT_COLUMN_PK 2 31426 1
000000006BB9BA18 25216 WRH$_WAITSTAT 2 48312 2
000000006BB9BA18 25292 INX_LTB_ID 11 214609 126
000000006BBC9858 268 SMON_SCN_TO_TIME_AUX 2 49225 0
000000006BBC9858 6400 WRH$_LIBRARYCACHE_PK 2 5146 1
000000006BBC9858 6400 WRH$_LIBRARYCACHE_PK 2 5146 1
000000006BBC9858 15717 SYS_IL0000015711C00011$$ 2 18626 1
000000006BBC9858 17103 SYS_LOB0000017100C00013$$ 2 22498 1
000000006BBC9858 24594 WRH$_SERVICE_STAT_PK 2 53330 1
000000006BBC9858 25292 INX_LTB_ID 11 214590 0
可以看到INX_LTB_ID及 INX_LTB_ID 的数据块 214609 tch(Touch Count)值(126)高的块为热块.
因本人测试环境为公司测试环境,可能是因为性能不错的原因,我同时执行了20-50多个测试代码,同时
运行读取数据代码并且把读取的数据范围放到1 ..9999999 ,还尝试把索引的PCTFREE值修改成1, 都没有出现热块现象。
所以以上的查询结果数据引用了 网络上搜索到的一些数据,这里表示歉意,考虑到是一个诚实的人,我还
是要说明一下。。但方法是类似的。
解决方案:
1.使用反转索引
create index idx_ltb_id on LTB (id) reverse;
但因这里使用了范围查询,所以就不太适合了。
2.修改PCTFREE值: 提高PCTFREE和使用小块儿表空间。
2.1
alter index INX_LTB_ID rebuild pctfree 40;
2.2
把索引放到一个小块的表空间中。
因为默认的是8K,这里修改成2K.
alter system set db_2k_cache_size=1M;
create tablespace l_index datafile size 200M blocksize 2k;
alter index inx_ltb_id rebuild tablespace l_index;