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

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

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

    posts - 42,comments - 83,trackbacks - 0
            Weblogic92中,不少系統(tǒng)為了降低系統(tǒng)的內(nèi)存開銷,抑或防止session丟失,管理人員會是用JDBC store來存放session信息。不過在使用這種配置的時候,不少客戶反映會碰到約束沖突的異常信息,如下,


    <Jan 23, 2009 10:07:33 AM CST> <Error> <HTTP Session> <BEA-100087> <The jdbc session data for session id: DcwFJ5mH1HbFrVR2L6z5xpyGXcWLbJFxHrxP2ZF6jQ1hVJ32Gmfl ctx:testWeb dblat:1232676391562 triggerLAT:0 has been modified by another server in the cluster.
    java.sql.SQLException: ORA-00001: unique constraint (SYSTEM.SYS_C003007) violated

     at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
     at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
     at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
     at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:743)
     at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:216)
     Truncated. see log file for complete stacktrace

            本文就對這個問題作一下分析,看看什么樣的原因會引起上述問題。

            首先,我們想一下,為什么會出現(xiàn)諸如ORA-00001,這樣的錯誤。下面是ORA-00001的問題官方描述,
            This error means that an attempt has been made to insert a record with a duplicate (unique) key. This error will also be generated if an existing record is updated to generate a duplicate (unique) key. Typically this is a duplicate primary key, but it need not be the primary key.
             如上所述,這類問題多是由于我們插入或更新紀(jì)錄時,出現(xiàn)duplicate (unique) key導(dǎo)致的。Weblogic92中,使用JDBC store來存儲session的時候,所有的session會被放入一張叫做wl_servlet_sessions的表中,我們現(xiàn)在看看wl_servlet_sessions,哪些列可能導(dǎo)致duplicate key呢?wl_servlet_sessions的結(jié)構(gòu)如下:
     create table wl_servlet_sessions
    ( wl_id VARCHAR2(100) NOT NULL,
        wl_context_path VARCHAR2(100) NOT NULL,
        wl_is_new CHAR(1),
        wl_create_time NUMBER(20),
        wl_is_valid CHAR(1),
        wl_session_values LONG RAW,
        wl_access_time NUMBER(20),
        wl_max_inactive_interval INTEGER,
       PRIMARY KEY (wl_id, wl_context_path) );
    對于不同的database,具體表結(jié)構(gòu)請參考 http://e-docs.bea.com/wls/docs92/webapp/sessions.html。從表結(jié)構(gòu)中我們可以看到,weblogic使用wl_id,wl_context_path作為聯(lián)合主鍵,由于對于一個session application而言,他的wl_context_path是固定,所以引發(fā)ORA-00001的只有wl_id。那么到底是insert,還是update引起這個問題的呢?Weblogic中,更新sessio的時候,只更新session data,不會更新其primary key,也就是說session update不會引起ORA-00001,原因只能是insert,即嘗試插入相同wl_id數(shù)據(jù)的時候,會引發(fā)該問題。即使同樣是插入操作,其直接原因也可能分為如下幾種情況,
    1: weblogic的bug
    2: 應(yīng)用場景問題(比如load balancer不能保證session stick)

            下面我會分別介紹一下這兩種情況,

            1:Weblogic的bug
            也許你會問,同一時刻,一個session id不是只能有一個與其對應(yīng)的session object在內(nèi)存中嗎?而且這應(yīng)該由weblogic來保證。是的,正常情況下,客戶端請求進(jìn)到Weblogic的時候,weblogic會檢查cache中是否存在與其session id對應(yīng)的session object,有的話,從cache中取出,沒有的話,它會通過getFromDB()從database中l(wèi)oad,如果還是沒有,這時候才會通過dbCreate去插入一條記錄。 如果中間某一處weblogic沒有控制好的話,問題就來了。我將以如下的一個test作為案例,分析一下具體過程,
            這里將不考慮應(yīng)用場景問題,即session stick可以被保證。假如一個客戶在訪問應(yīng)用系統(tǒng)時,順序訪問了三個應(yīng)用頁面(page 1/2/3),而這三個頁面中, page 1/3會涉及session更新(set attribute),而page 2只讀取session(get attribute). 由于這個問題和session的dbLAT、triggerLAT相關(guān),所以我們主要這里主要關(guān)注 dbLAT、triggerLAT的變化及問題點。
            1.1: client訪問page1, 假如page1中第一次訪問session對象,由于cache和db中均沒有該對象,那么我們會創(chuàng)建一個session, 并以wl_id和wl_context_path作為主鍵,插入紀(jì)錄。page1中作寫setAttribute的動作,請求結(jié)束后,這個對象會被同步到數(shù)據(jù)庫,同時session數(shù)據(jù)被放入內(nèi)存的cache中,如下:

    1   public void sync(HttpSession data) {
    2     // This should be invoked only at the end of the request
    3       
    4       session.syncSession();
    5       cache.put(session.id, data);
    6       
    7 
    8   }


    注意:cache用于限制內(nèi)存中當(dāng)前active的session數(shù),當(dāng)cache滿了的時候,最早進(jìn)入cache的session將被從cache中挪走。默認(rèn)的cache size為1024,這個至可以通過session-descriptor的cache-size配置。

    在來看看session.syncSession()邏輯, 它通過dbUpdate()實現(xiàn),dbUpdate()如下:

     1 private void dbUpdate() throws SQLException {
     2         
     3     if (isModified()) {
     4     conn = getConnection(jdbcProps);
     5     stmt = conn.prepareStatement(jdbcCtx.getUpdateQuery());
     6     int i = 0;
     7     
     8     // the sql update is performed only if the lat in the DB matches the value of dbLAT or triggerLAT.
     9     i = stmt.executeUpdate();
    10     if (i > 0) {
    11           dbLAT = accessTime;
    12           triggerLAT = 0;
    13         }
    14     if (i == 0) {
    15           dbCreate();
    16     }
    17       } else {
    18         // Session Data has not changed so just update last access time
    19         jdbcCtx.updateLAT(this, contextName);
    20       }
    21   }

    這里可以看到,syncSession的時候,首先檢查對象是否作過修改,如果沒有,通過jdbcCtx.updateLAT()去更新triggerLAT,如果做過修改,我們到數(shù)據(jù)庫中檢查對象是否存在,存在的話,更新dbLAT,如果不存在,通過dbCreate()插入紀(jì)錄。我們這一步中,因為是第一個請求,所以db中沒有記錄,要通過dbCreate()插入紀(jì)錄,假如同步進(jìn)數(shù)據(jù)庫的dbLAT為1,由于這是沒有timerTrigger被觸發(fā),它的triggerLAT為0。

            1.2: client訪問完page1后,繼續(xù)訪問page2,由于cache中能夠找到session-id對應(yīng)的對象,我們直接利用cache中的對象,該對象屬性如下:
            Session_A.dbLAT = 1(這里1標(biāo)示某個時間點)
            Session_A.triggerLAT = 0
            請求結(jié)束時,由于我們沒有修改這個session對象(只做了read attribute), 在syncSession的時候,我們會把這個update操作交給trigger去做,即jdbcCtx.updateLAT()。這個trigger就是LastAccessTimeTrigger,它用于批量更新類似未作修改的session的triggerLAT修改,每10秒被觸發(fā)一次。我們把這個Session_A交給trigger,這時候triggerLAT被賦一個值(這個值在trigger被觸發(fā)的時候被寫入數(shù)據(jù)庫),假如是2,如下:
            Session_A.triggerLAT = 2

            1.3: client訪問完page2后,繼續(xù)訪問page3, 假如這個時間間隔有點長(但不超過上述trigger的默認(rèn)間隔10秒),如果系統(tǒng)的訪問量很大,或者cache-size設(shè)定很小,Session_A在cache中的位置被其他后入的session占據(jù),即session中不再有這個session。請求進(jìn)入訪問page3,由于cache中沒有這個對象,我們需要通過getFromDB()從db中l(wèi)oad這個對象(注意:load數(shù)據(jù)后,我們將重建一個session對象,wl_id同于1.1, 1.2的session, 這里用Session_B表示)。如果此刻1.2中的trigger仍沒有被觸發(fā),即triggerLAT=2沒有被寫入數(shù)據(jù)庫,那么getFromDB()從db中獲取的數(shù)據(jù)將是1.1的數(shù)據(jù),即
            Session_B.dbLAT = 1
            Session_B.triggerLAT = 0

            1.4: 在page3請求處理結(jié)束前,即數(shù)據(jù)被同步到數(shù)據(jù)庫前,1.2的trigger被觸發(fā),即它將triggerLAT =2寫入數(shù)據(jù)庫。
            1.5: page3的請求處理結(jié)束,Session_B的dbLAT會變成一個其他值,比如2,如下:
            Session_B.dbLAT = 3
            Session_B.triggerLAT = 0
    此時他同樣會通過syncSession()去同步數(shù)據(jù),由于page3中涉及session的修改,所以它會通過dbUpdate()去檢查數(shù)據(jù)庫中是否存在session_id對應(yīng)的紀(jì)錄(數(shù)據(jù)的確是存在的, wl_access_time=triggerLAT=2),它加查的條件是數(shù)據(jù)庫中的只有wl_access_time等于當(dāng)前的dbLAT或者triggerLAT,顯然數(shù)據(jù)庫中的紀(jì)錄是不符合條件的,即返回紀(jì)錄數(shù)為0,我們將通過dbCreate()創(chuàng)建一條記錄,這樣問題就來了,我們嘗試創(chuàng)建相同wl_id,相同wl_context_path的紀(jì)錄,ORA-00001自然就出現(xiàn)了。

            這個問題的根源是weblogic沒有處理好cache和trigger間對象應(yīng)用的關(guān)系,即trigger拿著對象要求作batch update的時候,如果cache滿了,cache中的對象將被刪除,新入請求將會于server中創(chuàng)建同一session id對應(yīng)的不同對象。這個weblogic的一個bug(CR331498),修正bug的方法是通過WeakReference解決trigger和cache間的對象應(yīng)用關(guān)系,即如果某個對象被trigger refer的話,這個對象就不會被從cache中刪除。當(dāng)然,既然是cache的問題,我們其實也可以通過加大cache-size來解決。    

            2: 應(yīng)用場景問題
            從1中我們可以看到,引發(fā)ORA-00001的根本原因是一個session id的多個sess實例同時存在于一個server上。同樣,如果這多個實例分布在不同的server上,這樣的問題還是會出現(xiàn)。很簡單,加入server_1上有個session,session創(chuàng)建的時候,會往數(shù)據(jù)庫中插入該session對應(yīng)的記錄。如果插入數(shù)據(jù)后,server_2接收到帶著同一session id的request, 那么server_2會從數(shù)據(jù)庫中l(wèi)oad數(shù)據(jù)。如果在server_1、server_2的請求都涉及到session更新,那么后syncSession的那個request必然碰到ORA-00001的錯誤。比如server_1后結(jié)束,因為server_2結(jié)束請求時,把db中的數(shù)據(jù)更新,造成server_1和db中的數(shù)據(jù)完全不一致,server_1最終會嘗試通過dbCreate()創(chuàng)建記錄,結(jié)果當(dāng)然是ORA-00001。

            現(xiàn)在我們看看什么原因會導(dǎo)致server_1, server_2同時存在同一session id對應(yīng)的session對象呢? 基本上都是因為前端的代理無法保證session stick,無論是軟代理,還是hardware loadbalancer。通常情況下,這些代理都能保證session stick,至于為什么不能保證session stick,我們可以通過相應(yīng)的日志文件找出原因。比如apache,我們可以打開weblogic的wl proxy debug,log中基本可以幫助我們定位這些問題,可能是某個時刻,server_1無法相應(yīng),proxy錯誤的以為server_1不可用了,所以會failover請求到server_2上。

            這里我之所以以O(shè)RA-00001舉例,因為用Oracle的太多了,對于其他數(shù)據(jù)庫,雖然不會ORA-00001錯誤,但statck trace應(yīng)該是基本類似的,原因當(dāng)然都是唯一約束沖突。
           

    posted on 2009-06-16 09:03 走走停停又三年 閱讀(2335) 評論(3)  編輯  收藏 所屬分類: Weblogic

    FeedBack:
    # re: Weblogic92中使用JDBC store存儲session時,unique constraint violated(唯一約束沖突)相關(guān)問題分析[未登錄]
    2009-06-16 22:01 | sorcerer
    lz功力深厚啊.
      回復(fù)  更多評論
      
    # re: Weblogic92中使用JDBC store存儲session時,unique constraint violated(唯一約束沖突)相關(guān)問題分析
    2009-06-16 22:06 | 走走停停又三年
    赫赫,我只是把學(xué)到的東西紀(jì)錄在這里,方便的時候回頭看看,溫故而知新嘛。  回復(fù)  更多評論
      
    # re: Weblogic92中使用JDBC store存儲session時,unique constraint violated(唯一約束沖突)相關(guān)問題分析[未登錄]
    2009-06-17 16:38 | sorcerer
    常常聽人說要求不高就不要會話保持了,怕內(nèi)存吃不消,但是session其實應(yīng)該不大啊,一般寫程序都很注意了,不會往里面放特別大的東西.不過現(xiàn)在企業(yè)應(yīng)用都sso了,好像會話保持的必要也不大了.  回復(fù)  更多評論
      
    主站蜘蛛池模板: 亚洲中文字幕无码久久| 亚洲成a人片77777老司机| 亚洲www在线观看| 日本阿v免费费视频完整版| 久久久无码精品亚洲日韩蜜桃| 亚洲免费无码在线| 奇米影视亚洲春色| 日韩精品无码免费专区午夜| 国产亚洲综合一区柠檬导航| 天堂在线免费观看| 久久亚洲精品成人AV| 91免费国产自产地址入| 亚洲制服丝袜中文字幕| 天天操夜夜操免费视频| 无码色偷偷亚洲国内自拍| 免费又黄又硬又爽大片| 亚欧洲精品在线视频免费观看| 亚洲va中文字幕无码久久| 8x8×在线永久免费视频| 亚洲伊人色一综合网| 全免费a级毛片免费看不卡| 粉色视频成年免费人15次| 国产亚洲午夜高清国产拍精品| 精品一卡2卡三卡4卡免费视频| 亚洲视频日韩视频| 影音先锋在线免费观看| 免费播放国产性色生活片| 国产午夜亚洲精品午夜鲁丝片| 久久精品一区二区免费看| 亚洲国产成人va在线观看网址| 妞干网手机免费视频| 特色特黄a毛片高清免费观看| 亚洲成人中文字幕| 夜夜嘿视频免费看| 中文字幕手机在线免费看电影 | 啊v在线免费观看| 91精品全国免费观看青青| 亚洲高清无在码在线电影不卡| 国内自产少妇自拍区免费| 青青操免费在线视频| 狠狠色伊人亚洲综合网站色|