誤排查全攻略:從Navicat到Oracle服務(wù)端鏈路解析)
我最早遇到這個(gè)報(bào)錯(cuò)是在一次版本發(fā)布前的數(shù)據(jù)訂正窗口。Navicat連測(cè)試庫(kù)連了一整天都好好的突然某一次點(diǎn)擊連接直接彈了ORA-01012: not logged on當(dāng)時(shí)第一反應(yīng)是數(shù)據(jù)庫(kù)是不是被誰(shuí)關(guān)掉了。結(jié)果登到服務(wù)器上看監(jiān)聽(tīng)、看進(jìn)程全都正常數(shù)據(jù)庫(kù)也開(kāi)著。后來(lái)折騰了一圈才發(fā)現(xiàn)這個(gè)錯(cuò)誤遠(yuǎn)不像它字面上那么簡(jiǎn)單——它只是一個(gè)包裝出來(lái)的結(jié)果真正的根因藏在服務(wù)端的事件日志里。這篇文章就把我?guī)状翁幚鞳RA-01012的完整排查思路、驗(yàn)證步驟和一些容易忽略的細(xì)節(jié)寫(xiě)出來(lái)。如果你是開(kāi)發(fā)、測(cè)試、數(shù)據(jù)分析崗平時(shí)用Navicat連Oracle比較多遇到這個(gè)報(bào)錯(cuò)時(shí)不知道怎么下手可以參考我的排查順序。內(nèi)容不涉及高深理論每一步都是能直接照著操作的。1. 先搞清楚ORA-01012到底在說(shuō)什么一個(gè)被包裝過(guò)的錯(cuò)誤先說(shuō)結(jié)論ORA-01012全稱是not logged on直譯過(guò)來(lái)就是當(dāng)前會(huì)話未登錄。但這個(gè)報(bào)錯(cuò)出現(xiàn)在Navicat的連接窗口里和你直接用SQL*Plus登錄時(shí)報(bào)的同名錯(cuò)誤含義并不完全一樣。在很多情況下Navicat把服務(wù)端返回的真實(shí)錯(cuò)誤碼包裝成了ORA-01012返回給客戶端。換句話說(shuō)你看到的這個(gè)錯(cuò)誤真正的觸發(fā)原因可能藏在更底層的事件跟蹤里。這一點(diǎn)非常重要因?yàn)樗鼪Q定了排查方向——如果你一直在客戶端層面打轉(zhuǎn)可能折騰半天都找不到根因。1.1 什么時(shí)候最容易觸發(fā)這個(gè)錯(cuò)誤結(jié)合我自己遇到的場(chǎng)景ORA-01012最常在下面這幾種情況里冒出來(lái)數(shù)據(jù)庫(kù)實(shí)例處于啟動(dòng)的中間狀態(tài)。比如執(zhí)行了startup mount或者startup nomount數(shù)據(jù)庫(kù)還沒(méi)完全open此時(shí)客戶端連進(jìn)來(lái)就可能收到這類錯(cuò)誤。數(shù)據(jù)庫(kù)正在執(zhí)行shutdown或者剛執(zhí)行完shutdown但監(jiān)聽(tīng)器的服務(wù)注冊(cè)還沒(méi)刷新過(guò)來(lái)客戶端剛好在這個(gè)時(shí)間窗口去連。遠(yuǎn)程連接時(shí)數(shù)據(jù)庫(kù)服務(wù)端的sqlnet.ora、tnsnames.ora配置不對(duì)或者Oracle Net Service異常終止了會(huì)話。服務(wù)器內(nèi)存壓力大、會(huì)話數(shù)達(dá)到上限導(dǎo)致已有的后臺(tái)進(jìn)程被意外終止新建連接自然也進(jìn)不來(lái)。Navicat所連接的Oracle賬號(hào)被鎖、口令過(guò)期但服務(wù)端返回的錯(cuò)誤被客戶端包裝成了ORA-01012。你看光賬號(hào)被鎖這個(gè)原因從字面上就和not logged on八竿子打不著。所以如果你按字面去理解這個(gè)報(bào)錯(cuò)很容易鉆進(jìn)死胡同。1.2 為什么Navicat會(huì)把真實(shí)錯(cuò)誤吞掉Navicat連接Oracle走的是OCI驅(qū)動(dòng)Oracle Call Interface它在建立連接時(shí)會(huì)先和數(shù)據(jù)庫(kù)服務(wù)端做一次會(huì)話協(xié)商。如果協(xié)商階段失敗OCI層返回的錯(cuò)誤碼可能并不是最原始的服務(wù)端錯(cuò)誤——尤其是數(shù)據(jù)庫(kù)實(shí)例沒(méi)有完全就緒、或者監(jiān)聽(tīng)器狀態(tài)異常時(shí)Navicat只能拿到一個(gè)會(huì)話建立失敗的通用錯(cuò)誤最終就表現(xiàn)成了ORA-01012。這不是Navicat本身的bug而是Oracle客戶端驅(qū)動(dòng)在處理非標(biāo)準(zhǔn)會(huì)話狀態(tài)時(shí)的正常行為。理解這一點(diǎn)之后你應(yīng)該就能想到與其糾結(jié)這個(gè)錯(cuò)誤碼本身不如換個(gè)思路去服務(wù)端把真正的錯(cuò)誤事件挖出來(lái)。2. 從服務(wù)端事件跟蹤器挖出真實(shí)錯(cuò)誤這是排查的關(guān)鍵一步如果你打開(kāi)Navicat連接數(shù)據(jù)庫(kù)彈出的還是ORA-01012我建議你先別急著改Navicat的配置。正確的下一步是去看數(shù)據(jù)庫(kù)服務(wù)器上的事件跟蹤器SQL Trace / Event Log。這一步能把被包裝的真實(shí)錯(cuò)誤暴露出來(lái)。2.1 找到事件跟蹤器的位置事件跟蹤器是Oracle自帶的一個(gè)圖形化工具通常在Oracle客戶端安裝目錄下。最典型的是在開(kāi)始菜單里找Oracle - OraClientXX_home下面的配置和移植工具或集成管理工具里面有個(gè)名字帶事件跟蹤器Event Tracker的入口。如果你安裝的是完整客戶端一般都能找到。如果你服務(wù)器上只有命令行環(huán)境沒(méi)有圖形界面也可以用另一種方式直接查看alert日志。這是我更習(xí)慣的做法因?yàn)樯a(chǎn)服務(wù)器往往沒(méi)有桌面。2.2 查看alert日志定位根因alert日志一般在$ORACLE_BASE/diag/rdbms/{實(shí)例名}/{實(shí)例名}/trace/alert_{實(shí)例名}.log。用SQL*Plus或者直接登錄服務(wù)器進(jìn)去執(zhí)行下面這條SQL就能找到日志目錄SELECT value FROM v$diag_info WHERE name Diag Alert;如果實(shí)例已經(jīng)接近崩潰、SQL*Plus都進(jìn)不去那就用操作系統(tǒng)命令找find /u01/app/oracle -name alert_*.log 2/dev/null找到日志之后重點(diǎn)看最近一段時(shí)間的報(bào)錯(cuò)條目。以我的經(jīng)驗(yàn)最常見(jiàn)的幾種情況是ORA-01017: invalid username/password; logon denied賬號(hào)口令錯(cuò)或者賬號(hào)被特別處理過(guò)ORA-28000: the account is locked賬號(hào)被鎖ORA-28001: the password has expired口令過(guò)期ORA-12514: TNS listener does not currently know of service requested服務(wù)名不對(duì)常見(jiàn)于連接串里的服務(wù)名寫(xiě)錯(cuò)ORA-12541、ORA-12560這類的網(wǎng)絡(luò)監(jiān)聽(tīng)錯(cuò)誤如果alert日志里能看到這些具體的錯(cuò)誤碼那答案基本就明確了你就不用再在ORA-01012上死磕了。2.3 事件跟蹤器對(duì)比alert日志的使用場(chǎng)景事件跟蹤器和alert日志各有各的適用場(chǎng)景。事件跟蹤器更適合你在客戶端本機(jī)裝有完整Oracle客戶端的環(huán)境它能實(shí)時(shí)顯示服務(wù)端返回的每個(gè)事件alert日志則適合排查歷史問(wèn)題比如數(shù)據(jù)庫(kù)在某個(gè)時(shí)間點(diǎn)發(fā)生過(guò)什么異常。如果是生產(chǎn)環(huán)境、或者數(shù)據(jù)庫(kù)駐留在遠(yuǎn)程服務(wù)器上我個(gè)人更推薦直接用alert日志。因?yàn)槭录櫰魅菀子幸粋€(gè)局限它顯示的是客戶端本地收到的錯(cuò)誤如果錯(cuò)誤在網(wǎng)絡(luò)層就被淡化了也未必能看到真實(shí)根因。而alert日志是服務(wù)端的官方記錄可信度最高。提示排查ORA-01012時(shí)優(yōu)先看服務(wù)端alert日志這一步可以直接省掉大量無(wú)謂的客戶端調(diào)試。3. 數(shù)據(jù)庫(kù)自身狀態(tài)檢查從監(jiān)聽(tīng)器到實(shí)例的完整鏈路當(dāng)你從服務(wù)端日志里找到線索之后下一步就是把整個(gè)連接鏈路從頭到尾過(guò)一遍。我把這個(gè)檢查順序總結(jié)成先實(shí)例、再監(jiān)聽(tīng)、再賬號(hào)三步每一步都有對(duì)應(yīng)的驗(yàn)證命令。按照這個(gè)順序走基本能覆蓋80%以上的原因。3.1 檢查實(shí)例狀態(tài)和數(shù)據(jù)庫(kù)開(kāi)放狀態(tài)先確認(rèn)實(shí)例的狀態(tài)。用系統(tǒng)管理員賬號(hào)登進(jìn)數(shù)據(jù)庫(kù)或者用sqlplus以sysdba身份進(jìn)去執(zhí)行SELECT status FROM v$instance; SELECT open_mode FROM v$database;正常情況下第一個(gè)查詢應(yīng)該返回OPEN第二個(gè)查詢應(yīng)該返回READ WRITE。如果第一個(gè)查詢返回的是MOUNTED或STARTED說(shuō)明實(shí)例還沒(méi)完全啟動(dòng)完畢連接進(jìn)來(lái)自然會(huì)出現(xiàn)not logged on之類的錯(cuò)誤。這里有一個(gè)特殊情況如果數(shù)據(jù)庫(kù)是用startup upgrade方式啟動(dòng)的、或者正處于遷移狀態(tài)open_mode可能是READ WRITE之外的異常值。比如CONVERT、MIGRATE這些中間狀態(tài)此時(shí)客戶端連入也會(huì)報(bào)ORA-01012或類似的錯(cuò)誤。遇到這種情況先確認(rèn)license、遷移進(jìn)程是否結(jié)束再正常重啟一次實(shí)例。3.2 監(jiān)聽(tīng)器的檢查方法與常見(jiàn)假死情況實(shí)例狀態(tài)正常之后接著看監(jiān)聽(tīng)器。在服務(wù)器上執(zhí)行l(wèi)snrctl status重點(diǎn)關(guān)注輸出里的Service部分確認(rèn)你的數(shù)據(jù)庫(kù)service name是否在列表中以及狀態(tài)是否為UNKNOWN或READY。有一種非常典型的假活情況監(jiān)聽(tīng)器進(jìn)程還在端口也通但監(jiān)聽(tīng)器已經(jīng)沒(méi)有響應(yīng)了。這種情況下你從客戶端執(zhí)行tnsping是通的因?yàn)槎丝谀苓B通但真正建立會(huì)話時(shí)就會(huì)被拒絕表現(xiàn)也可能是一堆奇怪的ORA錯(cuò)誤。判斷方式很簡(jiǎn)單——你在服務(wù)器本地執(zhí)行l(wèi)snrctl status如果命令卡住不動(dòng)或者返回TNS-01169: The listener has not been started這類信息說(shuō)明監(jiān)聽(tīng)器進(jìn)程其實(shí)是掛了。處理方式也不復(fù)雜lsnrctl stop lsnrctl start如果監(jiān)聽(tīng)器經(jīng)常莫名其妙假死建議檢查一下監(jiān)聽(tīng)日志是否過(guò)大日志文件滿了之后監(jiān)聽(tīng)器會(huì)出現(xiàn)各種詭異問(wèn)題。清理監(jiān)聽(tīng)日志是個(gè)體力活但很有效不過(guò)操作前記得備份。3.3 賬號(hào)鎖定、口令過(guò)期與資源限制第三步檢查你連接用到的賬號(hào)??梢杂霉芾韱T賬號(hào)執(zhí)行SELECT username, account_status, lock_date, expiry_date FROM dba_users WHERE username YOUR_USERNAME;如果發(fā)現(xiàn)狀態(tài)是LOCKED或者EXPIRED把它解鎖就好ALTER USER your_username ACCOUNT UNLOCK; ALTER USER your_username IDENTIFIED BY your_password;還有一類坑是profile里設(shè)置了IDLE_TIME或者CONNECT_TIME限制。如果你用完連接后長(zhǎng)時(shí)間不操作會(huì)話被自動(dòng)斷開(kāi)此時(shí)新連接也可能報(bào)ORA-01012。檢查profile的方法SELECT resource_name, limit FROM dba_profiles WHERE profile (SELECT profile FROM dba_users WHERE username YOUR_USERNAME) AND resource_name IN (IDLE_TIME, CONNECT_TIME);如果限制太小可以調(diào)整或改用LIMITEDprofile。不過(guò)在生產(chǎn)環(huán)境我更建議從應(yīng)用層解決——讓連接池及時(shí)關(guān)閉空閑連接而不是依賴改數(shù)據(jù)庫(kù)參數(shù)。4. Navicat客戶端配置的常見(jiàn)坑tnsnames.ora與Oracle客戶端版本服務(wù)端都查完之后如果還是沒(méi)有定位到根因那就得回頭看Navicat這一側(cè)了。實(shí)際上我處理過(guò)的一個(gè)案例根因就出在客戶端的Oracle Net配置上服務(wù)端日志里根本沒(méi)有任何異常記錄??蛻舳伺渲眠@塊要分兩部分說(shuō)。4.1 tnsnames.ora配置錯(cuò)誤導(dǎo)致的ORA-01012Navicat通過(guò)OCI方式連接Oracle時(shí)最終還是要通過(guò)Oracle Net去解析服務(wù)名。如果你用的是服務(wù)名方式連接而tnsnames.ora里沒(méi)有對(duì)應(yīng)條目或者條目寫(xiě)錯(cuò)了就會(huì)觸發(fā)連接異常。檢查Navicat配置里填入的服務(wù)名字段和服務(wù)器上$ORACLE_HOME/network/admin/tnsnames.ora里的條目是否一致。一個(gè)典型的正確配置長(zhǎng)這樣ORCL (DESCRIPTION (ADDRESS (PROTOCOL TCP)(HOST 192.168.1.10)(PORT 1521)) (CONNECT_DATA (SERVER DEDICATED) (SERVICE_NAME orcl) ) )有個(gè)容易忽略的點(diǎn)SERVICE_NAME是服務(wù)名不一定是實(shí)例名。很多庫(kù)實(shí)例名叫orcl但服務(wù)名可能叫orcl.example.com具體取決于初始化參數(shù)service_names。判斷方法很簡(jiǎn)單登錄數(shù)據(jù)庫(kù)后執(zhí)行SHOW PARAMETER service_names;然后把Navicat里填的服務(wù)名改成這個(gè)值。這個(gè)操作雖小但經(jīng)常能解決莫名其妙的連接問(wèn)題。4.2 Navicat使用OCI驅(qū)動(dòng)時(shí)的版本匹配問(wèn)題另一個(gè)常見(jiàn)坑是Navicat自帶OCI驅(qū)動(dòng)和Oracle服務(wù)器版本不匹配。尤其在Oracle 12c、18c、19c這種大版本上如果Navicat配置的OCI庫(kù)版本太老建立連接時(shí)也會(huì)出現(xiàn)異常。在Navicat里工具 - 選項(xiàng) - 環(huán)境中可以看到OCI library的配置路徑。它默認(rèn)會(huì)使用Navicat安裝目錄下的OCI庫(kù)你也可以手動(dòng)指定到Oracle客戶端目錄下的oci.dllWindows或libclntsh.soLinux/macOS。我的建議是如果你機(jī)器上裝了Oracle完整客戶端優(yōu)先讓Navicat指向Oracle客戶端的OCI庫(kù)而不要用Navicat自帶的。原因很簡(jiǎn)單Oracle客戶端和服務(wù)器同版本之間兼容性最穩(wěn)Navicat附帶的OCI庫(kù)更新頻率不一定跟得上Oracle版本節(jié)奏。這里有一個(gè)值得注意的細(xì)節(jié)Oracle官方對(duì)于版本匹配有嚴(yán)格規(guī)定高版本客戶端連低版本數(shù)據(jù)庫(kù)通常沒(méi)問(wèn)題但低版本客戶端連高版本數(shù)據(jù)庫(kù)就可能出現(xiàn)意外。4.3 sqlnet.ora里容易忽略的SQLNET.AUTHENTICATION_SERVICES參數(shù)還有一個(gè)客戶端側(cè)的配置參數(shù)平時(shí)很少被注意到但在某些環(huán)境下會(huì)直接引發(fā)連接問(wèn)題sqlnet.ora里的SQLNET.AUTHENTICATION_SERVICES。如果你本機(jī)的sqlnet.ora里設(shè)置了SQLNET.AUTHENTICATION_SERVICES (NONE)而數(shù)據(jù)庫(kù)本身配置了某種外部認(rèn)證驗(yàn)證方式不匹配連接同樣會(huì)失敗。這種問(wèn)題比較隱蔽因?yàn)榉?wù)端日志里不一定有什么明顯痕跡客戶端所有參數(shù)看起來(lái)又都正常。處理方式也比較直接——先臨時(shí)把本機(jī)sqlnet.ora里的該參數(shù)注釋掉或者改為SQLNET.AUTHENTICATION_SERVICES (ALL)然后重啟Navicat再試一次。如果正常了說(shuō)明是認(rèn)證參數(shù)沖突再按實(shí)際安全策略收斂即可。注意修改后不需要重啟數(shù)據(jù)庫(kù)只需要重新連接即可。5. 真實(shí)案例復(fù)盤幾個(gè)典型根因的完整修復(fù)過(guò)程前面把整個(gè)排查框架講完了這一節(jié)我結(jié)合幾個(gè)親自處理過(guò)的案例來(lái)復(fù)盤。你會(huì)發(fā)現(xiàn)同一個(gè)ORA-01012背后的根因可能完全不一樣處理方式也大相徑庭。5.1 案例一密碼過(guò)期被當(dāng)成not logged on一個(gè)生產(chǎn)庫(kù)配套的報(bào)表賬號(hào)前一天還在正常跑數(shù)據(jù)同步第二天Navicat連接直接報(bào)ORA-01012。我按老套路先去服務(wù)器上看alert日志發(fā)現(xiàn)里面清清楚楚寫(xiě)著ORA-28001: the password has expired。原因也很常見(jiàn)Oracle 11g及以上版本默認(rèn)開(kāi)啟了密碼過(guò)期機(jī)制默認(rèn)壽命180天應(yīng)用賬號(hào)一直沒(méi)換過(guò)密碼就過(guò)期了。解決方案很簡(jiǎn)單把賬號(hào)密碼更新、并設(shè)置成長(zhǎng)期有效ALTER USER report_user IDENTIFIED BY new_password;同時(shí)可以臨時(shí)把該用戶的口令過(guò)期策略調(diào)掉ALTER PROFILE app_profile LIMIT PASSWORD_LIFE_TIME UNLIMITED;這里我要多說(shuō)一句生產(chǎn)中不建議一遇到密碼過(guò)期就改UNLIMITED尤其是核心業(yè)務(wù)賬號(hào)。應(yīng)該讓?xiě)?yīng)用側(cè)建立密碼周期替換機(jī)制或者用Oracle 12c及以后的Password File、AutoUpgrade這類功能來(lái)自動(dòng)化處理。但如果是自己跑測(cè)試、做數(shù)據(jù)分析的賬號(hào)設(shè)成UNLIMITED問(wèn)題不大省心。5.2 案例二數(shù)據(jù)庫(kù)被shutdown abort后重新啟動(dòng)到半途這個(gè)案例最有迷惑性。數(shù)據(jù)庫(kù)服務(wù)異常DBA執(zhí)行了shutdown abort隨后又執(zhí)行startup。結(jié)果startup進(jìn)行到一半卡住了監(jiān)聽(tīng)器顯示實(shí)例狀態(tài)正常但數(shù)據(jù)庫(kù)實(shí)際還在MOUNT狀態(tài)。此時(shí)Navicat連接報(bào)的就是ORA-01012。我去服務(wù)器上執(zhí)行ps -ef | grep ora_看到進(jìn)程都在執(zhí)行l(wèi)snrctl status監(jiān)聽(tīng)器也正常但登錄到SQL*Plus里執(zhí)行SELECT status FROM v$instance;返回的是MOUNTED。數(shù)據(jù)庫(kù)處于mount狀態(tài)時(shí)客戶端最多只能做控制文件相關(guān)操作普通業(yè)務(wù)連接當(dāng)然進(jìn)不來(lái)。等ALTER DATABASE OPEN;執(zhí)行完畢Navicat再連接立刻就好了。復(fù)盤這個(gè)案例的經(jīng)驗(yàn)是遇到ORA-01012第一件事不是調(diào)Navicat而是確認(rèn)數(shù)據(jù)庫(kù)到底處于什么狀態(tài)。實(shí)例狀態(tài)沒(méi)確認(rèn)之前其他所有客戶端操作都是浪費(fèi)時(shí)間。5.3 案例三Navicat連遠(yuǎn)程數(shù)據(jù)庫(kù)時(shí)監(jiān)聽(tīng)器半死狀態(tài)還有個(gè)案例數(shù)據(jù)庫(kù)和監(jiān)聽(tīng)器都正常但Navicat連接還是報(bào)ORA-01012。我反復(fù)看alert日志都沒(méi)有新記錄最后靈機(jī)一動(dòng)去服務(wù)器上執(zhí)行l(wèi)snrctl status發(fā)現(xiàn)命令一直卡在Connecting to...過(guò)了很久才打印出信息。這是監(jiān)聽(tīng)器半死的經(jīng)典表現(xiàn)——進(jìn)程在、端口通、但無(wú)法正常處理請(qǐng)求。原因通常是監(jiān)聽(tīng)日志文件太大超過(guò)2GB后Windows上會(huì)有問(wèn)題Linux上通常沒(méi)事但也會(huì)影響響應(yīng)速度或者監(jiān)聽(tīng)器線程有問(wèn)題。修復(fù)方式就是重啟監(jiān)聽(tīng)器lsnrctl stop lsnrctl start重啟之后Navicat立刻就能連上了。事后我把監(jiān)聽(tīng)日志做了個(gè)定時(shí)清理把超過(guò)一定大小的日志歸檔壓縮之后這個(gè)庫(kù)再?zèng)]出過(guò)同類問(wèn)題。提示遇到ORA-01012如果數(shù)據(jù)庫(kù)狀態(tài)、賬號(hào)狀態(tài)都正常一定記得去服務(wù)器上手動(dòng)執(zhí)行l(wèi)snrctl status觀察它是否卡頓。網(wǎng)絡(luò)層面能連通不代表監(jiān)聽(tīng)器健康。5.4 案例四本地OCI庫(kù)版本過(guò)舊導(dǎo)致連接協(xié)議不匹配最后一個(gè)案例是我自己本地折騰環(huán)境時(shí)遇到的。Navicat用的是自帶的OCI庫(kù)數(shù)據(jù)庫(kù)是19c結(jié)果是無(wú)論怎么配tnsnames.ora、賬號(hào)密碼絕對(duì)沒(méi)錯(cuò)連上瞬間就彈出ORA-01012。后來(lái)我發(fā)現(xiàn)Navicat連接設(shè)置里默認(rèn)使用的OCI庫(kù)路徑指向的是它安裝目錄下的老版本OCI把這個(gè)路徑改成Oracle客戶端安裝目錄下的oci.dll之后問(wèn)題當(dāng)場(chǎng)消失。其實(shí)原理也不復(fù)雜Oracle 19c默認(rèn)的會(huì)話數(shù)據(jù)加密和認(rèn)證參數(shù)比如SQLNET.ALLOWED_LOGON_VERSION_CLIENT比以前版本嚴(yán)格老版本OCI在協(xié)商階段就可能失敗。Navicat自帶的OCI庫(kù)版本如果太老就會(huì)出現(xiàn)連接報(bào)錯(cuò)但看不出具體原因的情況。這個(gè)案例強(qiáng)烈建議大家自查一下你的Navicat用的是哪個(gè)OCI庫(kù)版本是多少如果數(shù)據(jù)庫(kù)是12c以上最好讓Navicat指向客戶端最新版Oracle的OCI庫(kù)省心不少。6. 一些值得收藏的排查命令和后續(xù)優(yōu)化思路最后再把我常用的排查命令和思路集中整理一下。這些命令我已經(jīng)用習(xí)慣了每次遇到Oracle連接類問(wèn)題都從這里面找切入點(diǎn)。你也可以直接收藏這一節(jié)以后出問(wèn)題照著做。6.1 一套完整的驗(yàn)證命令序列假設(shè)你登錄到數(shù)據(jù)庫(kù)服務(wù)器上在確保賬號(hào)有權(quán)限的情況下按順序執(zhí)行以下操作查看實(shí)例狀態(tài)SELECT status FROM v$instance;返回OPEN則繼續(xù)否則先解決數(shù)據(jù)庫(kù)打開(kāi)問(wèn)題。查看數(shù)據(jù)庫(kù)模式SELECT open_mode FROM v$database;查看監(jiān)聽(tīng)器狀態(tài)lsnrctl status注意觀察命令是否快速返回以及Service列表是否包含你的目標(biāo)服務(wù)。查看賬號(hào)狀態(tài)SELECT username, account_status FROM dba_users WHERE username YOUR_USER;看alert日志最近一段時(shí)間的報(bào)錯(cuò)tail -200 $ORACLE_BASE/diag/rdbms/*/*/trace/alert_*.log這五步做完絕大多數(shù)ORA-01012的根因已經(jīng)浮出水面了。剩下的無(wú)非是根據(jù)具體原因去修。6.2 防患于未然減少ORA-01012出現(xiàn)頻率的幾個(gè)習(xí)慣經(jīng)歷過(guò)幾次之后我現(xiàn)在在環(huán)境搭建時(shí)就會(huì)提前做一些設(shè)置避免后來(lái)的人再踩坑。數(shù)據(jù)庫(kù)賬號(hào)的密碼過(guò)期策略要明確開(kāi)發(fā)測(cè)試庫(kù)可以直接設(shè)UNLIMITED生產(chǎn)庫(kù)走密碼周期更換流程。監(jiān)聽(tīng)日志要定期輪轉(zhuǎn)日志文件過(guò)大不僅拖慢監(jiān)聽(tīng)器還可能把磁盤塞滿。Linux環(huán)境下可以用logrotateWindows下寫(xiě)個(gè)計(jì)劃任務(wù)按大小清理。安裝客戶端時(shí)盡量選擇與服務(wù)器主版本相同或更高的版本。不要為了省空間去用精簡(jiǎn)版instant client完整的Oracle客戶端在排查問(wèn)題時(shí)能提供更多工具。Navicat里連接Oracle之前先在服務(wù)器本地用SQLPlus測(cè)一次連接。如果SQLPlus能連上而Navicat連不上問(wèn)題基本就鎖定在客戶端配置了如果兩邊都連不上優(yōu)先查服務(wù)端。6.3 如果以上方法都無(wú)效最后的兜底策略按照上面的鏈路檢查完理論上根因都能定位。但萬(wàn)一你就是遇到了那種所有檢查都正常、Navicat還是報(bào)ORA-01012的情況我還有一個(gè)兜底建議直接繞過(guò)OCi配置用Navicat自帶的instant client新建一個(gè)連接模式試試。具體做法是新建連接時(shí)在連接設(shè)置里找到高級(jí)或OCI相關(guān)選項(xiàng)取消使用自定義OCI路徑或者換一個(gè)Oracle客戶端版本路徑。這本質(zhì)上是強(qiáng)制更換連接驅(qū)動(dòng)實(shí)現(xiàn)很多時(shí)候能繞開(kāi)OCI庫(kù)的兼容性問(wèn)題。如果更換OCI路徑仍然不行那就試試用SQL*Plus或者SQL Developer(如果裝了)能否連上??蛻舳斯ぞ咧g對(duì)比連接結(jié)果能把問(wèn)題快速切分到是Oracle驅(qū)動(dòng)問(wèn)題還是Navicat本身問(wèn)題。這個(gè)切分思路在排查所有數(shù)據(jù)庫(kù)連接類問(wèn)題時(shí)都通用不局限于ORA-01012。從我個(gè)人的操作習(xí)慣來(lái)說(shuō)遇到ORA-01012我不再去背各種錯(cuò)誤碼的意義而是直接走服務(wù)端日志-實(shí)例狀態(tài)-監(jiān)聽(tīng)器-賬號(hào)狀態(tài)-客戶端OCI配置這條鏈路每一步都有明確的驗(yàn)證命令。這套方法幫我處理過(guò)幾十次類似連接問(wèn)題其中真正的not logged on場(chǎng)景其實(shí)很少大部分是被包裝過(guò)的其他原因。你下次再遇到Navicat報(bào)這個(gè)錯(cuò)不用慌按這條鏈路走一遍大概率能在十分鐘內(nèi)鎖定根因。