LOCK锁
共享锁和互斥锁。
四条规则:
一行只有在被写时才被锁。
一行writer会阻塞另一个对该行进行修改的并行writer
读不会阻塞写
写不会阻塞读
Oracle从来不escalate锁,因为会增大死锁的可能性。
Oracle锁分成:
DML锁:行锁TX,表锁TM
DDL锁
System锁: latch, mutex, internal lock
行锁:
当一个事务有一行的行锁时,事务也会有表锁,这个表锁是为了防止DDL操作和当前数据修改有冲突。
Oracle将锁的信息放在包含该行纪录的数据块中。这里采用了排队机制。当一个事务需要一个当前未被锁住的行锁时,该事务在数据块中添加信息。这个事务修改的所有行,都会指向在数据块的块头中包含的事务id的备份信息。(所以每行都知道是哪个事务锁住了它)
事务结束时, id信息仍然在数据块头中。如果其他事务想修改该行,那么它用前事务id去查看该锁是否 active。如果是,则在锁释放时,当前session收到提醒;否则,当前事务获取锁。
表锁TM
DML锁:保护数据,锁住表或者行.
DDL锁:保护schema object的结构,比如表和视图在数据字典中的定义。
系统锁:保护内部数据库结构,例如data files.
外键一般情况下都需要建立索引。如果没有索引,那么子表就会被频繁的锁住,降低并发性。
除非主键从来不被更新或者删除。
当以下两种情况同时发生,数据库会对子表的整张表加锁:
1。子表的外键列没有索引;
2。一个进程修改了父表的外键值(比如删除一行,或者修改主键的属性)或merge行进父表。
比如,DEPARTMENTS 表有department_id,是子表EMPLOYEES的外键。当EMPLOYEES的department_id列没有索引,那么修改DEPARTMENTS表的某一行时,EMPLOYEES全表都会被锁住。(这个很好理解,没有索引,所以系统不知道应该锁住EMPLOYEES的哪些行,只能全锁.)
对子表的DML操作不会锁父表。
相反的,如果子表上的外键有索引,对父表外键列的修改只会锁住索引指向的那些纪录。
DDL锁:
Exclusive DDL locks:
Share DDL locks:
Breakable Parse locks:
系统锁:
LATCH:协调多个进程同时对共享内存资源的访问。比如对SGA,后台进程例如DBWn,LGWR从SGA中分配内存,为了分配内存,这些进程使用shared pool latch去串行化访问,避免两个进程同时修改shared pool。
解析sql时需要的library cache,也需要latch,不过仅针对库缓存,而不是整个shared pool。
latch spinning: 一个进程连续的请求latch。
latch sleeping:一个进程在更新latch请求前释放了cpu.
v$latch
-----latch专题
Latch到底是什么呢?
是能不能独自访问或执行某一种共享资源的信号灯。从程序上来讲就是一个变量,标记了某个资源的一个状态。
Latch有三种类型,父latch,子latch,独立latch。其中,在v$latch_parent这个表中存放的是独立latch和父latch,每一个latch存放一条记录。子latch放在 v$latch_children表中,每个子latch存放一条记录,只有少数的latch有子latch,所以在v$latch_children表中,其latch name是一样的,但是其地址不一样,表明他保护的block不想同。在v$latch表中,保存了所有的latch的聚合信息,在这里能看到宏观的latch信息,在v$latch_children中可以看到微观的每个latch的信息。
根据概念理解,对于共享的数据结构都是需要使用latch进行保护的,有些数据结构使用一个latch,有些数据结构使用多个latch(子latch),多个latch的使用可以增加并发度,因为不用访问一个资源而占用了整个链表的latch,latch的访问是独享的。
看了latch的概念,那么进程如何得到latch呢?
进程得到latch的方式:willing-to-wait和no_wait两种方式:
Willing-to-wait方式是请求一个latch,如果一次请求没有获得,然后又有经过spin和try again,经过多次的spin(_SPIN_COUNT)如果还没有得到latch则进入到sleep状态,进入sleep前,它需要先安排一下他的后事,不能一睡不起呀(如何醒来,超时和repost),还有就是通知v$session_wait自己发生了latch free wait了,从这里可以知道,每次sleep都会对应着一个latch free wait event。spin也可以称之为active wait,cpu时间片该进程还没有消耗完毕,那么这样进程还是在消耗cpu时间的。这样的方式请求的latch的(level)级别要比自己持有的高才可以,为什么呢?如果a进程占有一个level 为5的latch,它去请求一个level为3的latch,而进程b,占有这个level为3的latch,又去请求那个level 为5的latch,这样会有什么问题呢?因为它是可以去spin的,又是可以去sleep的,sleep之后还是继续重复?那就永远没有完没有了了。所以呢,level的request是有level顺序的,不能随便的请求。
为什么要spin呢?spin是对于多cpu系统而言的,一个cpu是没有必要的,因为,spin是一个进程在cpu时间片内等待一个latch,它会让spin进程去其他的cpu去pin一下,如果有其他的进程释放了该latch,那么就可以得到了,又因为一个cpu,在一个时刻只能处理一个进程,进程切换的代价又很高,要建立上下文环境,所以尽量在cpu没有切换的时候能得到latch最好。Spin的次数也是有一个参数来指定的,_SPIN_COUNT,文档上有一个公式说
_SPIN_COUNT * sleeps / misses 的值越小越好,但是我没有明白过来,请大牛们解释一下。
在v$latch中,有gets,misses。那么gets是什么意思呢?gets是进程通过willng-to-wait得到的latch的次数,misses是willing-to-wait模式并且没有pin也没有得到latch的次数。那么经过多次的spin得到latch计作一个gets,经过多次spin没有的到latch,那就是sleep了。
Sleep,willing-to-wait模式请求latch失败了,然后就要去sleep,sleep要去告诉v$session_wait和V$SYSTEM_EVENT发生了latch free 等待事件。Sleep之后还是要醒来的,要么是闹钟(超时),要么就是告诉别人叫他一下(repost)。Sleep醒来之后,其下次的sleep时间要变长的,不知道为什么,睡得越多,每次睡眠的时间就越长(2s),但是当一个进程持有latch的时候睡眠的时间会很短(4厘秒)。(1秒=1000毫秒(ms),1毫秒=1/1,000秒(s) ,1秒=1,000,000 微秒(μs) ,1微秒=1/1,000,000秒(s), 1秒=1,000,000,000 纳秒(ns), 1纳秒=1/1,000,000,000秒(s) ,1秒=1,000,000,000,000 皮秒(ps), 1皮秒=1/1,000,000,000,000秒(s))
在上面看来,latch如果出现sleeps比较多,造成的影响是比较严重,因为每次request latch都会耗用cpu时间,sleep说明了白白浪费了cpu时间,这样会造成资源的浪费。
No_wait方式去请求一个latch,有两种情况:1、他请求的latch有多个类型相同的子latch可以请求,会先请求一个,如果得不到,就请求下一个同样的latch,如果都扫描了一遍,还是没有得到,则转入willing-to-wait。2、请求的latch级别与请求的相同或者低的时候,因为他有deadlock保护,所以可以。
No_wait方式在v$latch的记录为:IMMEDIATE_GETS 和 IMMEDIATE_MISSES
Latch的cleanup,在latch的使用过程中,可能会出现一些异常,而导致有些latch被异常占有得不到释放,这样就会有问题了,别的进程过来请求不到。出现这样的异常pmon进程会跟进处理,对于其处理的流程来说,最重要的莫过于将没有提交的事物回滚,那么就需要latch支持恢复,那么latch在开始操作前会先写一些信息去latch的恢复区。Pmon 3秒钟会自动运行一下,但是这也使很长的一段时间了,所以在进程在请求一个latch失败多次之后,会post pmon进程去check一下占有这个latch的process是不是正常。
Spin、gets、misses、sleep、cleanup
Spin是对于多cpu系统而设计的,目的是为了能够尽量不发生进程交换的情况下得到latch,该进程去查看别的cpu有没有释放其需要的latch,如果能得到就可以在一次cpu时间内处理完事情,多好呀。
Gets :willing-to-wait模式的not spin和spin(不论一次还是多次spin)的gets
Misses :willing-to-wait 的fail to get without spin
Sleep: 在一次cpu时间内没有得到latch,就会睡眠
IMMEDIATE_GETS: no_wait模式的get
IMMEDIATE_MISSES: no_wait模式的miss
Cleanup 占有latch的进程出现异常,需要PMON进行清理,以免长期占有latch。在进程得到latch之后需要先将恢复数据写到恢复区,保证出现异常之后能够正常恢复。
查看latch的视图:
1、select latch#, name from v$latchname;
可以看到latch的名字
2、Select total_waits from v$system_event where event=‘latch free’;
2、Select sum(sleeps) from v$latch;
上面这两个的结果实一样的。
3、select name, gets, misses, immediate_gets, immediate_misses, sleeps from v$latch order by sleeps;
4、select * from (SELECT file#, dbablk, class, state, TCH FROM X$BH b,v$latch_children lc WHERE b.HLADDR=lc.addr order by TCH desc) where rownum<=10;
此sql可以查询一下hot block,其中 TCH touch count header? 该值大,说明了被访问的次数相当多。X$bh中的HLaddr就是说的在block头的latch地址,正好是v$latch_children的addr。
--------
latch通常保护一组对象,mutex保护一个对象。
mutex:
因为latch是保护一组,所有它比mutex更容易成为瓶颈。
mutex消耗内存少。
internal locks:比latch, mutex更高级复杂
数据字典缓存锁:
文件和日志挂你锁:
表空间和undo段锁:
你也可以手动加锁。
1. SET TRANSACTION ISOLATION LEVEL
2. LOCK TABLE xxx
3. SELECT xxx FOR UPDATE
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/11903161/viewspace-681574/ ,如需转载,请注明出处,否则将追究法律责任。