【性能优化】之 LATCH 作业

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;



你可能感兴趣的:(【性能优化】之 LATCH 作业)