1、row cache lock
oracle将数据子典信息存于SGA内的行高速缓冲区(或dictionary cache),行高速缓冲区位于共享池内,可以通过如下命令进行确认:
SQL> select pool,name,bytes from v$sgastat where name='row cache';
POOL NAME BYTES
------------ ---------------------------------------- ----------
shared pool row cache 3707272
想要修改数据字典内容的进程,应该对其相应的row cache object 获得 row cache lock。其中最具代表性的是sequence,关于sequence的详细介绍,参照博客:http://blog.csdn.net/changyanmanman/article/details/7451009
在获取sequence的nextval过程中,需要修改数据字典信息时,应该对row cache object以SSX(shared sub-exclusive,这个模式就是,对于整个对象是以shared 模式,对于对象的一部分是以exclusive模式获取锁的一种模式。因sequence.nextval 调用修改sequence数据字典信息时,不是修改sequence自身。而只是修改sequence的“接下来的”值。因此,以SSX模式获得 row cache lock) 模式获得row cahce lock。SSX模式之间因为不存在共享性,所以多个进程同时对相同的sequence调用nextval时,发生对于row cache lock的争用。若在获得row cache lock 过程中发生争用,则等待row cache lock事件,row cache lock事件的P1=cache#,表示的不是对象信息,而是row cache 的类型。
ROW CACHE LOCK等待事件是一个共享池相关的等待事件。是由于对于字典缓冲的访问造成的。
- P1 - Cache Id
- P2 - Mode Held
- P3 - Mode Requested
mode 和REQUEST的取值:
- KQRMNULL 0 null mode - not locked
- KQRMS 3 share mode
- KQRMX 5 exclusive mode
- KQRMFAIL 10 fail to acquire instance lock
如果是RAC/OPS(ops的意思是oracle并行服务)环境,前台进程发出锁请求,LCK0进程发出锁请求。如果是单实例模式,由前台进程直接发出锁请求。
在RAC/OPS环境下,前台进程会循环等待锁的获取,最多会等待60秒钟。在单实例环境,前台进程会循环1000次,等待3秒钟。PMON进程无论在哪种模式,都会等待5秒钟。
要注意的是单实例模式下和多实例模式下申请该锁调用的模块是不同的(kqrget()- 单实例,kqgigt()- 多实例)。
如果发现这个等待十分高,一般来说可能由于2种原因:
一是共享池太小了,需要增加共享池;
另外一种情况是SQL分析过于频繁,对于共享池的并发访问量过大。
对于任何一种情况,绝大多数情况下加大共享池会有助于降低该等待,不过加大共享池的时候也要注意,并不一定所有的情况下增加共享池都会有明显的效果,特别是对于第二种情况,精确的分析十分重要。另外进一步分析,弄清楚哪些ROW CACHE的等待最为严重,有助于解决问题。
比如说如果发现dc_sequences等待比较严重,那么单纯的增加共享池的大小是起不到应有的作用的,而是要通过优化SEQUENCE的访问性能(比如CACHE,NOORDER等)来达到目的。对于早期的版本(7,8.0),SEQUENCE_CACHE_ENTRIES参数的调整也十分关键。
我们可以对row cache 文件进行转储,执行命令:
SQL> alter session set events 'immediate trace name row_cache level 12';
除了sequence之外,机会没有如此频繁的修改行高速缓冲区的信息的。因此,出现row cache 等待时,需要确认sequence上是否赋予了nocache属性,ops环境下,为了保障sequence的顺序,以nocache属性创建了sequence时,经常出现row cache lock等待现象。rac环境下,使用cache属相同时,保障各节点之间sequence的顺序是可能的。
2、enq:SQ-contention, DFS lock handle(SV)
oracle为了管理sequence使用了以下三种锁:
row cache lock:在调用sequence.nextval过程中,将数据字典信息进行物理修改时获取。赋予了nocache属性的sequence才发生。
SQ锁:在内存上缓存(cache)的范围内,调用sequence.nextval期间拥有此锁。赋予了cache属性的sequence才发生。
SV锁:RAC上节点之间顺序得到了保障的情况下,调用sequence.nextval期间拥有。赋予了CACHE+ORDER属性的sequece上发生。
赋予了cache属性的sequence调用nextval期间,应该以SSX模式获得SQ锁。许多会话同时获取SQ锁而发生争用过程中,若发生争用,则等待enq:SQ-contention事件。enq:SQ-contention事件的P2值是sequence的object ID,因此,若利用P2值与DBA_OBJECTS的结合,就可以知道对哪个 Sequence发生了等待对象。
创建Sequence赋予的CACHE值较小时,有enq:SQ-contention等待增加的趋势,CACHE值较小,内存上事先CACHE的值很快被耗尽,这时需要将数据字典信息物理修改,再次执行CACHE的工作,在此期间,因为一直要拥有SQ锁,相应的Enq:SQ-contention事件的等待时间也会延长,很不幸的是,在创建Sequence时,将CACHE值的缺省值设定为较小20, 因此创建使用量最多的Sequence时,CACHE值应该取1000以上的较大值。
偶而一次性同时创建许多会话,有时会发生enq:SQ-contention等待事件,其理由是V$SESSION.AUDSID(auditing sessionid) 列值是利用Sequence创建的,oracle在创建新的会话后,利用名为SYS.AUDSESS$的sequence的nextval创建AUDSID的值,SYS.AUDSESS$ Sequence的CACHE大小的缺省值设定为 20,许多会话同时连接,可以将SYS.AUDSESS$ sequence的CACHE大小扩大至1000,以此可以解决 enq:SQ-contention等待问题。
RAC上创建Sequence时,在赋予了CACHE属性的状态下:
(1)若没有赋予ORDER属性,则各节点将会把不同范围的Sequence值CACHE到内存上,比如拥有两个节点的RAC环境下,创建CACHE值为100的 sequence时,1节点会使用1-100,2节点会使用101-200。 使用时从各自节点取sequence。
(2)若两个节点之间会通过递增的使用sequence,必须赋予如下ORDER属性。
SQL>Create sequence ordered_sequence cache 100 order;
在order 的情况下,2个节点取的sequence是递增的。 下文会有示例来说明这两种情况。
如果已赋予CACHE+ORDER属性的sequence, oracle使用SV锁进行行同步,即,对赋予了ORDER属性的sequence调用nextval时,应该以SSX模式拥有SV锁,在获取SV锁过程中,若发生了争用,不是等待ROW CACHE或者是enq:SQ-contention,而是等待名为DFS lock handle事件,正因如此V$EVENT_NAME视图上不存在类似与"enq:SV-contention"
DFS lock handle事件是在OPS或者RAC环境下,除了 高速缓冲区 同步之外,还有 行高速缓冲区 或者 库高速缓冲区 同步获取锁的过程中的等待事件。 若保证全局范围内获得锁,在此过程中会发生DFS look handle等待,在获取SV锁的过程中发生的DFS lock handle等待事件的P1,P2值与enq:SQ-contention等待事件相同(p1=mode+namespace,p2=object#).因此会从P1值能确认是否是SV锁,通过P2可以确认哪些是Sequence发生过等待.
SV锁争用问题发生时的解决办法与SQ锁的情况相同,就是CACHE值进行适当的调整,这也是唯一的方法。
测试1:NOORDER的Sequence
node1:
SQL> create sequence seq_noorder start with 1 increment by 1 cache 20 NOORDER;
Sequence created.
SQL> select seq_noorder.nextval from dual;
NEXTVAL
----------
1
SQL> /
NEXTVAL
----------
2
SQL> /
NEXTVAL
----------
3
Node2:
SQL> select seq_noorder.nextval from dual;
NEXTVAL
----------
21
SQL> /
NEXTVAL
----------
22
SQL> /
NEXTVAL
----------
23
node2上不是从4开始的,是从21开始的,因为node1已经cache了20个。
测试2: ORDER的Sequence
node1:
SQL> create sequence seq_order start with 1 increment by 1 cache 20 ORDER;
Sequence created.
SQL> select seq_order.nextval from dual;
NEXTVAL
----------
1
SQL> /
NEXTVAL
----------
2
SQL> /
NEXTVAL
----------
3
Node2:
SQL> select seq_order.nextval from dual;
NEXTVAL
----------
4
SQL> /
NEXTVAL
----------
5
SQL> /
NEXTVAL
----------
6
指定Order 之后,取的序列就是顺序的。
小结:
没有赋予CACHE属性时,不管ORDER属性是否或RAC环境是否,一直等待ROW CACHE事件,ROW CACHE LOCK是否可以在全局范围内使用的锁,单实例环境或多实例环境同时可以发生。
Oracle Sequence默认是NOORDER,如果设置为ORDER;在单实例环境没有影响,在RAC环境此时,多实例实际缓存相同的序列,此时在多个实例并发取该序列的时候,会有短暂的资源竞争来在多实例之间进行同步。因次性能相比noorder要差,所以RAC环境非必须的情况下不要使用ORDER,尤其要避免NOCACHE ORDER组合。
在RAC等多节点环境下,sequence的CACHE值给性能带来的影响比单节点环境更严重,因此,尽量赋予CACHE+NOORDER属性,并要给与足够大的CACHE值。
但是如果使用了Cache,如果此时DB 崩溃了,那么sequence会从cache 之后重新开始,在cache中没有使用的sequence 会被跳过。即sequence 不连续。 所以只有在多节点高峰并发量很大的情况且对连续性要求不高的情况下,才使用:noorder + cache
根据创建Sequence时赋予的属性,整理等待事件的结果如下:
NOCACHE : --> row cache lock
CAHCE+NOORDER --> enq: SQ-contention(SQ lock)
CACHE+ORDER(RAC): --> DFS look handle(SV lock)