使用PLSQL演示TM锁互斥现象

PLSQL代码如下:

create or replace package pkg_show_tm is

  -- Author  : DEX
  -- Created : 1/24/2013 8:29:26 PM
  -- Purpose : 演示TM锁的兼容性
  -- 需要事先定义表名,任意表即可
  -- Action  : Session 1  : exec pkg_show_tm.blocker ;
  --           Sesssion 2 : set serveroutput on ;            
  --                        exec pkg_show_tm.waiter ;
  tb_name varchar2(20) := ‘t‘ ;
  -- Public type declarations
  procedure blocker ;
  procedure waiter ;

end ;
/


create or replace package body pkg_show_tm is
  Type rec is record(
    lockst   varchar2(2000),
    lockmode varchar2(20));
  Type t is table of rec;
  g_statement t;
  /*
    进程间通信,发送msg
  */
  procedure send(msg varchar2) is
    stats integer;
  begin
    dbms_pipe.pack_message(item => msg);
    stats := dbms_pipe.send_message(msg);
    if stats != 0 then
      raise_application_error(-20999, msg || ‘ error in blocker ‘);
    end if;
  end;
  /*
    进程间通信,接收msg
  */
  procedure recive(msg varchar2) is
    stats integer;
  begin
    stats := dbms_pipe.receive_message(msg);
    if stats != 0 then
      raise_application_error(-20999, msg || ‘ error in waiter ‘);
    end if;
  end;
  /*
    输出:
    Space     RS        RX        S         SRX       X         
    ************************************************************
  */
  procedure output_title is
  begin
    dbms_output.put(rpad(‘Space‘, 10, ‘ ‘));
    dbms_output.put(rpad(‘RS‘, 10, ‘ ‘));
    dbms_output.put(rpad(‘RX‘, 10, ‘ ‘));
    dbms_output.put(rpad(‘S‘, 10, ‘ ‘));
    dbms_output.put(rpad(‘SRX‘, 10, ‘ ‘));
    dbms_output.put_line(rpad(‘X‘, 10, ‘ ‘));
    dbms_output.put_line(rpad(‘*‘, 60, ‘*‘));
  end;
  
  /*
    session 1 = blocker 
    循环执行加锁操作 阻塞session 2
  */
  procedure blocker is
  begin
    for i in 1 .. g_statement.last loop
      execute immediate g_statement(i).lockst;
      send(g_statement(i).lockmode);                  --发送消息
    
      recive(g_statement(i).lockmode || ‘s‘);         --等待接收消息,以判断是否可以继续执行下一个加锁操作
      commit;
    end loop;
  end;
  /*
    session 2 = waiter 
    循环执行加锁操作,以判断与session 1 是否互斥
  */
  procedure waiter is
  begin
    output_title;
    /* output :
    Space     RS        RX        S         SRX       X         
    ************************************************************
    */
    for i in 1 .. g_statement.last loop
      recive(g_statement(i).lockmode);                --等待session 1 发送的消息,以判断session 1 是否已经成功加锁
      dbms_output.put(rpad(g_statement(i).lockmode, 10, ‘ ‘));
      /* output :
        Space     RS        RX        S         SRX       X         
        ************************************************************
        RS
      */
      for j in 1 .. g_statement.last loop
        /*
          这里如果session 2 中的加锁操作与session 1 如果互斥,则会爆出
          ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
          为了省事,直接加others捕获
        */
        begin
          execute immediate g_statement(j).lockst;
          dbms_output.put(rpad(‘Yes‘, 10, ‘ ‘));
          commit;
        exception
          when others then
            dbms_output.put(rpad(‘No‘, 10, ‘ ‘));
            commit;
        end;
      end loop;
      dbms_output.put_line(‘ ‘);
      /* output :
        Space     RS        RX        S         SRX       X         
        ************************************************************
        RS        Yes       Yes       Yes       Yes       No
      */
      send(g_statement(i).lockmode || ‘s‘);     --发送消息给session 1
    end loop;
  end;
/*
操作说明:
lock table tun2_tab in ROW SHARE mode ;           lmode=2
lock table tun2_tab in ROW EXCLUSIVE mode ;       lmode=3   
lock table tun2_tab in SHARE MODE ;               lmode=4
lock table tun2_tab in SHARE ROW EXCLUSIVE MODE ; lmode=5
lock table tun2_tab in EXCLUSIVE MODE ;           lmode=6
*/
begin
  g_statement := t();

  g_statement.extend(5);

  g_statement(1).lockst := ‘lock table ‘ || pkg_show_tm.tb_name ||
                           ‘ in ROW SHARE mode nowait ‘;
  g_statement(1).lockmode := ‘RS‘;

  g_statement(2).lockst := ‘lock table ‘ || pkg_show_tm.tb_name ||
                           ‘ in ROW EXCLUSIVE mode nowait  ‘;
  g_statement(2).lockmode := ‘RX‘;

  g_statement(3).lockst := ‘lock table ‘ || pkg_show_tm.tb_name ||
                           ‘ in SHARE MODE nowait  ‘;
  g_statement(3).lockmode := ‘S‘;

  g_statement(4).lockst := ‘lock table ‘ || pkg_show_tm.tb_name ||
                           ‘ in SHARE ROW EXCLUSIVE MODE nowait  ‘;
  g_statement(4).lockmode := ‘SRX‘;

  g_statement(5).lockst := ‘lock table ‘ || pkg_show_tm.tb_name ||
                           ‘ in EXCLUSIVE MODE nowait  ‘;
  g_statement(5).lockmode := ‘X‘;

end;
/


最好用sys执行,不然需要额外授权grant execute on dbms_pipe to &user ;
_sys@FAKE10> create table t (x int) tablespace users ;

Table created.


Session 1 : 
_sys@FAKE10> exec pkg_show_tm.blocker ;

PL/SQL procedure successfully completed.






Session 2 :
_sys@FAKE10> set serveroutput on
_sys@FAKE10> exec pkg_show_tm.waiter ;
Space     RS        RX        S         SRX       X
************************************************************
RS        Yes       Yes       Yes       Yes       No
RX        Yes       Yes       No        No        No
S         Yes       No        Yes       No        No
SRX       Yes       No        No        No        No
X         No        No        No        No        No

PL/SQL procedure successfully completed.

思路其实很简单,要演示TM锁互斥的时候。需要开启2个session。例如:
session 1 执行
lock table tun2_tab in ROW SHARE mode ; 


session 2 依次执行
lock table tun2_tab in (ROW SHARE|ROW EXCLUSIVE|...) mode ;
查看session 2 是否发生了等待 。

这里 
session 1 = pkg_show_tm .blocker 
session 2 = pkg_show_tm.waiter


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。