latch的基础概念

 select name from v$latchname where name like '%library%';

 latch free

The process waits for a latch that is currently busy (held by another process).

library cache load lock

The session tries to find the load lock for the database object so that it can load the object. The load lock is always obtained in Exclusive mode, so that no other process can load the same object. If the load lock is busy the session will wait on this event until the lock becomes available.

library cache lock

This event controls the concurrency between clients of the library cache. It acquires a lock on the object handle so that either:

One client can prevent other clients from accessing the same object

The client can maintain a dependency for a long time (for example, no other client can change the object)

This lock is also obtained to locate an object in the library cache.

library cache pin

This event manages library cache concurrency. Pinning an object causes the heaps to be loaded into memory. If a client wants to modify or examine the object, the client must acquire a pin after the lock.

入门1)

一直想点文章关于Latch的,又一直没写,一是因为懒,二是一直觉得现在关于Latch的书那么多,还有必要写吗?

后来,看到了一些帖子:

1.一个帖子,帖主发现了大量cache buffer chain,检查过了都是select造成了,遂对oracle声称的shared latch产生质疑。

2.某帖,做了buffer busy wait和cache buffer chain的实验,得出很多结论。可从头到尾,帖子的实验中,buffer busy wait和cache buffer chain都是同时出现的。如果真的那么清楚这两者的区别,能够模拟出一种大量buffer busy wait却没有任何cache buffer chain的实验吗?

如果你也有这些问题,你可能也跟我曾经一样,似懂非懂。所以,终下决心写点东西,以做分享,希望对菜鸟们有些帮助(高手请一笑而过):

请往下看,注意,再次声明,本文不是介绍各种各样latch的高级教程。本文只讲基础,那就是,你知道太多种latch了,可是,你真的知道什么是latch吗?

这点,就要从我们为什么需要latch上来说了,latch到底在保护什么?如果没有latch,什么样的操作会损坏latch想要保护的结构?

(数据结构第一章:链表。)

答:latch保护的是链表。会损坏链表的,不是对于链表上的数据的读写操作---这些操作是完全可以并行的。

会损害链表结构的,是将链表上的块摘下,或者插入新的块的操作----这样的操作,如果不存在并发控制,是会损坏链表的。

譬如,一个链表,a--b--c--e--

如过现在想往b与c间,插入新节点m。那么我就需要更改b的next node的指针,以及c的previous node指针。

如果没有latch做并发控制,那么与此同时,如果另一个进程也向b与c(往a与b,c与e之间不会有问题)之间插入n,也需要去更改b和c的指针,那就有可能造成这条链被破坏,譬如结构变成:

a--b--n

m--d--e

这就是latch的意义,用于挂链,下链!

而对于我们的cbc latch,锁控制的就是从disk上将block读入内存中的链上,以及将链上的block摘下并写回disk。

所以,latch只关心你操作的目标block是否在链上---因为可能牵扯到上链下链,而不关心你这个操作本身是select还是insert还是delete还是update!cbc latch与操作是select还是DML没有一毛关系!

所以,现在再回去看第一个问题----为什么select还是会产生大量的buffer cache chain争用?

现在你看清了,貌似这个问题问的有根有据,其实,问的原因和结果是没有联系的两码事。

那么shared latch是否真的存在吗?cache buffer chain是可share的吗?答案是:YES。并且完全可以通过实验模拟出来清楚的看到他的作用。

----------------------------------------------------------------------------------

(入门2)

先以一个问题引入:

10g中v$latch的底层表X$KSLLD为例,他的全称是

X$KSLLD: [K]ernel [S]ervice [L]? Management [L]atch [D]escriptor

请问,问号这里的L,是什么含义?猜测下,再往下看。

引入些补充知识:

对于操作系统来说,可以划分为三种lock,我们称之为OS Lock:

1.spinlock。最轻量级的lock,当获取不到时,发生自旋,不用发生上下文切换。

2.mutex--互斥量。当获取不到时,将发生上下文切换。好处是,不会使CPU发生空转。但是如果频繁的切换严重消耗资源。

3.semaphores--信号量。与mutex(互斥量类似),当获取不到时,发生上下文转换。但是与mutex的区别是,mutex互斥量,同一时间只能被一个进程获取,而信号量,可以同时好几个进程获取。

从上面我们可以看出,spinlock是最轻量级的而,oracle的latch是封装在系统的spinlock之上。

而oracle也有mutex,11g中,oracle引入了更轻量级的mutex,以取代某些latch。你对此就没有置疑过吗?spin才是最轻量的啊,不用发生上下文转换,mutex要上下文转换的,怎么可能比spin轻量?

MUTEX当然不可能比SPIN更轻量!oracle那么说因为,oracle引入的mutex不是上面所说的OS mutex!而是同样构建于spin之上KGX mutex。

扯远了,我们继续latch:

下面我们再来看X$KSLLD,

X$KSLLD: [K]ernel [S]ervice [L]? Management [L]atch [D]escriptor

问号处的[L],现在你知道了,这里是Lock,但是如果没有上面的知识,你很难理解这里为什么会是Lock。你会觉得,Lock怎么又在这里跟latch扯上关系?

事实上,我也看到某些大师将其翻译成:

X$KSLLD: [K]ernel [S]ervice [L]Latch Management [L]atch [D]escriptor

因为没有以上的知识,无法理解这里为什么又扯上了Lock。但是,有了上文的描述,你知道,这里L就是lock,只不过这个lock的意思不是

oracle里面各种各样的TX,TM等锁,而是OS Lock,用来控制进程间的交互与并行。

继续下去:latch是如何实现的,真的只是内存中的一个标志位那么简单吗?

很多人是这么理解latch的,一个标志位:A。可以有两种选择:

A=0 或者 A=1。任何进程想要对共享的资源进行操作时,先去看A,如果是0,则代表未被使用,那么置位成1,然后自己去使用,

如果是1,则等待。

就这么简单?

对的,你表面上看到的latch就这么简单,可是背后的故事,你知道吗?

现在想想:为什么我们要对共享资源加标志位?因为我们想以此控制对资源的串行访问。但是请别忘了,你这个标志位本身,也是一种资源,对这种标志位本身的访问,也是会发生冲突的!

再往微观处想想:现在标志A为0,而有两个进程P1与P2,在并行运行着,这时,同时要用到A。于是P1先将A从内存读入,检测A是0,于是准备将A置为1,在P1将A写回内存前,P2也将A读入,此时A仍然是0。好了,P2也发出命令将A置1。于是,P1,P2都认为是自己成功将A置为1的,拥有对资源的操作权。

然后呢?链表被破坏了。

----------------------------------------------------------------------------------

1.如果真的这么明白两者的区别,能模拟出一个大量buffer busy waits竞争却没有任何cbc latch争用的实验吗?

相信经过前面的描述,对latch到底是在做什么应该有了一点了解了。

正如第一讲中所阐述的,与select/DML相关的是buffer busy waits,而cache buffer chains与select/DML无关。

那么cache buffer chains与什么有关?

答:索引。准确的说,是唯一索引。

当一个操作,走的是唯一索引的话,那么oracle默认目标block是存在于目标链上的,这时,oracle会以shared的模式去获取

cbc latch。如果之后oracle发现弄错了,这个目标块不存在于链上。那么这时,oracle再会尝试以exclsive模式去获得

cbc latch,以将block挂往链上。

但是,如果是全表扫描,oracle会默认的认为目标block肯定是不存在于目标链上的,oracle会直接以exclsive模式去获得cbc latch。

这个实验是老早以前证明cbc是shared latch做的。实验我懒得再去做一遍了,相信大家看完后大家都能做出来,我就直接把实验结果写出来了。

当然,有兴趣的朋友可以做下把过程贴出来 ,我就偷懒下了,呵呵。

传统的buffer busy waits实验,这么做,我们就可以完全搞清楚cache buffer chains与buffer busy waits。

两个session,对于同一数据块的操作。

a.表上无索引,DML操作。

实验结果:大量cbc latch争用,大量buffer busy waits竞争。

b.表上无索引,SELECT操作。

实验结果:大量cbc latch争用, 无任何buffer busy waits竞争。

c.表上有唯一索引,DML操作。

实验结果:无任何cbc latch争用,大量buffer busy waits竞争。

d.表上有唯一索引,SELECT操作。

实验结果:无任何cbc latch争用,无任何buffer busy waits竞争。

现在,shared latch是否存在,cbc latch到底起什么作用,与buffer busy waits的区别,还要我多说什么吗?

2.第二节留下的问题:操作系统如何来做到操作FLAG A时防止并行带来的冲突?

答:操作系统第二章:原子性操作。

当oracle调用spin函数的时候,操作系统把“一个cpu的读取flag进入内存,检查flag值,值位,写回内存”封装成一个

原子性操作。以保证这个操作不会被中断。flag A不会同时被其他CPU修改。

那么操作系统是如何做到这一点的呢?

答:通过锁BUS LINE(总线)。

CPU到内存的结构是这样的:cpu core+cache+Bus Line.

其中,Bus Line是公用的。你的CPU可以是N核,但是,BUS Line是所有CPU共享的。所有CPU与内存的交互都是

通过这个BUS LINE。所以,当一个CPU发出spin命令时候,会锁住总线,以阻止其它CPU对内存的访问与操作。以此来保证对于标志位

的至位是原子性的。

其实这里面门道还是很多的,还有spin本身也分成很多类型,oracle用的是其中比较原始的一种。不过这些就太贴近操作系统,离oracle太远了。

所以这里就不赘述了。有兴趣的朋友们可以自己去查。

----------------------------------------------------------------------------------------------------

(入门4)

与前三节基本都是自己的研究不同,从这一节开始,很多东西都是外国佬的成果,不过我也做过实验可以基本保证正确。

下面开始:latch是如何运作的。

在9i以前,oracle内部使用自旋--睡眠--自旋--睡眠模式来不断尝试对latch的获取,每次的睡眠时间依次是:

0.01-0.01-0.03-0.03-0.08-0.07-0.16-0.23-0.4-0.39-0.73-0.72-1.39-1.38-2.04-2.04.....

但是,从9i开始,这种情况已经变了,latch不再使用这种模式,不再是很多人印象中那种毫无纪律一窝蜂的抢夺,latch使用一种队列机制。

这个队列的四个特性:

1.FIFO先进先出

2.可插队

3.队列中的latch请求每次只能有一个(第一个)被外部唤醒。队列中的latch请求不会自动醒来。

4.这个队列并不是必定存在的。

当有一个latch正在被以exclusive模式获得时,

此后继续来的对此latch申请分两种情况:

1.如果申请的也是exclusive模式,那么cpu会自旋一定次数(注意,我这里没说是_spin_count次)不断尝试获取,如果

还是没能获取,那么将进入队列中等待。(如果一定次数内能获取,就不用进入队列,所以说这个队列并不是必定存在的)

2.如果申请是shared latch,发现已经被exclusive持有了,那么会一次都不自旋的立刻进入队列之中睡眠。

如果现在这个latch一直不被释放,那么后面队列中的申请永远都不会自动醒来。只有,当一个hold的latch被释放时候,会发送一个唤醒信号给队列,仅唤醒队列中的第一个睡眠的等待。

举个例子,比如,现在一个execute模式的latch在hold,后面接着依次来了5个进程希望获得share latch,发现latch

被以exclusive获得,那么将一次都不自旋的依次进入队列等待。现在这个队列是这样的:

1s-2s-3s-4s-5s,

那么当excluse模式的latch释放后,他会发个信号给等待队列,等待队列中的第一个latch会被唤醒并且去获得shared latch。

所以,这个队列就变成了:

2s-3s-4s-5s!

你会发现,虽然都是share模式的latch!但是并没有一次性被全部唤醒!

所以如果你经常做system的dump,你可能看到过这种情况,那就是一个进程在申请获得以shared方式获得一个latch,

你去检查那个latch,发现这个latch是在以share模式持有的!

所以有了share waiting share!这还不是最神奇的,更神奇的还在后面。

这种latch的队列,是允许插队的。也就是说,譬如现在latch正在被share持有,而等待队列是:

2s-3s-4s-5s

现在来了第6个share模式的申请,那么他并不会进入等待队列中,而是会直接获得latch,因为share与share是兼容的!

我们来说说更神奇的事。在9i的时候,这种方式还有很多bug。

不知道你有没遇到过,一个查询莫名的hang住,或者数据库hang住。如果去做个trace,你可能会发现,进程正在等待获得一个latch,而当你再去检查那个latch时居然发现,那个latch并没有被任何进程持有!是free的!也就是说,latch request waiting for a free latch!

这种bug的原因就是因为这个latch request处在上面所说的队列中,并且由于某些原因,当前面latch释放时并没有将它唤醒,而它也绝对不会自己醒来。

如果你去找Oracle开SR,Oracle会让你用这个隐藏参数解决:

_enable_reliable_latch_waits=false.

reliable--依赖。现在应该能看懂这个隐藏参数的意思了吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值