Latch
oracle中,latch是一种轻量级的锁。一般来说,latch由三种内存元素成:pid(进程id),内存地址和内存长度。Latch保证对共享数 据结构的排它性访问,以此来保证内存结构的完整性不受到损坏。在多个会话同时修改或者检视(inspect)sga中同一个内存结构时,必须串行化访问以 保证sga中数据结构的完整性。
latch的征用是无序的大家不排队而是争抢
资源请求和分配
共享池
–sql解析,sql重用。。。
数据缓冲池
–数据访问,数据写入磁盘,数据读入内存。。。
–修改数据块
–数据段扩展
当高并发来时一个会话申请到一项资源就立刻将它锁上latch据为己有
查询有多少latch
Process A holds a latch 进程得到资源并锁定
Process B waits (spins and sleeps) 进程B等待(旋转和睡眠)未获得资源空转
Latch 的获取
Spin
当一个会话无法获得需要的latch时,会继续使用CPU(cup空转),达到一个时间间隔后再次尝试申请latch,直到达到最大到重试次数。
Sleep
当一个会话无法获得需要到latch时,会等待一段时间(sleep),达到一个时间间隔后,再次尝试申请latch,如此反复,直到达到最大的重试次数。
No wait方方式 --如果无法获取请求的latch,则:
-不会发生sleep 或者spin。
-转而获取其它的latch
Shared pool里的lacth争用
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 /
Procedure created.
SQL> create or replace procedure p2
2 as
3 l_cnt number;
4 begin
5 for i in 1 .. 100000
6 loop
7 select count(*) into l_cnt from t where x = i;
8 end loop;
9
10 end;
11 /
Procedure created.
可以看出P1没有绑定变量,P2有绑定变量。
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> alter session set sql_trace = true;
Session altered.
SQL> exec p1;
PL/SQL procedure successfully completed.
SQL> exec p2;
PL/SQL procedure successfully completed.
几乎所有项目第二个资源消耗都要远远小于第一个资源说明绑定变量和没绑定变量区别
orap分析系统没有什么问题性能问题不大
Ortp 连机交易系统需要绑定变量容易产生性能问题
buffer cache争用问题
数据块在内存区有DBWR进程会访问,会受到业务会话访问。当很多会话并发访问时间就是出现latch。
数据块首先要从磁盘上从数据文件里读内存里,通过哈希算法打散。变成散列。通过哈希算法存储在相应地址
比如图中有个一个块头是437,文件号是437。通过哈希算法算出2((1+437)mod 4 = 2);
这里到2就指像文件头了
SQL> desc X$bh
Name Null? Type
----------------------------------------- -------- ----------------------------
ADDR RAW(8)
INDX NUMBER
INST_ID NUMBER
HLADDR RAW(8)
BLSIZ NUMBER
**NXT_HASH RAW(8)
PRV_HASH RAW(8)**
NXT_REPL RAW(8)
PRV_REPL RAW(8)
FLAG NUMBER
FLAG2 NUMBER
LOBID NUMBER
RFLAG NUMBER
SFLAG NUMBER
LRU_FLAG NUMBER
TS# NUMBER
FILE# NUMBER
DBARFIL NUMBER
DBABLK NUMBER
CLASS NUMBER
STATE NUMBER
MODE_HELD NUMBER
CHANGES NUMBER
CSTATE NUMBER
LE_ADDR RAW(8)
DIRTY_QUEUE NUMBER
SET_DS RAW(8)
OBJ NUMBER
BA RAW(8)
CR_SCN_BAS NUMBER
CR_SCN_WRP NUMBER
CR_XID_USN NUMBER
CR_XID_SLT NUMBER
CR_XID_SQN NUMBER
CR_UBA_FIL NUMBER
CR_UBA_BLK NUMBER
CR_UBA_SEQ NUMBER
CR_UBA_REC NUMBER
CR_SFL NUMBER
CR_CLS_BAS NUMBER
CR_CLS_WRP NUMBER
LRBA_SEQ NUMBER
LRBA_BNO NUMBER
HSCN_BAS NUMBER
HSCN_WRP NUMBER
HSUB_SCN NUMBER
US_NXT RAW(8)
US_PRV RAW(8)
WA_NXT RAW(8)
WA_PRV RAW(8)
OQ_NXT RAW(8)
OQ_PRV RAW(8)
AQ_NXT RAW(8)
AQ_PRV RAW(8)
OBJ_FLAG NUMBER
TCH NUMBER
TIM NUMBER
CR_RFCNT NUMBER
SHR_RFCNT NUMBER
基表里标红到两个就是数据块头
NXT_HASH PRV_HASH
000000006D5A9FE0 000000006D5A9FE0
000000006D5AA040 000000006D5AA040
000000006D5AA060 000000006D5AA060
000000006D5AA0B0 000000006D5AA0B0
000000006D5AA0C0 000000006D5AA0C0
000000006D5AA0D0 000000006D5AA0D0
000000006D5AA0E0 000000006D5AA0E0
000000006D5AA0F0 000000006D5AA0F0
000000006D5AA110 000000006D5AA110
000000006D5AA170 000000006D5AA170
000000006D5AA1E0 000000006D5AA1E0
NXT_HASH下一个数据块头地址,PRV_HASH上一个数据块头地址!
访问数据块先申请一个latches,
1.Hash the block address
先把数据块文件头做一个哈希
2.Get buket latch
获得一个latc
3.Look for header
通过hash列表就能找的文件头
4.Found read block in cache
5.Not found read block off disk
在高速缓存中找到读块
未找到磁盘上的读块
S1、2、3、4、5都去访问一个数据块这就是热块争用,一个申请到了其它到就只有先等待。
出现热块就是
访问的会话太多文件头有事务槽ITL(Interested Transaction List)满了,就需要等待!
因为uodate没有提交,搜索的会话争用资源形成Latch
桶中的缓冲区太多
Latch相关的视图–V$LATCH
V$latch
这个视图实际上是oracle对每一个latch的统计信息的一个汇总,每一条记录表示一种latch.
NAME GETS MISSES SLEEPS IMMEDIATE_GETS IMMEDIATE_MISSES
---------------------------------------------------------------- ---------- ---------- ---------- -------------- ----------------
cached attr list 0 0 0 0 0
cache buffers lru chain 20030 0 0 191461 98
cache buffers chains 3049868 3 0 123803 1
cache buffer handles 1198 0 0 0 0
cache protection latch 0 0 0 0 0
cache table scan latch 1056 0 0 1056 0
6 rows selected.
NAME:latch名称
IMMEDIATE_GETS:以Immediate模式latch请求数
IMMEDIATE_MISSES:请求失败数
GETS:以Willing to wait请求模式latch的请求数
MISSES:初次尝试请求不成功次数
SPIN_GETS:第一次尝试失败,但在以后的轮次中成功
SLEEP[x]:成功获取前sleeping次数
WAIT_TIME:花费在等待latch的时间
SQL> desc v$latchholder
Name Null? Type
----------------------- -------- ----------------
PID NUMBER
SID NUMBER
LADDR RAW(8)
NAME VARCHAR2(64)
GETS NUMBER
PID:持有LATCH的进程ID
SID:拥有LATCH的会话ID
LADDR:LATCH地址
NAME:LATCH的名字
GETS:在WAIT模式或NO-WAIT模式下,获取LATCH的次数
CON_ID:容器ID
V$latch_children
柱 | 数据类型 | 描述 |
---|---|---|
addr | RAW(4 , 8) | 闩锁对象的地址 |
LATCH# | NUMBER | 父级闩锁的闩锁编号 |
CHILD# | NUMBER | 子锁存器编号(仅对每个父锁存器唯一) |
LEVEL# | NUMBER | 闩锁级别 |
NAME | VARCHAR2(50) | 闩锁名称 |
HASH | NUMBER | 锁存哈希 |
GETS | NUMBER | 在愿意等待模式下请求锁存器的次数 |
MISSES | NUMBER | 在愿意等待模式下请求闩锁且请求者必须等待的次数 |
SLEEPS | NUMBER | 愿意等待的闩锁请求导致会话在等待闩锁时进入睡眠状态的次数 |
IMMEDIATE_GETS | NUMBER | |
IMMEDIATE_MISSES | NUMBER | 无等待闩锁请求未成功(即丢失)的次数 |
WAITERS_WOKEN | NUMBER | 该列已被弃用,仅出于与Oracle早期版本的兼容性而出现。该列没有任何数据积累;它将始终为零。 |
WAITS_HOLDING_LATCH | NUMBER | 该列已被弃用,仅出于与Oracle早期版本的兼容性而出现。该列没有任何数据积累;它将始终为零。 |
SPIN_GETS | NUMBER | 愿意等待的闩锁请求错过了第一次尝试,但在旋转时成功了 |
SLEEP[1 , 2 , 3] | NUMBER | 这些列已被弃用,仅出于与Oracle早期版本的兼容性而存在,这些列未积累任何数据;它们将始终为零。作为这些列的替代,您可以查询V$EVENT_HISTOGRAM视图中相应EVENT列的值为latch free或的行latch:%。 |
SLEEP4 | NUMBER | 该列已被弃用,仅出于与Oracle早期版本的兼容性而出现。该列没有任何数据积累;它将始终为零。作为该列的替代,您可以查询V$EVENT_HISTOGRAM视图中相应EVENT列的值为latch free或的行latch:%。 |
SLEEP[5 , 6, 7 , 8 , 9 , 10 , 11] | NUMBER | 这些列已被弃用,仅出于与Oracle早期版本的兼容性而存在。这些列没有任何数据累积。 |
WAIT_TIME | NUMBER |
模拟
创建测试用存储过程
Create or replace procedure dummy is
begin
null;
end;
/
开启session 1
Begin
Dummy;
Dbms_lock.sleep(1000);
End;
/
开启session 2
alter procedure dummy compile; => session 2 将会被 'library cache pin' 堵塞。
---开启session 3
alter procedure dummy compile; => session 3 将会被 'library cache lock'堵塞。
开启session 4
select * from (select event,total_waits,time_waited,TIME_WAITED_MICRO,AVERAGE_WAIT,TOTAL_TIMEOUTS from v$system_event where wait_class <> 'Idle' order by 3 desc) where rownum<=50;