重現ORA-021500(17099)錯誤,並提供解決辦法

來源:互聯網
上載者:User
    兩天前寫的一篇文章提到公司的應用程式常常出現ORA-21500錯誤(http://blog.csdn.net/ah__fu/archive/2008/04/16/2296641.aspx),在網上找了很久,終於有以下資料提供了些頭緒:

http://www.dbatools.net/doc/bug10204.html     Bugs fixed in the 10.2.0.4 Patch Set
http://blogs.sun.com/mandalika/tags/oracle     Solaris/SPARC: Oracle 11gR1 client for Siebel 8.0

    從BLOG http://blogs.sun.com/mandalika/tags/oracle 的最後幾句話:

Although I'm not sure what exactly is the underlying issue for the core dump, my suspicion is that there is some memory corruption in Oracle client's code; and the Siebel Object Manager crash is due to the Oracle bug 5682121 - Multithreaded OCI clients do not mutex properly for LOB operations. The fix to this particular bug would be available in Oracle 10.2.0.4 release; and is already available as part of Oracle 11.1.0.6.0. In case if you notice the symptoms of failure as described in this blog post, upgrade the Oracle client in the application-tier to Oracle 11gR1 and see if it brings stability to the Siebel environment.

    得知,這個專家認為這個錯誤是ORACLE用戶端的BUG導致的,必須要將用戶端升級到ORACLE 10G才能解決。(P.小型股份有限公司使用的ORACLE版本是9.0.4)

   文檔http://www.dbatools.net/doc/bug10204.html 中搜尋ORA-21500也發現一行這樣的資訊:
       BUG ID; 5682121
       Bug: Multithreaded OCI clients do not mutex properly for LOB operations (ORA-21500 [17099])
           我們的代碼也的確是在多線程環境下使用PRO*C導致了ORA-21500(P.S PRO*C實際上只是先行編譯器,仍然需要將PC的代碼轉換成對OCI的調用,ORACLE應該只用一種API介面,那就是OCI)

   因此,到處尋找ORACLE 10G Client for Linux的光碟片,找了半天也沒找到。沒辦法,只好自己想辦法讓這個BUG重現,便於確定是否是自己的代碼的問題。
    出問題的代碼在於自己封裝了一個資料庫的串連池,其原理是預先分配N個串連,然後每個線程需要串連的時候從串連池中擷取一個空閑串連來使用,使用完後立即將串連釋放回串連池。這樣,每個線程不必一直佔用一個串連,也不必每次都直接連接資料庫。串連池的代碼很簡單,看了幾遍也沒發現哪兒有問題。於是編寫了以下的代碼來類比串連池的原理,嘗試讓ORA-21500重現。//ORA21500.pc
#ifndef ORA_PROC
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <pthread.h>
    // 必須先定義這個宏,避免在每個PC檔案中產生全域的sqlca變數
    #define SQLCA_NONE
    #include <sqlca.h>
#endif

#define MAX_CONN 4
#define CONN_STR "webuser/webuser@bsmp"

/**//// 去除PROC函數內的“變數未使用”的警告的宏
#define NO_WARNING {sqlstm.sqlvsn = sqlstm.sqlvsn;}

/**//// 分配一個全域的串連池
EXEC SQL BEGIN DECLARE SECTION;
sql_context ctx[MAX_CONN];
EXEC SQL END DECLARE SECTION;

/**//// 線程函數
void* read_data(void* param)
...{
    EXEC SQL BEGIN DECLARE SECTION;
    struct sqlca sqlca;
    int* index = (int*)param;
    int nCount = 0;
    int user_id;
    EXEC SQL END DECLARE SECTION;
    printf("%s %d: index=%d thread=%d ", __FUNCTION__, __LINE__, *index, (int)pthread_self());
    EXEC SQL CONTEXT USE :ctx[*index];

    EXEC SQL DECLARE CurUser CURSOR FOR 
        SELECT user_id FROM webuser.user_info WHERE rownum<=100000;
    EXEC SQL OPEN CurUser;
    do
    ...{
        EXEC SQL FETCH CurUser INTO :user_id;
        nCount++;
    } while(sqlca.sqlcode==0);
    EXEC SQL CLOSE CurUser;
    printf("%s %d: user count=%d index=%d thread=%d ", __FUNCTION__, __LINE__, 
        nCount, *index, (int)pthread_self());
    return NULL;
    NO_WARNING;
}

/**//// 建立串連池和線程
void test()
...{
    EXEC SQL BEGIN DECLARE SECTION;
    struct sqlca sqlca;
    int i;
    char ConnStr[100];
    int index[MAX_CONN];
    EXEC SQL END DECLARE SECTION;
    pthread_t threads[MAX_CONN];
    strcpy(ConnStr, CONN_STR);
    memset(threads, 0, sizeof(threads));
    /**//// 建立串連池
    for (i=0; i<MAX_CONN; i++)
    ...{
        EXEC SQL CONTEXT ALLOCATE :ctx[i];
        EXEC SQL CONTEXT USE :ctx[i];
        EXEC SQL CONNECT :ConnStr;
        if (sqlca.sqlcode != 0)
        ...{
            printf("%s %d: 串連資料庫錯誤:code=%d, errm=%s ", __FUNCTION__, __LINE__,
                sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
            return;
        }
    }
    //建立線程
    for (i=0; i<MAX_CONN; i++)
    ...{
        index[i] = i;
        if (0!=pthread_create(threads+i, NULL, read_data, index+i))
        ...{
            printf("%s %d: create thread failed! ", __FUNCTION__, __LINE__);
        }
        else
        ...{
            printf("%s %d: thread[%d]=%d ", __FUNCTION__, __LINE__, i, (int)threads[i]);
        }
    }
    //連接線程
    for (i=0; i<MAX_CONN; i++)
    ...{
        pthread_join(threads[i], NULL);
    }
    printf("%s %d: end ", __FUNCTION__, __LINE__);
    return;
    NO_WARNING;
}

int main()
...{
    test();
    return 1;
}

/**//*
proc userid=webuser/webuser@bsmp parse=partial code=cpp sqlcheck=full threads=yes char_map=string def_sqlcode=false oraca=false objects=false errors=true lines=true auto_connect=no ltype=none hold_cursor=yes release_cursor=no iname=ORA21500.pc oname=ORA21500.cpp cpool=yes cmax=5 cmin=3 cincr=2 ctimeout=2 cnowait=7 
g++ -o ORA21500.o -c ORA21500.cpp -g -Wall -Werror -I"${ORACLE_HOME}/precomp/public"
g++ -o ORA21500.exe ORA21500.o  "${ORACLE_HOME}/lib/libsql9.a" "${ORACLE_HOME}/lib/libclntsh.so" -lpthread
*/

    代碼的最後幾行是編譯命令。編譯後執行這個程式,幾乎每次都發生core dump, 但是出現ORA-21500資訊的次數較少。以上的代碼實際上也就是我封裝的串連池的原理所在。如此簡單的代碼也能崩潰掉,看來這真的是ORACLE用戶端的BUG無疑了。

    正當打算將用戶端安裝成ORACLE 10g的時候,突然想到修改一下PRO*C的編譯參數試試。
    於是將proc編譯參數中的串連池資訊去掉:cpool=yes cmax=5 cmin=3 cincr=2 ctimeout=2 cnowait=7
    再編譯執行以上代碼,結果讓我大跌眼睛(還好我不戴眼睛)————居然運行完全正確,沒有發生core dump!
    恩,從上面的現象看來,ORA-21500內部錯誤這個BUG與ORACLE的串連池機制有關。關閉串連池暫時可以屏蔽掉這個錯誤,但是不能肯定在其他環境下不會出現。

   OK,綜上所述:
1、在PRO*C的編譯選項中,如果使用了串連池,多線程下會出現ORA-21500錯誤;去掉串連池選項即可解決;
2、直接調用OCI代碼,在多線程和串連池同時使用的情況下,也可能會出現這個錯誤;
3、不管怎麼樣,ORA-21500[17099]始終是一個BUG,最好還是換成ORACLE新版的產品。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.