<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    走自己的路

    路漫漫其修遠(yuǎn)兮,吾將上下而求索

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      50 隨筆 :: 4 文章 :: 118 評(píng)論 :: 0 Trackbacks
         我們知道給資源上鎖可以使我們串行化地訪問(wèn)資源,oracleplsql開(kāi)發(fā)人員提供了DBMS_SQL包用來(lái)管理USERL LOCK鎖資源。這種鎖可以使得多個(gè)session串行的執(zhí)行某個(gè)存儲(chǔ)過(guò)程,還可以用來(lái)排他的訪問(wèn)某個(gè)外部設(shè)備或服務(wù),甚至可以檢測(cè)事務(wù)的提交或回滾(提交或回滾時(shí)鎖的釋放)。

    有人說(shuō)我在java端調(diào)用db的存儲(chǔ)過(guò)程,可以使用synchronized lock來(lái)串行的調(diào)用存儲(chǔ)過(guò)程。那就不需要db lock呢?因?yàn)楫?dāng)java端應(yīng)用服務(wù)器down的時(shí)候,存儲(chǔ)過(guò)程已經(jīng)在執(zhí)行了,但是可能oracle sessionRACdb)并沒(méi)有立即釋放掉。當(dāng)我們重啟應(yīng)用服務(wù)器后,其實(shí)后臺(tái)的以前的存儲(chǔ)過(guò)程還在執(zhí)行,如果再次調(diào)用存儲(chǔ)過(guò)程,這就無(wú)法保證存儲(chǔ)過(guò)程的串行執(zhí)行了。所以說(shuō)存儲(chǔ)過(guò)程的同步鎖是必須放在Oracle db端的。

    DBMS_LOCK包具有下面幾個(gè)API,主要說(shuō)明以下幾個(gè),其他的可以參考oracle相應(yīng)文檔,我們這里只用X鎖(排他鎖也稱(chēng)寫(xiě)鎖)。

     

    PROCEDURE DBMS_LOCK.ALLOCATE_UNIQUE

        (lockname 
    IN VARCHAR2

        ,lockhandle OUT 
    VARCHAR2

        ,expiration_secs 
    IN INTEGER DEFAULT 864000);

     

    參數(shù)

    描述

    lockname

    鎖的名稱(chēng)

    lockhandle

    與該名稱(chēng)相對(duì)應(yīng)的鎖的唯一標(biāo)識(shí)

    expiration_secs

    這種名稱(chēng)到鎖的映射的保存時(shí)間

    當(dāng)多個(gè)session用同樣的名字lockname來(lái)獲取唯一標(biāo)識(shí)字符串時(shí),不同的session用同樣名字獲取的lockhandle是相同的,lockname是最大128位的字符串,而且是大小寫(xiě)敏感的,鎖的名字最好不要用"ORA$"打頭,這種鎖的名稱(chēng)是被oracle保留的名稱(chēng)。DBMS_LOCK.ALLOCATE_UNIQUE執(zhí)行完后就會(huì)commit所以不能被trigger調(diào)用。所有獲得的映射都為存放在SYS用戶(hù)DBMS_LOCK_ALLOCATED視圖中。

    FUNCTION DBMS_LOCK.REQUEST
        (id 
    IN INTEGER
        ,lockmode 
    IN INTEGER DEFAULT X_MODE
        ,timeout 
    IN INTEGER DEFAULT MAXWAIT
        ,release_on_commit 
    IN BOOLEAN DEFAULT FALSE)
    RETURN INTEGER;
     
    
    FUNCTION DBMS_LOCK.REQUEST
        (lockhandle 
    IN VARCHAR2
        ,lockmode 
    IN INTEGER DEFAULT X_MODE
        ,timeout 
    IN INTEGER DEFAULT MAXWAIT
        ,release_on_commit 
    IN BOOLEAN DEFAULT FALSE)
    RETURN INTEGER;
     

    參數(shù)

    描述

    id

    鎖的唯一標(biāo)識(shí)

    lockhandle

    DBMS_LOCK.ALLOCATE_UNIQUE返回的handle

    lockmode

    鎖的模式

    timeout

    等待時(shí)間

    release_on_commit

    COMMIT or ROLLBACK事務(wù)時(shí)是否釋放鎖

    返回值

    描述

    0

    成功申請(qǐng)到鎖

    1

    超時(shí)

    2

    死鎖

    3

    參數(shù)錯(cuò)誤

    4

    已經(jīng)擁有特定id或handle的鎖

    5

    不合法的lockhandle

    用戶(hù)定義的鎖標(biāo)識(shí)必須在 0 1073741823. 鎖標(biāo)識(shí)在范圍2000000000 2147483647 oracle公司預(yù)先保留。推薦用lockhandle的方法獲得鎖,因?yàn)殒i的名稱(chēng)是比較容易辨別的,也是比較容易描述的。第一種方法不被oracle推薦。

    在共享服務(wù)器模式和分布式事務(wù)時(shí)我們最好把release_on_commit設(shè)置為true。

    FUNCTION DBMS_LOCK.RELEASE
        (id 
    IN INTEGER)
    RETURN INTEGER;
    FUNCTION DBMS_LOCK.RELEASE
        (lockhandle 
    IN VARCHAR2)
    RETURN INTEGER;

    參數(shù)

    描述

    id

    鎖的數(shù)字標(biāo)識(shí)

    lockhandle

    ALLOCATE_UNIQUE返回的鎖的handle

    返回值

    描述

    0

    成功

    3

    參數(shù)錯(cuò)誤

    4

    并沒(méi)有擁有特定的鎖

    5

    不合法的lockhandle

    RELEASE 函數(shù)用來(lái)釋放先前申請(qǐng)的鎖。當(dāng)鎖不用時(shí)最好立即釋放,這是很好的習(xí)慣。鎖本身就是寶貴的資源,并且可以盡早釋放被鎖住的資源,而且可以有效地避免死鎖。

    如何使用這些api,很容易只要在我們的存儲(chǔ)過(guò)程之前或者之后調(diào)用申請(qǐng)鎖,釋放鎖(或者在事務(wù)提交或rollback的時(shí)候自動(dòng)釋放鎖)就可以了,但這樣也帶來(lái)了存儲(chǔ)過(guò)程代碼的侵入性,每個(gè)存儲(chǔ)過(guò)程都必須調(diào)用申請(qǐng)鎖,釋放鎖。我們可以寫(xiě)一個(gè)wrapper把鎖的申請(qǐng)和釋放包裹起來(lái)。類(lèi)似于模板模式。


    create or replace package FRM_TEST_TESTING is
         
    PROCEDURE loop4_specific_round;

    end FRM_TEST_TESTING;

    create or replace package body FRM_TEST_TESTING is

      
    -- Function and procedure implementations
      --PROCEDURE loop4_specific_round(p_loop_count IN INTEGER) AS
      PROCEDURE loop4_specific_round AS
      
      PRAGMA AUTONOMOUS_TRANSACTION;
      
      
    BEGIN   
        
    FOR r IN 1 .. 60 LOOP
          SYS.dbms_lock.sleep(
    20);
          
          DBMS_OUTPUT.PUT_LINE(
    'During testing SP executing. '|| to_char(sysdate,'MM/DD/YYYY HH24:MI:SS'));
          
    insert into TEST_TEST
            (name, Creationtime)
          
    values
            (
    '1111', sysdate);  
          
        
    commit;
        
    END LOOP;
       
        
      
    END loop4_specific_round;
    end FRM_TEST_TESTING;

    create or replace package frm_test_task_pkg is
     
      
    function  frm_test_task_func(i_lock_name in varchar2, i_procname in varchar2, i_expiration_time Integer default 864000, i_wait_time Integer default DBMS_LOCK.maxwait) return number;

    end frm_test_task_pkg;


    create or replace package body frm_test_task_pkg is

      
    procedure app_task_wrapper_proc(i_procname in varchar2as
      
      PRAGMA AUTONOMOUS_TRANSACTION;  
       cur BINARY_INTEGER :
    = DBMS_SQL.OPEN_CURSOR;
       fdbk BINARY_INTEGER; 
      
    begin
         DBMS_SQL.PARSE (cur, 
          
    'begin ' || i_procname  || ';end;',
         DBMS_SQL.NATIVE);
         fdbk :
    = DBMS_SQL.execute (cur);
         DBMS_OUTPUT.put_line(
    'Fetch rows : ' || fdbk);
         DBMS_SQL.close_cursor(cur);
         
    commit;
      
    end app_task_wrapper_proc;
      
      
    function  frm_test_task_func(i_lock_name in varchar2, i_procname in varchar2, i_expiration_time Integer default 864000, i_wait_time Integer default DBMS_LOCK.maxwait) return number is
          v_result     
    number;
          v_lockhandle 
    varchar2(200);
          v_sid   pls_integer;       
       
    begin
        dbms_lock.allocate_unique(i_lock_name, v_lockhandle, i_expiration_time);

        v_result :
    = dbms_lock.request(v_lockhandle, dbms_lock.x_mode, i_wait_time, true);
        
        
    select sys_context('USERENV','SID'into v_sid from dual;
        
        dbms_output.put_line (to_char(systimestamp,
    'HH24:MI:SS.FF3')||': SID '||v_sid||' requests lock');

        
    if v_result <> 0 then
        dbms_output.put_line(
               
    case 
                  
    when v_result=1 then 'Timeout'
                  
    when v_result=2 then 'Deadlock'
                  
    when v_result=3 then 'Parameter Error'
                  
    when v_result=4 then 'Already owned'
                  
    when v_result=5 then 'Illegal Lock Handle'
                
    end);
         
    else 
              app_task_wrapper_proc(i_procname);
      
    end if;
         
    commit;
         
    return v_result;
      EXCEPTION
       
    WHEN OTHERS
       
    THEN
          
    /*
          || Anonymous block inside the exception handler lets me declare
          || local variables to hold the error code information.
          
    */

          
    DECLARE
             error_code 
    NUMBER := SQLCODE;
             error_msg  
    VARCHAR2 (300) := SQLERRM;
          
    BEGIN
               DBMS_OUTPUT.put_line(error_code 
    || '' ||error_msg);
               
    --RE RAISE ERROR TO CLIENT
               raise;
          
    END-- End of anonymous block.

      
    end frm_test_task_func;

    end frm_test_task_pkg;

     

    java端,應(yīng)用程序只需要調(diào)用frm_test_task_func,把需要串行化的存儲(chǔ)過(guò)程作為參數(shù)傳入。Java端還需要提供db鎖的邏輯名,這樣同步在相同的邏輯名的鎖上的存儲(chǔ)過(guò)程會(huì)同步執(zhí)行。在實(shí)際存儲(chǔ)過(guò)程參數(shù)比較復(fù)雜的情況下,傳參可能是個(gè)問(wèn)題。

    其實(shí)還有一種方法,對(duì)已有的存儲(chǔ)過(guò)程代碼沒(méi)有侵入性,申請(qǐng)和釋放db鎖在java端完成,oracle保證了session斷掉后,會(huì)釋放session占有的鎖資源。所以如果應(yīng)用服務(wù)器down掉后,session在經(jīng)過(guò)一段時(shí)間后會(huì)被釋放,鎖資源也會(huì)被釋放。現(xiàn)在要考慮的是是否在事務(wù)結(jié)束的時(shí)候自動(dòng)釋放鎖,考慮到現(xiàn)在申請(qǐng)鎖是由java端完成的,所以釋放鎖也由java端顯式的調(diào)用release釋放。


    create or replace package body frm_test_task_pkg is

     
    procedure frm_test_lock_acquire(i_lock_name in varchar2, i_expiration_time in Integer default 864000, i_wait_time in Integer default DBMS_LOCK.maxwait, o_result out number, o_lockhandle out varchar2as
          v_result     
    number;
          v_lockhandle 
    varchar2(200);
       
    begin
        
    --acquire a unique lock id
        sys.dbms_lock.allocate_unique(i_lock_name, v_lockhandle, i_expiration_time);
        
        
    --acquire a lock
        v_result := sys.dbms_lock.request(v_lockhandle, dbms_lock.x_mode, i_wait_time, false);
      
        
    --set return values
         o_result := v_result;
         o_lockhandle :
    = v_lockhandle;
     
    end frm_test_lock_acquire;
     
     
    function frm_test_lock_release(i_lockhandle in varchar2return number as
           v_result 
    number;
     
    begin
          
    --release lock according to lockhandle
          v_result := sys.dbms_lock.release(i_lockhandle);
     
          
    return v_result;
     
    end frm_test_lock_release;

    end frm_test_task_pkg;

     

     

    這樣java端需要先調(diào)用frm_test_lock_acquire申請(qǐng)鎖,然后執(zhí)行用戶(hù)邏輯的存儲(chǔ)過(guò)程,最后在顯式的調(diào)用frm_test_lock_release釋放鎖。如果不想每次用到的時(shí)候都去申請(qǐng),釋放鎖,在java端也可以使用模板模式,假設(shè)子類(lèi)實(shí)現(xiàn)execute方法來(lái)完成需要串行化執(zhí)行的存儲(chǔ)過(guò)程,這時(shí)要注意我們可能會(huì)將connection傳入到子類(lèi)的execute方法中,但是子類(lèi)卻不能將connection關(guān)閉掉,因?yàn)槲覀冞€需要在execute方法執(zhí)行完后會(huì)用它來(lái)釋放鎖。當(dāng)然如果子類(lèi)真的把物理的connection關(guān)閉掉也沒(méi)有問(wèn)題,但是現(xiàn)在我們大都使用connection pool,把connection返回給pool的時(shí)候,session的鎖資源并沒(méi)有清除。這樣還需要將傳入到子類(lèi)execute方法的connection封裝一下,或者叫裝飾一下,我們有兩種解決方法:

    ·         oonnection wrapperclose并不關(guān)閉連接或者返回給pool

    ·         connection wrapperclose方法關(guān)閉連接,但是需要在關(guān)閉連接之前釋放鎖,然后在抽象父類(lèi)的方法中只需判斷connection是否close掉,connection.isClosed()方法,如果沒(méi)有close就調(diào)用wrapeerclose方法,既釋放鎖又關(guān)閉連接。

     

    總結(jié)一下:lock放在java端會(huì)出現(xiàn)意外的情況,鎖就必須放在db端,為了避免對(duì)已有存儲(chǔ)過(guò)程代碼的侵入性,可以使用wrapper存儲(chǔ)過(guò)程,由于動(dòng)態(tài)執(zhí)行存儲(chǔ)過(guò)程傳遞參數(shù)是個(gè)難題,所以還是把鎖的申請(qǐng),釋放放在了java端,也避免了對(duì)已有存儲(chǔ)過(guò)程代碼的侵入性,同時(shí)又懶得每次都去申請(qǐng)鎖,釋放鎖,可以在java端使用模板模式,但是使用模板模式時(shí)又怕子類(lèi)不小心關(guān)了connection,而沒(méi)釋放鎖,就將connection封裝了一下,將這個(gè)被封裝后的connection傳到了子類(lèi)的execute方法中。




    主站蜘蛛池模板: 亚洲乱码中文论理电影| 亚洲自国产拍揄拍| 国产成人亚洲综合一区| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 国产精品69白浆在线观看免费| 中文字幕在线视频免费观看| 午夜无码A级毛片免费视频| 四虎影院免费视频| 亚洲成A人片在线观看无码不卡| 亚洲成AV人片在线观看无| 亚洲人成7777影视在线观看| 一个人看的www在线免费视频| 一级毛片无遮挡免费全部| 亚洲免费在线视频播放| 一本色道久久88亚洲综合| 亚洲网红精品大秀在线观看 | 67194在线午夜亚洲| 一区二区在线视频免费观看| 免费精品国产日韩热久久| 国产亚洲AV夜间福利香蕉149| 国产亚洲精品精品国产亚洲综合| 亚洲自偷自偷偷色无码中文| 亚洲综合激情五月丁香六月| 美女被cao网站免费看在线看| 中文字幕在线观看免费视频 | 114一级毛片免费| 亚洲视频在线精品| 中文字幕在线观看亚洲日韩| 国产精品99精品久久免费| 亚洲黄片毛片在线观看| 中文字幕乱码亚洲无线三区 | 亚洲综合色丁香麻豆| 美女被羞羞网站免费下载| 少妇高潮太爽了在线观看免费| 免费久久精品国产片香蕉| 亚洲区精品久久一区二区三区| 亚洲AV一区二区三区四区| 久久久久久国产精品免费无码 | 亚洲v国产v天堂a无码久久| 国产精品亚洲片在线va| 免费国产叼嘿视频大全网站|