環(huán)境避坑指南)
簡(jiǎn)介適用于Delphi和CBuilder開發(fā)者的ODAC組件包Oracle Data Access Components用于在VCL/FMX應(yīng)用中快速連接并操作Oracle數(shù)據(jù)庫(kù)。核心包含TOracleConnection管理物理連接TOracleQuery與TOracleTable執(zhí)行查詢和數(shù)據(jù)維護(hù)TOracleTransaction控制事務(wù)可覆蓋從基礎(chǔ)增刪改查到存儲(chǔ)過程調(diào)用、異步操作等常見開發(fā)需求尤其適合需要直接訪問Oracle的中高級(jí)桌面應(yīng)用開發(fā)者。資源共1807個(gè)文件壓縮包約9.7MB。以pas源碼、dcu編譯單元、dfm窗體設(shè)計(jì)、dpk包文件為主另有大量bmp圖標(biāo)、sql腳本、配置文件、編譯批處理及示例工程便于理解組件結(jié)構(gòu)、查閱接口定義并快速搭建測(cè)試環(huán)境。當(dāng)前已有467人學(xué)習(xí)適合對(duì)照示例掌握ODAC的連接配置、數(shù)據(jù)綁定與性能優(yōu)化技巧。1. ODAC 是什么Delphi 項(xiàng)目換掉 BDE 之后Oracle 連接靠它接住Delphi 連 Oracle繞不開連接控件這一層。早年用 BDE 的項(xiàng)目重裝系統(tǒng)要重配別名換臺(tái)機(jī)器經(jīng)常連不上后來改用 ADO又得先裝 OLEDB 驅(qū)動(dòng)。ODAC 是專注 Oracle 的 Delphi 連接控件TOraSession 管連接、TOraQuery 跑 SQL部署不用裝額外數(shù)據(jù)庫(kù)引擎環(huán)境依賴少了一大截。ODAC 最實(shí)用的是直連模式不依賴本機(jī)安裝 Oracle 客戶端桌面程序交付時(shí)少很多折騰。代價(jià)是要弄懂 SID、Service Name、字符集這些概念寫錯(cuò)一個(gè)字符就報(bào) ORA-12154。這篇寫給接手 Delphi Oracle 維護(hù)項(xiàng)目的開發(fā)也寫給新項(xiàng)目選型想評(píng)估 ODAC 值不值得用的人。下面按落地順序講組件選型、最小代碼、生產(chǎn)參數(shù)、翻車現(xiàn)場(chǎng)。2. 組件架構(gòu)與連接串TOraSession 的職責(zé)和 Server 參數(shù)的三種寫法ODAC 整套組件里真正高頻的只有四個(gè)先分清職責(zé)再動(dòng)手寫代碼后面排查問題會(huì)快很多。連接掉了、事務(wù)沒提交、界面刷不出來分別對(duì)應(yīng)不同組件的配置搞混了只能瞎試。2.1 四個(gè)核心組件TOraSession、TOraQuery、TOraDataSource、TOraScriptTOraSession 是連接持有者一個(gè)程序通常只保留一個(gè)實(shí)例負(fù)責(zé)建立連接、提交回滾事務(wù)、維護(hù)會(huì)話狀態(tài)。TOraQuery 是 SQL 執(zhí)行器查詢、插入、更新、刪除都走它用它之前把 SQL 文本賦進(jìn)去再綁定參數(shù)。TOraDataSource 是結(jié)果集和界面控件之間的橋把 TOraQuery 的數(shù)據(jù)交給 DBGrid、DBEdit 顯示。TOraScript 用來執(zhí)行一段連續(xù) SQL建表、初始化數(shù)據(jù)、跑遷移時(shí)會(huì)用到。組件選型上我一般守一條原則90% 的界面查詢用 TOraQuery TOraDataSource 就夠別圖省事直接拖 TOraTable。TOraTable 適合做單表維護(hù)工具但表結(jié)構(gòu)一復(fù)雜它的映射規(guī)則會(huì)讓人頭痛而且不小心會(huì)把整表數(shù)據(jù)拉進(jìn)內(nèi)存。TOraScript 也不是常駐組件初始化腳本跑完就可以從窗體上刪掉。這里有一個(gè)容易忽略的關(guān)系TOraQuery 必須指定 Session 屬性指向 TOraSession 實(shí)例否則運(yùn)行時(shí)直接報(bào)會(huì)話未賦值。一個(gè) TOraSession 可以掛任意多個(gè) TOraQuery它們共用同一條物理連接這也是程序里只保留一個(gè) TOraSession 實(shí)例的原因。設(shè)計(jì)期配置可以雙擊 TOraSession 調(diào)出連接編輯器填好賬號(hào)密碼點(diǎn) Connect 就能預(yù)覽連通性這個(gè)編輯器對(duì)新手指路很有用。但項(xiàng)目一旦要交付連接參數(shù)建議放進(jìn)運(yùn)行時(shí)代碼最好從配置文件讀取否則每個(gè)客戶環(huán)境變了都得重編譯。2.2 Server 參數(shù)的三種形態(tài)TNS 別名、直連 SID、直連 Service Name連接信息集中在 TOraSession 的屬性上。UserName 和 Password 對(duì)應(yīng)數(shù)據(jù)庫(kù)賬號(hào)Port 默認(rèn) 1521這些都好理解。最容易出錯(cuò)的是 Server 屬性它有三種取值形態(tài)Server 寫法示例前置條件適用場(chǎng)景TNS 別名ORCL需要 tnsnames.ora 且能被 ODAC 找到沿用公司既有 TNS 配置直連 SID192.168.1.10:1521:ORCL無Oracle 11g 及更早版本直連 Service Name192.168.1.10:1521/ORCLPDB無Oracle 12c 及以后的 PDBTNS 別名看起來很省事但在直連模式下根本不生效。Options.Direct 為 True 時(shí)ODAC 不會(huì)去解析 tnsnames.oraServer 必須寫成 host:port:sid 或 host:port/service_name 的完整形式。第二個(gè)容易踩的是 12c 以后的多租戶架構(gòu)默認(rèn)建的是 CDB 和 PDB連 PDB 要用 Service Name也就是斜杠寫法如果照舊用冒號(hào)寫法填 SID大概率報(bào) ORA-12154。所以我的習(xí)慣是新項(xiàng)目一律用 host:port/service_name并把這段配置寫在 INI 里代碼里只做讀取和賦值OraSession1.Options.Direct : True; OraSession1.Server : Config.ReadString(DB, Server, 192.168.1.10:1521/ORCLPDB); OraSession1.UserName : Config.ReadString(DB, UserName, APP_USER); OraSession1.Password : Config.ReadString(DB, Password, );代碼邏輯不復(fù)雜先把直連模式打開Server 用斜杠格式指向 Service Name賬號(hào)密碼從配置讀取。這里有個(gè)細(xì)節(jié)Password 不要硬編碼在代碼里也不要出現(xiàn)在編譯產(chǎn)物中配置文件本身的讀權(quán)限要收緊否則數(shù)據(jù)庫(kù)賬號(hào)等于裸奔。如果配置文件缺失或讀出來是空串我一般會(huì)在 Connect 前做一次顯式檢查把「數(shù)據(jù)庫(kù)服務(wù)器未配置」這類中文提示拋給操作員而不是讓系統(tǒng)彈一個(gè)英文 ORA 錯(cuò)誤。這個(gè)習(xí)慣能減少大量無謂的工單。提示設(shè)計(jì)期用連接編輯器調(diào)試時(shí)Object Inspector 里填的 Server 格式和運(yùn)行時(shí)代碼要保持一致。很多人設(shè)計(jì)期用 TNS 別名連上了運(yùn)行時(shí)代碼卻按直連寫結(jié)果設(shè)計(jì)期正常、運(yùn)行期報(bào)錯(cuò)這類問題排查起來很繞。3. 最小可運(yùn)行代碼ODAC 建立連接、參數(shù)查詢、事務(wù)提交的三段式連接串和組件關(guān)系理清之后就可以寫代碼了。這一章按依賴順序給三段代碼查詢和寫入都建立在連接已建立的前提下順序不要顛倒。3.1 建立連接并處理失敗Connected 判斷與 EOraError 捕獲連接動(dòng)作本身不復(fù)雜難在處理失敗。下面是我在窗體按鈕事件里常用的寫法也可以直接抽成公共函數(shù)procedure TMainForm.ConnectDB; begin // 已連接就直接返回避免重復(fù)建連 if OraSession1.Connected then Exit; OraSession1.ConnectTimeout : 10; // 單位秒生產(chǎn)環(huán)境必須限制 try OraSession1.Connect; except on E: EOraError do begin LogError(Format(連接失敗錯(cuò)誤碼 %d%s, [E.ErrorCode, E.Message])); raise; // 不要吞異常讓上層知道失敗 end; end; end;說明幾處細(xì)節(jié)。Connected 判斷在按鈕被連續(xù)點(diǎn)擊時(shí)很有用否則每次點(diǎn)擊都觸發(fā)一次建連數(shù)據(jù)庫(kù)側(cè)會(huì)話會(huì)快速堆積。ConnectTimeout 默認(rèn)是 0表示無限等待生產(chǎn)環(huán)境必須給一個(gè)有限值我一般給 10 秒否則數(shù)據(jù)庫(kù)不可達(dá)時(shí)程序會(huì)卡在連接動(dòng)作上像死機(jī)一樣。EOraError 是 ODAC 的統(tǒng)一異常類ErrorCode 就是 ORA- 后面的數(shù)字也就是 12154、12541 這類記日志時(shí)務(wù)必帶上。最后那個(gè) raise 是關(guān)鍵很多問題看起來像「沒反應(yīng)」其實(shí)是異常被吞了。如果要做斷線重連我見過最可靠的做法不是依賴某個(gè)組件事件而是把 ConnectDB 抽成公共函數(shù)在每次 ExecSQL 或 Open 之前調(diào)用一次。調(diào)用成本很低因?yàn)閮?nèi)部有 Connected 判斷只有在斷線時(shí)才會(huì)真正重建連接這個(gè)模式在客戶端程序里比任何自動(dòng)重連配置都直觀。3.2 參數(shù)化查詢綁定變量寫法與結(jié)果集遍歷查詢用 TOraQuerySQL 文本里用冒號(hào)標(biāo)記參數(shù)OraQuery1.Session : OraSession1; OraQuery1.SQL.Text : SELECT EMP_NO, EMP_NAME, DEPT_ID FROM EMP WHERE DEPT_ID :DEPT_ID ORDER BY EMP_NO; OraQuery1.ParamByName(DEPT_ID).AsInteger : 10; OraQuery1.Open; try while not OraQuery1.Eof do begin LogInfo(OraQuery1.FieldByName(EMP_NAME).AsString); OraQuery1.Next; end; finally OraQuery1.Close; // 釋放游標(biāo)防止會(huì)話堆積 end;這里第一條要記住的是 Session 屬性必須賦值忘了的話運(yùn)行時(shí)會(huì)提示 TOraQuery 沒有指定會(huì)話。參數(shù)用 :DEPT_ID 寫然后通過 ParamByName 賦值這是 Oracle 綁定變量的標(biāo)準(zhǔn)姿勢(shì)。千萬別用字符串拼 SQL 的方式既容易被注入又會(huì)因?yàn)槊總€(gè)查詢條件不同導(dǎo)致數(shù)據(jù)庫(kù)反復(fù)硬解析并發(fā)一高 shared pool 先撐不住。Open 和 ExecSQL 要分清Open 用于查詢并把結(jié)果集加載進(jìn)來ExecSQL 用于執(zhí)行不返回結(jié)果集的 INSERT、UPDATE、DELETE 語句兩者混用會(huì)在運(yùn)行時(shí)暴露「數(shù)據(jù)集未打開」或游標(biāo)狀態(tài)錯(cuò)亂的錯(cuò)誤。循環(huán)里 FieldByName 按字段名取元數(shù)據(jù)方便但循環(huán)上萬次時(shí)有損耗如果數(shù)據(jù)量大、性能敏感可以把 FieldByName 提出循環(huán)或改用 Fields[0] 按索引訪問。finally 里的 Close 是習(xí)慣也是防止游標(biāo)泄漏的關(guān)鍵和數(shù)據(jù)庫(kù)連接一樣不釋放的游標(biāo)攢到一定數(shù)量同樣會(huì)拖垮會(huì)話。3.3 事務(wù)寫入Commit、Rollback 與異常傳播寫入操作要包事務(wù)原則很簡(jiǎn)單要么全部成功要么全部回滾OraSession1.StartTransaction; try OraQuery1.SQL.Text : UPDATE EMP SET SALARY SALARY * :RATIO WHERE DEPT_ID :DEPT_ID; OraQuery1.ParamByName(RATIO).AsFloat : 1.1; OraQuery1.ParamByName(DEPT_ID).AsInteger : 10; OraQuery1.ExecSQL; OraQuery1.SQL.Text : INSERT INTO OP_LOG(OP_TIME, OP_TYPE) VALUES(SYSDATE, :OP_TYPE); OraQuery1.ParamByName(OP_TYPE).AsString : UPDATE_SALARY; OraQuery1.ExecSQL; OraSession1.Commit; except OraSession1.Rollback; raise; end;StartTransaction 和 BeginTransaction 是同義方法選哪個(gè)看團(tuán)隊(duì)習(xí)慣。事務(wù)內(nèi)多個(gè) SQL 共用一個(gè)連接所以 TOraQuery 不需要新建實(shí)例改 SQL.Text 和參數(shù)即可。Commit 之前任何一個(gè)步驟拋異常都會(huì)跳進(jìn) except 分支回滾然后 raise 把原始異常繼續(xù)往外拋界面層收到后可以彈中文提示。這里有個(gè)血淚經(jīng)驗(yàn)回滾之后不要只彈個(gè)對(duì)話框就完事一定要讓異常繼續(xù)傳播否則調(diào)用方不知道寫入失敗了界面顯示成功但數(shù)據(jù)沒進(jìn)去這種問題最難查。事務(wù)保持短是另一個(gè)原則。不要在事務(wù)里放循環(huán)執(zhí)行大量更新事務(wù)越長(zhǎng)鎖的持有時(shí)間和 undo 累積量越大并發(fā)場(chǎng)景下很容易阻塞其他會(huì)話。批量寫入建議先算好批次每批一個(gè)事務(wù)處理一批提交一次。4. 生產(chǎn)環(huán)境必調(diào)的三個(gè)參數(shù)直連模式、連接池、字符集代碼跑通只是第一步生產(chǎn)環(huán)境是否穩(wěn)定取決于連接層的參數(shù)怎么設(shè)。這一章說我每次上線前都會(huì)檢查的三個(gè)項(xiàng)順序正好對(duì)應(yīng)部署、并發(fā)、中文數(shù)據(jù)三個(gè)場(chǎng)景。4.1 直連模式與客戶端模式性能與排查成本的取舍Options.Direct 是第一個(gè)要決定的參數(shù)它決定整個(gè)連接層要不要依賴外部客戶端對(duì)比項(xiàng)DirectTrueDirectFalse部署依賴無 Oracle 客戶端需要安裝 Oracle 客戶端或 Instant Client連接速度更快略慢多一層客戶端協(xié)議棧功能覆蓋覆蓋常規(guī)業(yè)務(wù)場(chǎng)景覆蓋全部客戶端特性排查難度只看 ODAC 日志需要同時(shí)查客戶端日志我的建議是純桌面應(yīng)用、數(shù)據(jù)庫(kù)走內(nèi)網(wǎng)地址的直接用 DirectTrue省掉客戶端等于省掉一半的部署問題。DirectFalse 的場(chǎng)景主要是使用集群特性、需要 TNS 負(fù)載均衡、或者依賴客戶端側(cè)高級(jí)配置時(shí)。直連模式不是萬能遇到集群環(huán)境先驗(yàn)證一下當(dāng)前 ODAC 版本對(duì)集群特性的支持程度再?zèng)Q定這個(gè)驗(yàn)證不能省。這里再補(bǔ)一個(gè)判斷方法如果部署目標(biāo)機(jī)器上已經(jīng)由運(yùn)維統(tǒng)一裝好了 Oracle 客戶端且公司規(guī)范要求所有應(yīng)用走 TNS 別名那就用 DirectFalse 順著規(guī)范走如果目標(biāo)機(jī)器環(huán)境不可控比如客戶現(xiàn)場(chǎng)、門店電腦優(yōu)先 DirectTrue。本質(zhì)是選一個(gè)你知道所有變量的方案。4.2 連接池與會(huì)話管理防止連接堆積程序里如果頻繁 Connect、Disconnect數(shù)據(jù)庫(kù)側(cè)的會(huì)話建立和銷毀開銷不小還把 process 數(shù)拉得很高。ODAC 提供連接池常見配置如下OraSession1.PoolingOptions.Pooling : True; OraSession1.PoolingOptions.MinPoolSize : 2; OraSession1.PoolingOptions.MaxPoolSize : 20; OraSession1.PoolingOptions.ConnectionLifetime : 600;Pooling 打開后物理連接會(huì)被復(fù)用Disconnect 只是把連接還給池子而不是真正關(guān)閉。MinPoolSize 讓系統(tǒng)啟動(dòng)后就保持兩條熱連接避免首個(gè)請(qǐng)求等建連MaxPoolSize 是硬上限防止失控代碼把連接數(shù)打滿ConnectionLifetime 單位是秒連接使用超過這個(gè)時(shí)長(zhǎng)就銷毀重建避免數(shù)據(jù)庫(kù)側(cè)會(huì)話資源老化。不同版本 ODAC 的屬性樹略有差異以你安裝版本的 Object Inspector 為準(zhǔn)但思路一致。連接池和直連模式之間有個(gè)坑部分舊版本在直連模式下連接池支持不完整表現(xiàn)是開池后連接反而變慢或報(bào)錯(cuò)。遇到這種情況先用一個(gè)最小 Demo 驗(yàn)證池化在直連模式下是否正常再?zèng)Q定要不要退回客戶端模式。4.3 字符集中文亂碼與字段超長(zhǎng)的源頭字符集問題不是偶發(fā)是配置出來的。連接建立后ODAC 默認(rèn)的客戶端字符集不一定和數(shù)據(jù)庫(kù)一致中文亂碼就在這一步埋下OraSession1.Options.Charset : AL32UTF8;這個(gè)設(shè)置告訴 ODAC 用 UTF-8 與應(yīng)用交換數(shù)據(jù)。上線前可以用一條 SQL 確認(rèn)數(shù)據(jù)庫(kù)的語言環(huán)境SELECT USERENV(LANGUAGE) FROM DUAL;返回結(jié)果形如 SIMPLIFIED CHINESE_CHINA.AL32UTF8看到 AL32UTF8 就與上面的設(shè)置對(duì)齊。老庫(kù)如果是 ZHS16GBK 字符集Options.Charset 也填 ZHS16GBK不要混用混用的直接后果是查詢出來中文正常寫入時(shí)字段長(zhǎng)度不夠報(bào) ORA-12899。這里有一個(gè)容易被忽略的坑AL32UTF8 下 VARCHAR2(N) 的長(zhǎng)度單位是字節(jié)一個(gè)中文占 3 字節(jié)。VARCHAR2(20) 在 UTF-8 庫(kù)里只能存 6 個(gè)漢字字段設(shè)計(jì)時(shí)就要按字節(jié)留冗余否則用戶輸入稍微長(zhǎng)一點(diǎn)的中文就報(bào)錯(cuò)。字符集這種事沒有后悔藥上線后發(fā)現(xiàn)亂碼只能全鏈路排查所以在連接參數(shù)這一層定死它是成本最低的防線。5. ODAC 連接 Oracle 的避坑清單5 個(gè)高頻翻車現(xiàn)場(chǎng)與排查路徑這一章直接給結(jié)論每一條按現(xiàn)象、原因、解決三段寫。都是我在維護(hù)項(xiàng)目里踩過的按出現(xiàn)頻率排從最常遇到的說起。5.1 ORA-12154連接標(biāo)識(shí)符解析失敗現(xiàn)象程序啟動(dòng)連接時(shí)報(bào) ORA-12154錯(cuò)誤文本是 TNS:could not resolve the connect identifier specified但設(shè)計(jì)期明明連得上。原因最常見的是設(shè)計(jì)期填了 TNS 別名 ORCL連接編輯器和本地客戶端配合能解析運(yùn)行時(shí)代碼把 Options.Direct 置為 True 后ODAC 不再讀 tnsnames.ora別名就變成無法解析的標(biāo)識(shí)符。另一種是 Server 用冒號(hào)格式寫了 SID但 12c 以后數(shù)據(jù)庫(kù)開放的是 Service Name。解決統(tǒng)一改寫成 host:port/service_name 完整格式如果公司規(guī)范要求用 TNS 別名那就把 Direct 關(guān)掉并保證 tnsnames.ora 在 ODAC 能讀到的路徑下。出現(xiàn)這個(gè)錯(cuò)先別查網(wǎng)絡(luò)先檢查 Server 字符串本身十次里有八次是格式問題。5.2 OCI.dll 加載失敗客戶端模式的環(huán)境依賴現(xiàn)象DirectFalse 時(shí)連接報(bào)錯(cuò)提示找不到 OCI.dll 或 Oracle 客戶端初始化失敗同一套程序在不同機(jī)器上表現(xiàn)還不一樣有的報(bào)錯(cuò)有的直接閃退。原因程序是 32 位進(jìn)程而目標(biāo)機(jī)器只裝了 64 位 Oracle 客戶端或者客戶端目錄沒有寫進(jìn) PATH進(jìn)程根本找不到 OCI 接口庫(kù)。這類問題的麻煩在于每臺(tái)機(jī)器的 Oracle 家目錄、PATH 都不一樣排查像是開盲盒。解決要么在目標(biāo)機(jī)器裝 32 位 Instant Client把目錄寫進(jìn) PATH要么放棄客戶端模式改用 DirectTrue。桌面交付場(chǎng)景我強(qiáng)烈推薦后者少一個(gè)環(huán)境依賴就少一類工單。5.3 中文亂碼與 ORA-12899字符集不匹配的連鎖反應(yīng)現(xiàn)象查詢出來的中文是問號(hào)或者插入中文時(shí)報(bào) ORA-12899 value too large for column英文數(shù)字正常。原因Options.Charset 沒有設(shè)置或設(shè)置的字符集與數(shù)據(jù)庫(kù)不一致。AL32UTF8 下字段長(zhǎng)度按字節(jié)計(jì)一個(gè)中文占 3 字節(jié)VARCHAR2(20) 存 6 個(gè)漢字就滿了。解決先跑 SELECT USERENV(LANGUAGE) FROM DUAL 查到庫(kù)的語言環(huán)境再把 Options.Charset 改成一致。字段長(zhǎng)度在設(shè)計(jì)上要給中文字段留足冗余新表按字節(jié)數(shù) ×3 評(píng)估。已經(jīng)亂碼的數(shù)據(jù)要查是哪一層轉(zhuǎn)壞的通常從連接參數(shù)改起改完再驗(yàn)證新寫入的數(shù)據(jù)。5.4 ORA-00020連接數(shù)把數(shù)據(jù)庫(kù)進(jìn)程打滿現(xiàn)象系統(tǒng)正常運(yùn)行幾天后突然所有連接都報(bào) ORA-00020 maximum number of processes重啟程序只能好一陣過后又復(fù)發(fā)。原因代碼里 Open 了數(shù)據(jù)集但沒 Close或者每次操作都新建連接而不釋放數(shù)據(jù)庫(kù)側(cè)的會(huì)話只增不減直到打滿 process 上限。這類問題的特點(diǎn)是慢不會(huì)上線當(dāng)天爆容易被誤判成數(shù)據(jù)庫(kù)故障。解決給所有查詢塊補(bǔ) try/finally Close連接統(tǒng)一走 TOraSession 單例。打開連接池并設(shè) MaxPoolSize給連接數(shù)一個(gè)天花板。排查時(shí)用 v$session 按機(jī)器和程序名分組哪個(gè)來源會(huì)話多就順著代碼找哪里的泄漏。先查自己再查數(shù)據(jù)庫(kù)方向錯(cuò)了會(huì)浪費(fèi)很長(zhǎng)時(shí)間。5.5 32 位與 64 位進(jìn)程不匹配裝好了客戶端還是連不上現(xiàn)象同一份程序在 A 機(jī)器一切正常在 B 機(jī)器連不上報(bào)錯(cuò)五花八門從 OCI 初始化失敗到內(nèi)存錯(cuò)誤都有可能。原因Delphi 程序如果按 32 位編譯B 機(jī)器上只裝了 64 位 Oracle 客戶端兩邊位數(shù)對(duì)不上OCI 加載層面就失敗。這個(gè)問題在 x64 系統(tǒng)普及后尤其常見因?yàn)樾卵b的客戶端默認(rèn)都是 64 位。解決確認(rèn)程序編譯目標(biāo)是 Win32 還是 Win64客戶端位數(shù)必須一致。最省心的還是直連模式DirectTrue 不加載外部 OCI位數(shù)問題直接消失。這也算我推薦直連的另一個(gè)理由少一堆系統(tǒng)層面的玄學(xué)。6. 把連接耗時(shí)與錯(cuò)誤碼寫進(jìn)日志一個(gè)提前暴露隱患的驗(yàn)證習(xí)慣環(huán)境裝好、參數(shù)調(diào)完怎么驗(yàn)證這套配置是可靠的我現(xiàn)在的習(xí)慣是每次連接都記錄耗時(shí)和錯(cuò)誤碼把連接從黑匣子變成可審計(jì)的數(shù)據(jù)。別小看這一步它能提前暴露網(wǎng)絡(luò)抖動(dòng)、監(jiān)聽隊(duì)列堆積、數(shù)據(jù)庫(kù)負(fù)載這些在界面上看不出來的問題。6.1 用 GetTickCount 記錄連接耗時(shí)uses Windows, SysUtils; var startTick, elapsedMs: DWORD; begin startTick : GetTickCount; try OraSession1.Connect; elapsedMs : GetTickCount - startTick; LogInfo(數(shù)據(jù)庫(kù)連接成功耗時(shí) IntToStr(elapsedMs) ms); except on E: EOraError do begin elapsedMs : GetTickCount - startTick; LogError(數(shù)據(jù)庫(kù)連接失敗錯(cuò)誤碼 IntToStr(E.ErrorCode) 耗時(shí) IntToStr(elapsedMs) ms E.Message); raise; end; end; end;GetTickCount 在 Delphi 7 到新版都可用兼容老項(xiàng)目。局域網(wǎng)內(nèi)正常連接耗時(shí)一般在幾十毫秒如果首次連接超過 1 秒說明網(wǎng)絡(luò)質(zhì)量、監(jiān)聽隊(duì)列或數(shù)據(jù)庫(kù)負(fù)載有問題。這個(gè)日志的價(jià)值在于積累基線哪天連接變慢對(duì)比基線就能定位是數(shù)據(jù)庫(kù)側(cè)還是網(wǎng)絡(luò)側(cè)不用猜。6.2 錯(cuò)誤碼速查日志里最該留下的字段連接日志里除了時(shí)間一定要保留 ORA 錯(cuò)誤碼。下面是幾個(gè)高頻碼可以直接做成代碼注釋或運(yùn)維手冊(cè)O(shè)RA 錯(cuò)誤碼含義優(yōu)先排查ORA-12154TNS 解析失敗Server 格式、tnsnames.oraORA-12541無監(jiān)聽監(jiān)聽狀態(tài)、防火墻、端口ORA-01017用戶名或密碼無效憑據(jù)配置ORA-00020進(jìn)程數(shù)超限連接池、會(huì)話泄漏ORA-12899字段超長(zhǎng)字符集、字段長(zhǎng)度血淚教訓(xùn)是不要只把 E.Message 寫進(jìn)日志。ORA-12541、ORA-12154 這類錯(cuò)誤消息本身是英文長(zhǎng)句同一個(gè)錯(cuò)誤碼能派生好幾種文本只有錯(cuò)誤碼是穩(wěn)定的拿它做統(tǒng)計(jì)和關(guān)鍵字檢索才可靠。日志里有了錯(cuò)誤碼客戶把報(bào)錯(cuò)截圖發(fā)過來你一眼就能定位方向不用反復(fù)遠(yuǎn)程確認(rèn)?,F(xiàn)在任何一個(gè)新環(huán)境部署完我第一件事就是連一次、看日志里的耗時(shí)和錯(cuò)誤碼再?zèng)Q定要不要交付。這個(gè)習(xí)慣幫我擋掉過好幾次「明明能連上但客戶環(huán)境一跑就崩」的尷尬。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取