天天看點

Oracle字元亂碼、資料越界通路典型Bug分析

前言

作為乙方,在甲方客戶那裡驗收階段發現兩個詭異Bug。以下就問題來源、問題根因、解決方案、如何避免做較長的描述。

一、Bug1:Oracle讀寫字元亂碼。

1、問題來源

Oracle資料庫監聽

http://blog.csdn.net/laoyang360/article/details/46524519

 需要擷取最新插入的中文類别字元,以判定分類。單步調試發現每次接收到的都是亂碼。

2、問題根因

編碼格式不一緻導緻。

3、解決方案

第一步:檢視oracle自身的編碼格式,可以通過指令select userenv('language') from dual;檢視。

檢視後得知,目前多家公用的oracle采用SIMPLIFIED CHINESE_CHINA.ZHS16GBK編碼格式。

第二步:檢視背景程式使用的編碼格式,背景程式通過OTL讀取、寫入orcle資料庫。采用了設定環境變量putenv(),寫入的是

NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8,也就是UTF-8編碼格式。

第三步:由于oracle編碼格式已經固定,隻能修改背景程式寫入環境變量的格式為IMPLIFIED CHINESE_CHINA.ZHS16GBK編碼格式。

第四步:檢視編譯工具VS2010使用的編碼格式,我們知道:VC6.0預設ANSI編碼,而VS2010預設UNICODE編碼。UNICODE編碼和GBK編碼格式不一緻,需要進行轉換。

第五步:查了很久,最終通過轉換接口utf82gbk()以及gbk2utf8()完成格式轉換。

也就是說:從資料庫讀出資料,需要調用gbk2utf8()進行格式轉換才能在VS2010下正常顯示。

同樣的,向資料庫寫入資料,需要調用utf82gbk()接口進行轉換,才能確定寫入oracle不會顯示亂碼。

并且,前台程式統一為utf8編碼,統一轉化才能確定沒有亂碼。

至于Utf8和Unicode的關系(一句話,utf8是對unicode字元集進行編碼的一種編碼方式。)詳見知乎讨論:

http://www.zhihu.com/question/23374078

4、如何避免

1)最早設計的時候,定下資料庫的編碼格式,各個子產品負責人按照統一格式進行處理。

2)編譯器不同導緻編碼格式不同,需要大家統一接口進行轉換。實作接口可參見如下:

http://blog.csdn.net/p569354158/article/details/6567175

二、Bug2:溢出,資料越界

程式頻繁執行500個任務後,頁面不能顯示傳回資料。從界面顯示的資料看,序号為67138,67139,63140的都不能顯示。

但是小的序号69,70都能正常顯示對應的傳回資料。

資料越界通路,可能在程式的某個地方采取了2個位元組unsigned short存儲(最大範圍:0-2的16次幂-1,即:0-65535)。

問題是如何發現的:

第一步:通過數字67138等猜測資料越界,原因:67138>65535。而69,70小于65535的都顯示正常。

第二步:資料庫入庫編号和程式日志編号一一對比。檢視得知:oracle對應的資料庫表最早入庫序号為67138等大于65535的值,但在程式第二次傳回插入的結果表裡變成了1618的序号值,而該序号非pointId,而是taskId或者systemId,也就是初步斷定發生了越界通路。

第三步:研究為什麼這個編号需要在資料庫中唯一呢?和相關設計人員電話确認。因為後期的程式需求偏離了最早期的設計,此處在我看來完全不需要使用唯一值。隻要保證每次任務下發唯一即可。因為我們的程式中由taskId索引唯一任務号,pointId的節點号為二級索引。程式使用查詢表中也是,先一級索引後二級索引。

資料庫中采用序列sequence進行唯一值遞增判定,最大值設定的非常大999999999999999999999999999。且兩個表的觸發器Trigger都調用這個序列,也就說,程式的頻繁任務下發任務中,頻繁執行後,幾周或者幾個月很容易就超過65535的序号值。

綜上,基本判定程式中出現越界通路導緻顯示異常。

1)情況着急:采取簡單規避方案:更新序列Sequence目前值,目前值已經查過67000,需要回歸到1重新開始。

但有個知識點是,在sqldevelop以及所有的檢視工具下,該值都不能手動修改。

需要删除後重建。使用者必須具有ALTER ANY SEQUENCE 才能修改修改sequence,可以alter除start之外的所有sequence,如果想修改start值,必須drop sequence再re-create。

2)未來需要定位到哪裡越界通路的,把類型至少改為unsigned int存儲。

定義序号類型的變量,要考慮它的源頭,如何産生的,最小值、最大值是多少,再确定定義哪種類型的變量。

總結:

1.兩個Bug都不難,但都需要理清思路,避免走彎路。第一個Bug梳理到改完耗時3.5h,第二個Bug耗時5h。

2.第二個Bug很詭異,在我執行完500個任務就突然不顯示傳回資料。程式基本沒有改動任何邏輯。想了很久,排除了非傳輸裝置的影響,非搜尋程式的影響,最終定位和65535有關。

作者:銘毅天下

轉載請标明出處,原文位址:

http://blog.csdn.net/laoyang360/article/details/50282569