晶晶实验四_讨论闩 篇

来源:互联网 发布:知乎 电影书籍推荐 编辑:程序博客网 时间:2024/05/01 07:52



因为闩的种类过多,以后分别在各自领域详细介绍.今天先总的介绍闩的概念.

  闩不象锁,锁的结构非常复杂,在晶晶实验五中,我们就要讨论锁的结构.闩的实现相对与锁来说就非常简单了,大多数闩没

有等待者,持有者等等这些队列,且大部分闩没有共享,独占等模式.(当然有部分闩例外).闩是内存中的一些位,使用CPU的硬

指令(test and set 或swap)将其值设置为0或非0,表示是否被持有,他的实现代码极其简单,因此,闩的获取和释放都是非常

快的.闩的持有过程也应该非常短暂.比如:有些闩是保护lock和 pin的释放获取过程,当lock 和pin 获取完毕,闩就可以释放

了,真正的操作在lock和 pin的保护下完成.闩不会伴随整个操作.

  闩分三种 1,长闩;2,短闩;3,共享闩

  长闩:当请求闩而没有立即获得.请求闩的进程会被放进一个等待者队列.当闩的持有者释放时,会通知队列中第一个等待者

.

  短闩:当请求闩而没有立即获得,多CPU下会先自旋,如果自旋仍然得不到闩会进入睡眠状态,N厘秒后醒来再次自旋,如果得

不到,再次睡眠...依次循环直到获得闩.睡眠的时间是1,4,4,8,8,16,16(单位厘秒)...每两次倍增.每睡眠一次都对应一次

latch free等待事件.
    
自旋的伪码大概如下:
    定义全局变量 latch=0
        读取(资源ID)
        {
           int bz=1:
            while(bz==1)
             {
                Swap(latch,bz); 
              }
           访问资源;
           latch=0;
        }    
  在X86系统中,Swap函数就是XCHG指令.他是一条CPU的硬指令,他的执行不会被打断.
  因为自旋的过程是一个忙测试循环,在此期间进程会一直占有CPU的时间片,通常还会使用指令禁止进程切换,单CPU下在自

旋期间,持有闩的进程不可能得到执行,也不可能释放他所持有的闩,所以在单CPU下自旋是没有意义的.

  共享闩: 具文档中介绍,共享闩只有一个,就是Cache Buffer Chain闩(简称CBC闩)。

编写了个小脚本,反复读取某个表的同一个块.看此共享闩是否有争用:
create or replace procedure my_cursor is 
  cursor aa is select id from jj_2 where rowid='AAAMvgAAHAAAAGwAAA';
  x number(5);
begin
  for i in 1..300000 loop
    open aa;
    fetch aa into x;
    close aa;
  end loop;  
end;
/
在执行前先观察CBC闩的情况:
SQL> select sid,event,time_waited,time_waited_micro,total_timeouts,total_waits from v$session_event where 

(sid=190 or sid=203) and event='latch: cache buffers chains';

       SID EVENT                          TIME_WAITED TIME_WAITED_MICRO TOTAL_TIMEOUTS TOTAL_WAITS
---------- ------------------------------ ----------- ----------------- -------------- -----------
       190 latch: cache buffers chains              0                39              0           4
       203 latch: cache buffers chains              0               391              0           9
在一个会话中执行上面的脚本,查看V$session_event视图,可以发现没有新的相关CBC闩的等待,分别在两个会话中执行上

面的脚本,执行一次后,查看V$session_event中的等待事件:
       SID EVENT                          TIME_WAITED TIME_WAITED_MICRO TOTAL_TIMEOUTS TOTAL_WAITS
---------- ------------------------------ ----------- ----------------- -------------- -----------
       190 latch: cache buffers chains              0                39              0           4
       203 latch: cache buffers chains              0               574              0          13
再试一次:
       SID EVENT                          TIME_WAITED TIME_WAITED_MICRO TOTAL_TIMEOUTS TOTAL_WAITS
---------- ------------------------------ ----------- ----------------- -------------- -----------
       190 latch: cache buffers chains              0                75              0           5
       203 latch: cache buffers chains              0               682              0          17
发现第一次190号会话的CBC闩没有增加,而203号会话增加了,第二次执行后两个绘画都增加了。再进一步了解一下闩的相关

视图:
SQL> select misses,gets,sleeps,spin_gets from v$latch where name='cache buffers chains';

原值:
    MISSES       GETS     SLEEPS  SPIN_GETS
---------- ---------- ---------- ----------
      1289   14632424         32       1260

只一个会话执行语句:
    MISSES       GETS     SLEEPS  SPIN_GETS
---------- ---------- ---------- ----------
      1289   15232803         32       1260
可以发现,MISSES没有增加。只是GEST增加了60多万次。

两个会话都执行脚本:

    MISSES       GETS     SLEEPS  SPIN_GETS
---------- ---------- ---------- ----------
      1479   16433137         42       1441

只一个会话执行的时候MISSES 并没增加,两个都执行的时候有少量增加,增加了将近200次。两个会话,几乎同时60多万次的

进行CBC请求,只有近200次的MISSES,因此这里CBC闩只是有限共享。

   父闩; 子闩;

   有些闩只能有一个,但有些闩可以有众多的子闩,这是为什么呢?只要是闩所保护的资源是成组出现的,且互相之间互不影

响,那么就可以拥有众多子闩,例如:Cache Buffer Chain闩(简称CBC闩).他主要是保护buffer cache 中 hash表的hash链,因

为hash链不止一条,且各个HASH链没什么影响,所以可以为每个HASH链都准备一个闩,这样CBC闩就拥有了众多的子闩,在8I中

就是这样的,但9I改变了算法,减少了每个HASH链中buffer head的数量.因此,9I中一个CBC子闩会对应多条HASH链,象CBC这样

有众多子闩的闩就可以称为非独立闩.或组闩.还有一种情况,并非所保护的资源是成组出现但也有多个子闩,如: redo copy

闩,子闩的个数,oracle默认为CPU个数的两倍,oracle是为了限制同时向log buffer复制日志缓存的进程的数量. 从而限制取

得此闩进程的数量.因此,此闩也是非独立闩,但子闩数量不多.

  相关视图:
   观察闩的情况是用v$latch 和v$latch_children两个视图, v$latch视图基本上是v$latch_children分组求合的结果.以

前见坛友问过此视图的列意义,在此简单说明几列:
   gets       以愿意等待模式请求闩,并获得的次数.
   misses     以愿意等待模式请求闩,自旋或睡眠后获得的次数.
   sleeps     以愿意等待模式请求闩,睡眠的次数.
   spin_gets  第一次自旋即成功获得闩的次数.
   sleep1     睡眠一次后获得闩的次数
   sleep2     睡眠两次后获得闩的次数
   sleep3     睡眠三次后获得闩的次数
   sleep4     睡眠四次以上获得闩的次数
sleeps 和 sleep1,2,3,4的关系:
sleeps=sleep1*1+sleep2*2+sleep*3+sleep4*N...
在多CPU环境中每次睡眠醒来都要自旋,假如:第三次睡眠后醒来,自旋获得闩了,spin_gets不再增加.他只反映第一次自旋即
获得闩的次数.sleep3加1.sleeps加3.文档中有个相关自旋的公式:  sleeps/(misses-spin_gets) 这个公式的结果反映了几

次睡眠才会获得闩,这个值是个平均值.大家可以根据这个值的高低来判断自旋的效率.
   自旋到底有什么样的作用呢?? 自旋可以减少进程切换所带来的负担,如果一个进程刚刚获得CPU时间片,但是只因为无法

获得闩而让出了时间片,浪费了刚刚到手的时间片,如果有很多进程都是因为无法理解获得闩而被切换走时间片,众多的进程

切换操作也会带来一些系统的负担.
   补充一下进程的切换,它到底有多大的负担呢??我们都知道每个进程是运行在他的上下文中,从A进程切换到B进程,主要完

成的任务就是把A进程的上下文保存起来,而把B进程的上下文调出,进程切换的主要负担就是上下文的切换,上下文到底有多

少内容呢??参考LINUX的原码,在arch/i386/kernel/entry.S中,我们可以看到上下文主要是指CPU中的所有寄存器,保护A进程

的上下文就是当A进程运行时,把所有的CPU中寄存器的当前值压入堆栈.在32位系统中也就是100个字节左右,简单点说,上下

文切换的负担就是将CPU中的寄存器的值转入内存。
原创粉丝点击