對(duì)比)
簡(jiǎn)介dgreadiness_v3.6.zip 面向在 Windows 10 上使用 VMware Workstation 運(yùn)行 Ubuntu 等虛擬機(jī)的開(kāi)發(fā)與運(yùn)維人員針對(duì)關(guān)閉 Hyper-V 后仍提示 VMware 與 Device/Credential Guard 不兼容、虛擬機(jī)無(wú)法啟動(dòng)的問(wèn)題提供基于 Windows Defender Device Guard 與 Credential Guard 硬件就緒工具的禁用方案。壓縮包共 6 個(gè)文件約 32KB包含 PowerShell 工具腳本、p7b 策略文件、xml 配置模板和 txt 說(shuō)明文檔分別用于執(zhí)行禁用配置、承載策略設(shè)置、提供審計(jì)/強(qiáng)制模式模板及使用說(shuō)明體量輕便、結(jié)構(gòu)明確。已有 3189 人學(xué)習(xí)下載說(shuō)明該問(wèn)題在 Win10 虛擬機(jī)場(chǎng)景中較常見(jiàn)。讀者可借助其中的腳本與策略模板快速完成 Credential Guard 關(guān)閉減少手動(dòng)修改組策略和注冊(cè)表的試錯(cuò)成本也可據(jù)說(shuō)明文檔理解各配置項(xiàng)作用為同類(lèi)兼容性排錯(cuò)提供參考。1. dgreadiness_v3.6.zip 不是安裝包是切換前的一張?bào)w檢單dgreadiness_v3.6.zip 這個(gè)名字本身就說(shuō)明了它的形態(tài)一個(gè)壓縮包、帶版本號(hào)、沒(méi)有安裝向?qū)Ы忾_(kāi)來(lái)是一堆 SQL 和 shell。它的作用不是部署容災(zāi)而是在你已經(jīng)有一套主備庫(kù)主庫(kù) 一個(gè)或多個(gè)備庫(kù)之后回答一個(gè)很具體的問(wèn)題——現(xiàn)在這一刻我敢不敢做切換演練很多團(tuán)隊(duì)的做法是切之前拉個(gè)會(huì)幾個(gè)人分別敲幾條命令看看憑感覺(jué)拍板這個(gè)包要做的事是把這些零散判斷固化成一份可重復(fù)、可對(duì)比、可交給別人的報(bào)告。它適合兩類(lèi)人一類(lèi)是要定期做容災(zāi)演練的 DBA另一類(lèi)是接手了一套沒(méi)人維護(hù)的備庫(kù)、想先摸清底細(xì)的運(yùn)維。讀完你能自己解包、配連接、改檢查項(xiàng)、定閾值也知道哪些綠燈其實(shí)是假的。2. 就緒度先立標(biāo)準(zhǔn)六類(lèi)檢查項(xiàng)和它們的權(quán)重怎么定打開(kāi)一個(gè) readiness 包最忌諱的動(dòng)作是直接bash run.sh然后盯著輸出里紅色的那幾行看。紅色代表什么、多紅才算不能切、這次紅下次綠是環(huán)境變了還是腳本抽風(fēng)——這些問(wèn)題在腳本里沒(méi)有答案得你自己先定。所以第一章不做操作先把“ready”這個(gè)詞拆成可判定的指標(biāo)。2.1 傳輸層、應(yīng)用層、結(jié)構(gòu)層就緒度的三個(gè)觀察面我把所有檢查項(xiàng)歸到三個(gè)觀察面這個(gè)分法決定了后面報(bào)告的排版也決定了排錯(cuò)時(shí)的查找順序。傳輸層看的是日志能不能從主庫(kù)順利落到備庫(kù)歸檔目的地狀態(tài)是不是 VALID、有沒(méi)有 ERROR 文本、傳輸延遲多少、歸檔目的地所在文件系統(tǒng)的剩余空間、網(wǎng)絡(luò)往返時(shí)延和帶寬是否夠峰值 redo 量。這一層出問(wèn)題表現(xiàn)是備庫(kù)長(zhǎng)時(shí)間收不到新日志但主庫(kù)業(yè)務(wù)一切正常最容易被忽略。應(yīng)用層看的是日志到了之后有沒(méi)有被真正應(yīng)用MRP 進(jìn)程在不在、狀態(tài)是 APPLYING_LOG 還是 WAITING_FOR_LOG、應(yīng)用延遲多少、備庫(kù)已應(yīng)用的最大序列號(hào)和主庫(kù)當(dāng)前序列號(hào)差多少、有沒(méi)有歸檔缺口GAP。這一層最容易出“假綠燈”后面避坑那章會(huì)專(zhuān)門(mén)講。結(jié)構(gòu)層看的是兩邊長(zhǎng)得像不像db_unique_name 和 log_archive_config 是否互認(rèn)、standby_file_management 是否 AUTO、保護(hù)模式和 redo 傳輸是 SYNC 還是 ASYNC、standby redo log 的組數(shù)和單組大小、臨時(shí)文件和數(shù)據(jù)文件路徑、密碼文件、以及多租戶(hù)下每個(gè) PDB 的狀態(tài)。結(jié)構(gòu)層的問(wèn)題平時(shí)完全不疼一切換就卡死是典型的“平時(shí)不查、演練翻車(chē)”。三個(gè)面里傳輸層和應(yīng)用層是動(dòng)態(tài)指標(biāo)數(shù)值會(huì)隨業(yè)務(wù)波動(dòng)結(jié)構(gòu)層是靜態(tài)指標(biāo)除非有人改配置否則長(zhǎng)期不變。這個(gè)差異直接影響采集頻率動(dòng)態(tài)指標(biāo)要頻繁采、看趨勢(shì)靜態(tài)指標(biāo)一次采完存成基線(xiàn)就行不必每次全量跑。提示如果包里的檢查項(xiàng)命名和上面不一樣不要急著改腳本先按這三個(gè)面把現(xiàn)有項(xiàng)歸類(lèi)缺哪一類(lèi)再補(bǔ)比重寫(xiě)省事得多。2.2 權(quán)重怎么定按「切換后多久能對(duì)外服務(wù)」倒推權(quán)重不是技術(shù)參數(shù)是業(yè)務(wù)參數(shù)。我的做法是問(wèn)一句話(huà)這一項(xiàng)不達(dá)標(biāo)切換后要多花多久才能對(duì)外服務(wù)答案越長(zhǎng)權(quán)重越高。按這個(gè)口徑歸檔缺口和結(jié)構(gòu)層錯(cuò)配是最貴的——缺口意味著要補(bǔ)日志甚至重建結(jié)構(gòu)錯(cuò)配意味著切換根本起不來(lái)這兩類(lèi)給高權(quán)重傳輸延遲和應(yīng)用延遲影響的是切換瞬間丟多少數(shù)據(jù)、追平要多久中等權(quán)重目的地狀態(tài)、SRL 配置這類(lèi)屬于“不達(dá)標(biāo)立刻知道、修起來(lái)也快”的低權(quán)重。檢查項(xiàng)觀察面權(quán)重不達(dá)標(biāo)后果歸檔序列缺口應(yīng)用層25需補(bǔ)日志或重建備庫(kù)參數(shù)與結(jié)構(gòu)一致性結(jié)構(gòu)層20切換可能直接失敗應(yīng)用延遲應(yīng)用層20切換后追平日志耗時(shí)長(zhǎng)傳輸延遲傳輸層15數(shù)據(jù)丟失窗口變大目的地狀態(tài)與錯(cuò)誤文本傳輸層10日志傳輸中斷SRL 組數(shù)與大小結(jié)構(gòu)層10切換時(shí)日志寫(xiě)不下這張表不需要照抄權(quán)重之和湊夠 100 就行關(guān)鍵是每一項(xiàng)的權(quán)重你能對(duì)著業(yè)務(wù)說(shuō)清楚理由。說(shuō)不清理由的項(xiàng)要么刪掉要么降權(quán)別放在那里制造噪音。2.3 判定口徑要先寫(xiě)死否則報(bào)告沒(méi)法比較分?jǐn)?shù)之外還要有檔位否則你只知道“78 分”不知道“78 分能不能切”。我一般分三檔PASS、WARN、FAIL。PASS 拿滿(mǎn)分WARN 拿一半FAIL 拿零分。但光有分?jǐn)?shù)不夠——結(jié)構(gòu)層錯(cuò)配這種項(xiàng)哪怕總分 95 也不能切所以還要引入“否決項(xiàng)”概念歸檔缺口、SRL 配置、參數(shù)一致性這三項(xiàng)只要有一個(gè) FAIL無(wú)論總分多少都判定為 NOT READY。這樣做的好處是報(bào)告有兩層結(jié)論分?jǐn)?shù)用來(lái)橫向比較這次 92、上次 88是在變好還是變差否決項(xiàng)用來(lái)縱向決策能不能現(xiàn)在切。只給分?jǐn)?shù)人會(huì)拿分?jǐn)?shù)當(dāng)擋箭牌只給否決項(xiàng)又看不出趨勢(shì)。兩者都要。還有一條容易被忽略口徑要寫(xiě)進(jìn)配置文件不要寫(xiě)死在腳本里。閾值、權(quán)重、否決項(xiàng)名單都放conf/env.conf這樣半年后別人接手改一個(gè)數(shù)字就能適配另一套庫(kù)不用讀懂 SQL。3. 解包與首輪巡檢三條命令跑出第一份報(bào)告標(biāo)準(zhǔn)立完動(dòng)手。這一章的目標(biāo)不是跑出完美報(bào)告是在 20 分鐘內(nèi)跑出第一份“能看”的報(bào)告哪怕里面一半是 FAIL——先有基線(xiàn)再談優(yōu)化。3.1 解包與目錄約定先固定路徑再談自動(dòng)化不要在原目錄里直接解壓。壓縮包解開(kāi)后通常有bin/、sql/、conf/、out/這幾類(lèi)目錄如果每次巡檢都解到不同路徑報(bào)告會(huì)散落在各處后面想做趨勢(shì)對(duì)比就得靠人肉找文件。我固定用一個(gè)版本化路徑# 固定安裝路徑按版本號(hào)分目錄升級(jí)時(shí)并存而不是覆蓋 sudo mkdir -p /opt/dgcheck/3.6 sudo unzip -q dgreadiness_v3.6.zip -d /opt/dgcheck/3.6 # 看一眼結(jié)構(gòu)確認(rèn)入口腳本和 SQL 目錄在哪 find /opt/dgcheck/3.6 -maxdepth 2 -type d | sort ls -l /opt/dgcheck/3.6/bin/ 2/dev/null || ls -l /opt/dgcheck/3.6 # 腳本給執(zhí)行權(quán)限但不要給全局可寫(xiě) sudo chown -R dbcheck:dba /opt/dgcheck/3.6 sudo chmod -R 750 /opt/dgcheck/3.6 sudo chmod 640 /opt/dgcheck/3.6/conf/*.conf 2/dev/null這段的關(guān)鍵點(diǎn)在三處。-d指定目標(biāo)目錄避免解壓出來(lái)的文件平鋪在當(dāng)前目錄把家目錄搞亂。find -maxdepth 2是先摸清結(jié)構(gòu)再?zèng)Q定怎么調(diào)因?yàn)椴煌虬?xí)慣里入口腳本可能在bin/也可能在根目錄ls一次就知道比自己猜快。chmod 640給配置文件是因?yàn)槔锩嫱ǔS羞B接串和口令權(quán)限放開(kāi)等于把庫(kù)的鑰匙掛在墻上。專(zhuān)用運(yùn)行賬號(hào)也是必要的一步。我一般建一個(gè)只讀賬號(hào)dbcheck跑巡檢讓它屬于dba組以便讀v$視圖但不給它sysdba。巡檢腳本只需要看狀態(tài)不需要改任何東西——一個(gè)能改東西的巡檢腳本某天就會(huì)改錯(cuò)東西這是血淚經(jīng)驗(yàn)。注意如果包里帶.sh之外的可執(zhí)行文件比如編譯好的采集二進(jìn)制先用file看一眼架構(gòu)和依賴(lài)別在 x86 機(jī)器上跑 arm 的產(chǎn)物報(bào)錯(cuò)信息會(huì)誤導(dǎo)你以為連不上庫(kù)。3.2 連接配置與最小巡檢命令配置文件只放連接信息和閾值不放任何邏輯。常見(jiàn)做法是conf/env.conf這種以 shell 變量形式被source進(jìn)來(lái)的文件# conf/env.conf —— 只放連接與閾值邏輯一律進(jìn)腳本 PRIMARY_TNSdg_primary # 主庫(kù)連接串走 tnsnames 或等價(jià)命名 STANDBY_TNSdg_standby # 備庫(kù)連接串 RUN_USERdbcheck # 巡檢專(zhuān)用賬號(hào)非 sysdba THRESH_TRANSPORT_LAG60 # 傳輸延遲告警線(xiàn)單位秒 THRESH_APPLY_LAG120 # 應(yīng)用延遲告警線(xiàn)單位秒 THRESH_ARCHIVE_GAP0 # 允許的序列缺口通常為 0 THRESH_FRA_PCT85 # 歸檔區(qū)使用率告警線(xiàn)百分比 DENY_ITEMSarchive_gap,srl_config,param_diff # 否決項(xiàng)名單四個(gè)閾值里前三個(gè)是硬指標(biāo)最后一個(gè)歸檔區(qū)使用率是很多人不做但極容易翻車(chē)的項(xiàng)——?dú)w檔區(qū)滿(mǎn)的那一刻主庫(kù)會(huì)掛起備庫(kù)也跟著停屬于“平時(shí)不查、爆起來(lái)要命”的類(lèi)型。DENY_ITEMS是把第 2 章的口徑落地的字段后面折算報(bào)告時(shí)直接讀這個(gè)變量。配好之后首輪就跑最小集合一個(gè)主庫(kù)側(cè)、一個(gè)備庫(kù)側(cè)別一上來(lái)跑全套不然輸出里幾百行你不知道哪行是關(guān)鍵的。#!/usr/bin/env bash # bin/first_run.sh —— 首輪最小巡檢只采集傳輸與應(yīng)用兩個(gè)面 set -uo pipefail BASE/opt/dgcheck/3.6 source $BASE/conf/env.conf TS$(date %Y%m%d_%H%M%S) OUT$BASE/out/$TS mkdir -p $OUT # 主庫(kù)歸檔目的地狀態(tài) 當(dāng)前最大序列 sqlplus -S $RUN_USER$PRIMARY_TNS $BASE/sql/10_primary.sql \ $OUT/primary.txt 21 # 備庫(kù)應(yīng)用進(jìn)程狀態(tài) 已應(yīng)用序列 sqlplus -S $RUN_USER$STANDBY_TNS $BASE/sql/20_standby.sql \ $OUT/standby.txt 21 echo report dir: $OUT grep -iE error|ORA-|SP2- $OUT/*.txt echo 存在錯(cuò)誤先看上面幾行 || echo 采集無(wú)報(bào)錯(cuò)set -uo pipefail沒(méi)有加-e這是有意的首輪巡檢最怕的是某項(xiàng)失敗就整體退出導(dǎo)致你只看到第一個(gè)問(wèn)題、看不到后面的問(wèn)題。所以讓每個(gè)采集動(dòng)作各自失敗、各自留痕最后統(tǒng)一 grep 一遍錯(cuò)誤關(guān)鍵字。-S是靜默模式去掉客戶(hù)端橫幅和提示符輸出更干凈后面好解析。時(shí)間戳精確到秒是為了同一天多次演練時(shí)不覆蓋文件。grep -iE error|ORA-|SP2-這行是一個(gè)廉價(jià)但有效的兜底。數(shù)據(jù)庫(kù)客戶(hù)端的報(bào)錯(cuò)文本格式很好認(rèn)寬泛匹配一下就能把“沒(méi)連上庫(kù)”和“連上了但查詢(xún)出錯(cuò)”區(qū)分開(kāi)——這兩種情況后面的處理完全不一樣。3.3 輸出目錄與失敗兜底o(hù)ut/目錄要有輪轉(zhuǎn)策略別等它撐滿(mǎn)。一次全量巡檢的輸出不大但每天跑、跑半年加上有人手動(dòng)重跑很容易堆到幾百個(gè)目錄。我一般保留最近 30 次全量 所有“變更前后”的對(duì)比目錄其余刪除# 只保留最近 30 個(gè)時(shí)間戳目錄其余清理命名規(guī)則必須嚴(yán)格按時(shí)間戳 cd /opt/dgcheck/3.6/out || exit 0 ls -1d 2*/ | sort | head -n -30 | xargs -r rm -rf前提是目錄名嚴(yán)格是YYYYMMDD_HHMMSSsort的字典序才等于時(shí)間序。如果包自帶的輸出目錄用了別的命名比如帶主機(jī)名就先改命名規(guī)則再用這條命令否則head -n -30刪掉的可能是昨天剛跑的基線(xiàn)這種翻車(chē)比不清理更糟。失敗兜底還有一個(gè)細(xì)節(jié)退出碼。巡檢腳本的退出碼要能被上層調(diào)度感知——0 表示全 PASS1 表示有 WARN2 表示有 FAIL 或否決項(xiàng)。這樣掛到定時(shí)任務(wù)或流水線(xiàn)上不用解析文本就知道結(jié)果。很多包默認(rèn)只 echo 不 return接進(jìn)自動(dòng)化就得自己補(bǔ)這幾行。4. 改 SQL 與定閾值把通用檢查項(xiàng)調(diào)成你庫(kù)的形狀一份通用腳本跑在你自己的庫(kù)上一定會(huì)有一半檢查項(xiàng)“看起來(lái)對(duì)但不準(zhǔn)”。這不是腳本的問(wèn)題是每套庫(kù)的形態(tài)差異太大有的單實(shí)例、有的多租戶(hù)有的保護(hù)模式要求零丟失、有的能接受幾分鐘延遲有的 redo 峰值每秒幾十兆、有的幾百 K。所以第三章的核心動(dòng)作是改改 SQL 的取數(shù)口徑、改閾值的紅線(xiàn)位置。4.1 三類(lèi)必改的 SQL序列缺口、備庫(kù)日志、參數(shù)一致性序列缺口是最該改的一條。很多腳本只查應(yīng)用延遲但延遲是個(gè)瞬時(shí)值——?jiǎng)倓倯?yīng)用完一批日志延遲會(huì)顯示接近 0哪怕中間其實(shí)缺了一段日志沒(méi)傳過(guò)來(lái)。真正的缺口要看序列號(hào)連續(xù)性-- sql/30_gap.sql主庫(kù)側(cè)取每個(gè)線(xiàn)程的當(dāng)前序列 set lines 200 pages 100 feedback off heading on col thread# format 999 col cur_seq format 9999999 select thread#, max(sequence#) as cur_seq from v$thread group by thread# order by thread#;-- sql/31_applied.sql備庫(kù)側(cè)取每個(gè)線(xiàn)程已應(yīng)用的序列 col thread# format 999 col applied_seq format 9999999 select thread#, max(sequence#) as applied_seq from v$archived_log where applied YES group by thread# order by thread#;邏輯說(shuō)明主庫(kù)的v$thread給出每個(gè)線(xiàn)程當(dāng)前寫(xiě)到哪備庫(kù)的v$archived_log里appliedYES的最大值給出應(yīng)用到哪。兩者相減就是缺口。多線(xiàn)程RAC 形態(tài)下必須按 thread 分別算只取全局最大值會(huì)漏掉某個(gè)線(xiàn)程掉隊(duì)的場(chǎng)景——這種場(chǎng)景在小庫(kù)上不常見(jiàn)在大庫(kù)上就是演練失敗的常見(jiàn)原因。參數(shù)說(shuō)明appliedYES這個(gè)過(guò)濾條件是關(guān)鍵去掉它拿到的是“收到了多少”而不是“應(yīng)用了多少”這兩者在備庫(kù)開(kāi)啟實(shí)時(shí)應(yīng)用時(shí)差別很大。如果你的庫(kù)用的是備庫(kù)實(shí)時(shí)應(yīng)用還可以補(bǔ)一條查v$standby_apply_progress但對(duì)缺口判斷來(lái)說(shuō)上面兩條已經(jīng)夠用。**備庫(kù)日志SRL**是最容易被跳過(guò)的一條因?yàn)樗挥绊懭粘_\(yùn)行只影響切換-- sql/40_srl.sql兩邊分別跑比對(duì)組數(shù)與單組大小 col group# format 999 col mb format 9999 select group#, thread#, bytes/1024/1024 as mb, status from v$standby_log order by thread#, group#; select group#, thread#, bytes/1024/1024 as mb from v$log order by thread#, group#;判據(jù)是經(jīng)驗(yàn)值備庫(kù) SRL 的組數(shù)不少于主庫(kù)在線(xiàn) redo 組數(shù) 每線(xiàn)程 1 組單組大小與主庫(kù)一致不能小于。組數(shù)不夠切換瞬間會(huì)因?yàn)闊o(wú)處寫(xiě)日志而卡住大小不一致切換后可能出現(xiàn)頻繁切換log switch拖慢性能。這兩條都屬于“不改也沒(méi)事、一切換就死”的類(lèi)型。參數(shù)一致性要挑真正影響切換的那幾個(gè)別把全部參數(shù)拉出來(lái)比對(duì)噪音太大-- sql/50_param.sql只比對(duì)與容災(zāi)強(qiáng)相關(guān)的參數(shù) col name format a32 col value format a48 select name, value from v$parameter where name in ( db_unique_name,log_archive_config,log_archive_dest_state_2, standby_file_management,protection_mode,archive_log_format, db_recovery_file_dest_size,remote_login_passwordfile ) order by name;這十個(gè)參數(shù)里log_archive_config少寫(xiě)一個(gè)備庫(kù)名、standby_file_management是 MANUAL、remote_login_passwordfile不是 EXCLUSIVE任意一條都能讓切換直接失敗。db_recovery_file_dest_size放在這里是很多人的疑問(wèn)——它影響的是歸檔空間不在切換時(shí)立刻報(bào)錯(cuò)但會(huì)在切換后補(bǔ)日志階段把空間撐爆屬于第二波翻車(chē)。提示參數(shù)比對(duì)一定要在主備兩邊都跑同一條 SQL 再 diff不要拿主庫(kù)的輸出去和心里記著的備庫(kù)配置比記憶比腳本更容易錯(cuò)。4.2 閾值不是拍腦袋用歷史延遲分布定紅線(xiàn)THRESH_APPLY_LAG120這個(gè)數(shù)字從哪來(lái)很多人是從別人的腳本里抄的。抄來(lái)的數(shù)字有兩個(gè)問(wèn)題一是和自己的業(yè)務(wù)量不匹配二是沒(méi)人知道為什么是 120。我的做法是先裸跑兩周只采集不判定把每次的傳輸延遲、應(yīng)用延遲記下來(lái)然后看分布分位傳輸延遲應(yīng)用延遲用途p502 秒15 秒常態(tài)參考不必告警p9540 秒90 秒WARN 線(xiàn)留出批量作業(yè)的余量p9975 秒150 秒FAIL 線(xiàn)超過(guò)說(shuō)明真的出問(wèn)題了規(guī)律很樸素WARN 線(xiàn)設(shè)在 p95 附近FAIL 線(xiàn)設(shè)在 p99 或 p95 的兩倍。這樣日常波動(dòng)不會(huì)天天報(bào)警真出事的時(shí)候一定越線(xiàn)。設(shè)置完還要回頭看一次——如果一周內(nèi) WARN 觸發(fā)了十幾次說(shuō)明線(xiàn)畫(huà)低了或者這條鏈路的容量本來(lái)就不夠該擴(kuò)容而不是調(diào)閾值。批量作業(yè)窗口要單獨(dú)對(duì)待。夜間跑大批量的時(shí)候apply lag 從 15 秒沖到 300 秒是正常的如果這時(shí) FAIL 了你會(huì)收到一堆無(wú)效告警然后開(kāi)始忽略告警——這是運(yùn)維里最危險(xiǎn)的狀態(tài)。解決辦法是給批量窗口單獨(dú)一組閾值按時(shí)間切換配置而不是把全局閾值放寬到 300 秒那樣等于白天也不告警了。4.3 把文本輸出收斂成一張就緒度表采集出來(lái)是一堆文本人看得懂、腳本看不懂。中間加一層折算把關(guān)鍵指標(biāo)壓成keyvalue再交給一個(gè)小腳本算分# 采集腳本末尾把關(guān)鍵指標(biāo)壓成一行一個(gè)鍵值供折算腳本消費(fèi) { echo archive_gap$ARCHIVE_GAP echo apply_lag$APPLY_LAG echo transport_lag$TRANSPORT_LAG echo dest_status$DEST_STATUS echo srl_ok$SRL_OK echo param_diff$PARAM_DIFF } $OUT/metrics.kv#!/usr/bin/env python3 # bin/score.py —— 把 metrics.kv 折算成就緒度分?jǐn)?shù)與結(jié)論 import sys, os WEIGHTS {archive_gap: 25, param_diff: 20, apply_lag: 20, transport_lag: 15, dest_status: 10, srl_ok: 10} DENY set(os.environ.get(DENY_ITEMS, ).split(,)) def load(path): kv {} with open(path, encodingutf-8) as f: for line in f: line line.strip() if not line or not in line: continue k, v line.split(, 1) kv[k.strip()] v.strip() return kv def judge(key, raw, kv): # 數(shù)值型指標(biāo)與閾值比較非數(shù)值型按 1/0 判定 if key archive_gap: return PASS if int(raw) 0 else (WARN if int(raw) 3 else FAIL) if key in (apply_lag, transport_lag): warn float(os.environ.get(THRESH_ key.upper(), 120)) v float(raw) return PASS if v warn else (WARN if v warn * 2 else FAIL) return PASS if raw in (1, VALID, TRUE) else FAIL def main(path): kv load(path) score, denies, rows 0.0, [], [] for key, w in WEIGHTS.items(): if key not in kv: rows.append((key, MISSING, 0)) continue res judge(key, kv[key], kv) got w if res PASS else (w * 0.5 if res WARN else 0) score got if res FAIL and key in DENY: denies.append(key) rows.append((key, res, got)) for k, r, g in rows: print(f{k:16s} {r:8s} {g:5.1f}) verdict NOT READY if denies else (READY if score 90 else RISKY) print(f\nscore{score:.1f} verdict{verdict} deny{,.join(denies) or -}) if __name__ __main__: main(sys.argv[1])邏輯上分三層。WEIGHTS是第 2 章那張表的代碼化judge是個(gè)小狀態(tài)機(jī)把原始數(shù)值翻譯成 PASS/WARN/FAIL注意 WARN 的判定用的是“閾值的兩倍”這樣只改一個(gè)配置就能同時(shí)控制兩檔最后主線(xiàn)按權(quán)重累加denies收集否決項(xiàng)。指標(biāo)缺失MISSING單獨(dú)記一檔而不是直接 FAIL因?yàn)槿笔遣杉〔皇黔h(huán)境壞混在一起會(huì)誤導(dǎo)排查方向。參數(shù)說(shuō)明DENY_ITEMS從環(huán)境變量讀是為了讓同一份腳本能跑“演練前嚴(yán)格模式”和“日常寬松模式”兩套口徑不用改代碼。閾值同樣從環(huán)境變量兜底o(hù)s.environ.get(..., 120)保證腳本單獨(dú)運(yùn)行也不崩。跑起來(lái)驗(yàn)證一下# 用一次真實(shí)采集結(jié)果折算先看分?jǐn)?shù)再看否決項(xiàng) cd /opt/dgcheck/3.6 DENY_ITEMSarchive_gap,srl_ok,param_diff python3 bin/score.py out/20240101_020000/metrics.kv輸出如果verdictNOT READY但score92.0說(shuō)明分?jǐn)?shù)被某一兩個(gè)高分項(xiàng)撐著否決項(xiàng)里有 FAIL——這正是第 2 章說(shuō)的“分?jǐn)?shù)管趨勢(shì)、否決項(xiàng)管決策”別被 92 分騙了。5. 排查記錄五個(gè)讓報(bào)告「全綠」卻切不過(guò)去的坑5.1 延遲顯示 0 秒但備庫(kù)落后兩小時(shí)現(xiàn)象報(bào)告里 apply lag 一直是 0 到 2 秒看起來(lái)健康真做切換時(shí)發(fā)現(xiàn)丟了大量數(shù)據(jù)業(yè)務(wù)對(duì)賬對(duì)不上。原因apply lag 的語(yǔ)義是“最近一條已應(yīng)用日志的應(yīng)用時(shí)間與當(dāng)前時(shí)間之差”它衡量的是“追平速度”不是“落后多少”。如果日志根本沒(méi)傳到備庫(kù)備庫(kù)無(wú)日志可應(yīng)用這個(gè)值反而會(huì)很小甚至為 0——這是最典型的假綠燈。解決把歸檔缺口序列號(hào)差值作為主判據(jù)延遲只作為輔判據(jù)。缺口不為 0 時(shí)無(wú)論延遲多好看都判 FAIL。這條一定要進(jìn)否決項(xiàng)名單。5.2 SRL 組數(shù)和主庫(kù)一樣多切換仍然卡住現(xiàn)象比對(duì)下來(lái) SRL 組數(shù)“看起來(lái)夠”切換演練時(shí)仍然在 redo 寫(xiě)入階段卡了幾十秒。原因組數(shù)夠但單組大小小于主庫(kù)的在線(xiàn) redo切換后日志切換頻率暴增或者組數(shù)是照“主庫(kù)組數(shù)”配的沒(méi)有算上每線(xiàn)程多一組的余量。多線(xiàn)程環(huán)境下每個(gè)線(xiàn)程都需要獨(dú)立的 SRL 組共用一個(gè)組池也會(huì)卡。解決按“每線(xiàn)程主庫(kù)在線(xiàn)組數(shù) 1”配單組大小嚴(yán)格等于主庫(kù) redo 大小字節(jié)數(shù)一致而不是四舍五入后的兆數(shù)一致。改完重跑 4.1 里那條 SRL SQL 確認(rèn)。5.3 巡檢賬號(hào)權(quán)限過(guò)高審計(jì)上翻車(chē)現(xiàn)象腳本跑得好好的某次安全審計(jì)里被指出巡檢使用了高權(quán)限賬號(hào)且口令明文放在配置文件里。原因?yàn)榱耸∈轮苯佑昧俗罡邫?quán)限賬號(hào)順手把口令寫(xiě)進(jìn)env.conf文件權(quán)限還是 644。解決換專(zhuān)用只讀賬號(hào)只授查詢(xún)v$視圖所需的最小權(quán)限配置文件權(quán)限收到 640 并限定屬組口令改走外部認(rèn)證或憑據(jù)文件不進(jìn)版本庫(kù)。這條不是技術(shù)問(wèn)題但會(huì)真的讓項(xiàng)目停掉早改比晚改省事。5.4 輸出目錄把磁盤(pán)撐滿(mǎn)反過(guò)來(lái)拖垮歸檔現(xiàn)象某天主庫(kù)歸檔區(qū)寫(xiě)滿(mǎn)業(yè)務(wù)掛起查下來(lái)是巡檢報(bào)告占了同一個(gè)文件系統(tǒng)。原因out/目錄沒(méi)做輪轉(zhuǎn)加上有人為了排查重復(fù)手動(dòng)跑全量一年下來(lái)堆了上千個(gè)目錄。巡檢工具本身成了故障源這個(gè)坑我見(jiàn)過(guò)不止一次。解決輸出目錄和歸檔區(qū)物理隔離別放同一個(gè)掛載點(diǎn)加輪轉(zhuǎn)3.3 那條并給巡檢腳本本身加一個(gè)“單次輸出不超過(guò) N 兆”的保護(hù)——如果超了說(shuō)明采集范圍寫(xiě)錯(cuò)了應(yīng)該報(bào)錯(cuò)而不是繼續(xù)寫(xiě)。5.5 多租戶(hù)下 PDB 狀態(tài)沒(méi)納入檢查現(xiàn)象容器庫(kù)層面全綠切換后發(fā)現(xiàn)某個(gè) PDB 處于 MOUNT 或者沒(méi)開(kāi)讀寫(xiě)。原因檢查項(xiàng)只查了容器級(jí)視圖沒(méi)有遍歷 PDB。多租戶(hù)環(huán)境里 PDB 的狀態(tài)是獨(dú)立管理的容器級(jí)看起來(lái)一切正常。解決在結(jié)構(gòu)層加一條遍歷 PDB 的查詢(xún)把每個(gè) PDB 的打開(kāi)模式和恢復(fù)狀態(tài)列出來(lái)任一 PDB 不是 READ WRITE 就判 WARN若你的業(yè)務(wù)依賴(lài)該 PDB 則升級(jí)為 FAIL。這條屬于“不查不知道、查了才發(fā)現(xiàn)”的類(lèi)型建議第一次就補(bǔ)上。6. 把它釘進(jìn)演練流程基線(xiàn)對(duì)比與回歸的兩種做法跑通一次巡檢不算什么能讓它在半年后仍然是團(tuán)隊(duì)判斷切換時(shí)機(jī)的依據(jù)才算落地。我用兩種做法把它釘住定時(shí)留基線(xiàn)變更前后做 diff。定時(shí)巡檢我掛在業(yè)務(wù)低峰只跑全量并把結(jié)果目錄按“日期_類(lèi)型”命名類(lèi)型分daily和drill。daily每天一次用來(lái)攢趨勢(shì)數(shù)據(jù)drill只在演練和變更前跑用來(lái)做決策。兩者閾值可以不同daily用寬松口徑只看異常drill用嚴(yán)格口徑加全部否決項(xiàng)。這樣平時(shí)不會(huì)天天被報(bào)警吵真要切換的時(shí)候有一個(gè)不會(huì)說(shuō)謊的口徑。# 低峰定時(shí)每日基線(xiàn) 目錄按類(lèi)型分方便后面按類(lèi)型做趨勢(shì) 30 2 * * * /opt/dgcheck/3.6/bin/run_all.sh -t daily \ /opt/dgcheck/3.6/out/cron.log 21 # 演練前手工嚴(yán)格口徑帶完整否決項(xiàng) # /opt/dgcheck/3.6/bin/run_all.sh -t drill第二種做法更重要變更前后做 diff而不是看單次分?jǐn)?shù)。因?yàn)榉謹(jǐn)?shù)會(huì)騙人——加了個(gè)索引、換了批處理時(shí)間分?jǐn)?shù)可能從 88 漲到 93但某項(xiàng)具體指標(biāo)其實(shí)是惡化的只是被其他項(xiàng)蓋過(guò)去了。所以我每次都把metrics.kv和上一次 drill 的做逐鍵對(duì)比# 變更前后逐鍵 diff只關(guān)心變化方向和幅度 diff (sort out/20240101_drill/metrics.kv) \ (sort out/20240201_drill/metrics.kv) | grep -E ^[]輸出里是變更后是變更前。如果archive_gap從 0 變成 1哪怕分?jǐn)?shù)還在 90 以上也要停下來(lái)查——缺口從 0 到 1 往往意味著某個(gè)歸檔目的地開(kāi)始間歇性失敗這種早期信號(hào)只有 diff 抓得到。還有一個(gè)我踩過(guò)的細(xì)節(jié)基線(xiàn)要跟著版本走。升級(jí)到 3.6 之后新增了檢查項(xiàng)舊基線(xiàn)里沒(méi)有這些鍵diff 出來(lái)一堆看著像惡化其實(shí)是新增。所以升級(jí)后第一件事是重跑一次 drill 生成新基線(xiàn)并把舊基線(xiàn)的metrics.kv單獨(dú)歸檔不要混在同一個(gè)目錄里比。我自己的習(xí)慣是每次演練前打印一行結(jié)論貼在工單里score / verdict / deny 列表 / 與上次 drill 的差異項(xiàng)。四個(gè)字段誰(shuí)看都清楚也逼著自己別只看總分。這套東西的價(jià)值不在腳本多聰明在于它把“我覺(jué)得可以切”變成“這份報(bào)告說(shuō)可以切而且和上次比沒(méi)變差”——前者靠人后者能交接。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取