Oracle RAC 与 ADG 的巡检建议按四层依次进行:集群层 → ASM 存储层 → 数据库实例层 → Data Guard 同步层。如果 RAC 主库和 RAC 备库都需要巡检,就应在主库和备库分别执行一遍。
一、集群层(Grid 用户)
bash
# 1. 集群守护进程状态(CRS/CSS/OHASD)
crsctl check crs
# 2. 所有集群资源状态,重点看 ONLINE/OFFLINE/UNKNOWN
crsctl stat res -t
crsctl stat res -t -init
# 3. 节点成员与角色
olsnodes -n -s -t
# 4. 私网(心跳)网卡配置,确认 interconnect 绑定正确
oifcfg getif
# 5. 表决盘与 OCR
crsctl query css votedisk
ocrcheck
# 6. 数据库/服务/监听资源
srvctl status database -d <db_unique_name> -v
srvctl status service -d <db_unique_name>
srvctl config database -d <db_unique_name>
srvctl status asm -n <节点名>
srvctl status listener
lsnrctl status
二、ASM 存储层(+ASM 实例)
bash
asmcmd lsdg
asmcmd lsdsk --statistics
sql
-- 磁盘组容量与冗余余量(ASM 实例执行)
SELECT name, type, state, total_mb, free_mb,
required_mirror_free_mb, usable_file_mb, offline_disks
FROM v$asm_diskgroup_stat
ORDER BY name;
-- 磁盘挂载/成员状态,检查是否有 MISSING/FORCING/OFFLINE 盘
SELECT group_number, disk_number, name, path,
mount_status, header_status, mode_status, state
FROM v$asm_disk
ORDER BY group_number, disk_number;
三、RAC 实例层(任一实例执行)
sql
-- 1. 各实例状态、角色、启动时间
SELECT inst_id, instance_number, instance_name, host_name, version,
status, database_status, active_state, startup_time, thread#
FROM gv$instance
ORDER BY inst_id;
-- 2. 集群私网互连地址
SELECT inst_id, name, ip_address, is_public, source
FROM gv$cluster_interconnects
ORDER BY inst_id;
-- 3. 服务在节点间的分布(防止单节点承载全部连接)
SELECT inst_id, name, network_name, goal, clb_goal
FROM gv$active_services
ORDER BY inst_id, name;
-- 4. 在线日志与线程对应关系(RAC 每个实例一个线程)
SELECT group#, thread#, sequence#, bytes, status, archived
FROM v$log
ORDER BY thread#, group#;
-- 5. 会话与等待概况(按实例汇总)
SELECT inst_id, COUNT(*) AS session_cnt,
SUM(CASE WHEN status = 'ACTIVE' THEN 1 ELSE 0 END) AS active_cnt
FROM gv$session
GROUP BY inst_id
ORDER BY inst_id;
四、ADG 同步层(核心,必须在备库执行)
1. 数据库角色与保护模式
sql
SELECT db_unique_name, database_role, protection_mode, open_mode,
switchover_status, force_logging, flashback_on, current_scn
FROM v$database;
期望:备库
DATABASE_ROLE = PHYSICAL STANDBY、OPEN_MODE = READ ONLY WITH APPLY;PROTECTION_MODE与规划一致。
2. 传输延迟与应用延迟
sql
SELECT name, value, unit, time_computed, datum_time
FROM v$dataguard_stats
WHERE LOWER(name) IN ('apply lag', 'transport lag')
ORDER BY name;
注意:在主库执行该视图返回空集,不能据此判断同步正常 。
value是字符串,必须连同unit一起读;连续两次采样datum_time不前进,说明没有收到新日志,此时延迟显示很小也不能视为正常。
3. 归档目标状态(11gR2 及以上)
sql
SELECT dest_id, status, type, database_mode, recovery_mode, destination,
error, gap_status, archived_thread#, archived_seq#,
applied_thread#, applied_seq#
FROM v$archive_dest_status
WHERE status <> 'INACTIVE'
ORDER BY dest_id;
期望:
status = VALID、error为空、gap_status = VALID。
4. 归档日志缺口
sql
SELECT thread#, low_sequence#, high_sequence#
FROM v$archive_gap
ORDER BY thread#;
返回空表示当前没有阻断恢复的缺口;但空结果不等于全程没有丢过日志。
5. Data Guard 进程(RAC 备库按实例看)
sql
SELECT inst_id, name, pid, type, role, action, client_role, client_pid,
thread#, sequence#, block#, block_count, delay_mins, dest_id
FROM gv$managed_standby
ORDER BY inst_id, name;
关键看
MRP0是否为APPLYING_LOG。RAC 备库若启用多实例 Redo Apply,MRP 会分布在多个实例;单实例模式则只在一个实例上有 MRP0。
6. 备库恢复/应用进度
sql
SELECT start_time, type, item, units, sofar, total, timestamp
FROM v$recovery_progress
WHERE type = 'MEDIA RECOVERY'
ORDER BY start_time DESC, item;
非协调实例在 Multi-Instance Redo Apply 下可能无行;
sofar/total含义随item变化,不要统一换算百分比。
7. 备用日志组(Standby Redo Log)
sql
SELECT group#, thread#, sequence#, bytes, used, archived, status
FROM v$standby_log
ORDER BY thread#, group#;
容量与在线日志一致,且每个线程的 SRL 组数 ≥ 该线程在线日志组数 + 1 ;
status不应长期为UNASSIGNED。
8. 同步位点对比(主备分别执行)
sql
-- 主库:各线程当前最大序列
SELECT thread#, MAX(sequence#) AS last_seq
FROM v$log
GROUP BY thread#
ORDER BY thread#;
-- 备库:各线程已应用到的最大序列
SELECT thread#, MAX(sequence#) AS applied_seq
FROM v$archived_log
WHERE applied = 'YES'
GROUP BY thread#
ORDER BY thread#;
两侧差值持续增大,说明应用跟不上或传输中断。
9. 最近 24 小时 Data Guard 告警
sql
SELECT *
FROM (
SELECT timestamp, facility, severity, dest_id, message_num, error_code, message
FROM v$dataguard_status
WHERE severity IN ('Warning', 'Error', 'Fatal')
AND timestamp >= SYSDATE - 1
ORDER BY timestamp DESC, message_num DESC
) WHERE ROWNUM <= 100;
10. Broker 配置巡检(若使用 Broker 管理)
bash
dgmgrl sys/<密码>@<主库TNS> "show configuration verbose"
dgmgrl sys/<密码>@<主库TNS> "show database verbose <db_unique_name>"
dgmgrl sys/<密码>@<主库TNS> "validate database <db_unique_name>"
重点看
Configuration Status、各库Status、Transport Lag、Apply Lag、Protection Mode,以及validate是否报错。
五、主机与日志层
bash
# 资源使用
top -b -n1 | head -20
vmstat 1 5
iostat -x 1 5
free -g
df -h
# 进程检查(备库看 MRP,主库看 LNS/ARCn)
ps -ef | grep -iE "mrp|lns|arc"
# 告警日志
adrci -exec "show alert -tail 200"
六、结果判定要点
| 检查项 | 正常判定 |
|---|---|
| 集群资源 | 所有资源 ONLINE,无 UNKNOWN/INTERMEDIATE |
| ASM | state = MOUNTED,offline_disks = 0,usable_file_mb 有合理余量 |
| 实例 | 所有节点 status = OPEN,database_status = ACTIVE |
| 备库角色 | PHYSICAL STANDBY,READ ONLY WITH APPLY |
| 传输/应用延迟 | 接近 0,且 datum_time 持续推进 |
| 归档目标 | VALID、无 error、gap_status = VALID |
| 日志缺口 | v$archive_gap 为空 |
| MRP 进程 | MRP0 处于 APPLYING_LOG |
| SRL | 数量满足"每线程 ≥ 在线组数 + 1",无长期 UNASSIGNED |
下面是一份可直接保存执行的 Oracle RAC + ADG 一键巡检脚本 (Bash + SQL*Plus)。脚本只读,不改动任何配置;默认以 root 运行时可自动切换到 grid / oracle 用户执行对应命令,也可分别以对应用户运行。
使用方式
bash
chmod +x rac_adg_inspect.sh
./rac_adg_inspect.sh -d <db_unique_name> [-s <ORACLE_SID>] [-o <输出目录>]
# 主库、备库各跑一次;Broker 巡检需额外提供连接串:
DG_BROKER_CONNECT='sys/<密码>@<主库TNS>' ./rac_adg_inspect.sh -d <db_unique_name>
脚本内容
bash
#!/usr/bin/env bash
# =============================================================================
# rac_adg_inspect.sh --- Oracle RAC + ADG 一键巡检(只读)
# 适用:Oracle 11gR2 / 12c / 18c / 19c / 21c+,RAC 主库与 RAC 备库
# 用法:./rac_adg_inspect.sh -d <db_unique_name> [-s <ORACLE_SID>] [-o <输出目录>]
# 建议:以 root 执行,脚本自动以 grid 执行集群命令、以 oracle 执行 SQL
# =============================================================================
set -uo pipefail
DB_UNIQUE_NAME=""
ORACLE_SID="${ORACLE_SID:-}"
OUT_DIR=""
GRID_USER="grid"
DB_USER="oracle"
usage() {
cat <<EOF
用法: $0 -d <db_unique_name> [-s <ORACLE_SID>] [-o <输出目录>]
-d DB_UNIQUE_NAME,必填,如 ORCL
-s ORACLE_SID,可选,未指定时自动探测
-o 输出目录,可选,默认 \$HOME/rac_adg_inspect_<时间戳>
-h 显示帮助
可选环境变量:
DG_BROKER_CONNECT Data Guard Broker 连接串,如 'sys/pass@ORCL_DG'
EOF
exit 0
}
while getopts "d:s:o:h" opt; do
case "$opt" in
d) DB_UNIQUE_NAME="$OPTARG" ;;
s) ORACLE_SID="$OPTARG" ;;
o) OUT_DIR="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
[ -z "$DB_UNIQUE_NAME" ] && { echo "错误:必须指定 -d <db_unique_name>"; usage; }
TS="$(date +%Y%m%d_%H%M%S)"
OUT_DIR="${OUT_DIR:-$HOME/rac_adg_inspect_${TS}}"
mkdir -p "$OUT_DIR/sql" || exit 1
LOG="$OUT_DIR/inspect.log"
: > "$LOG"
log() { echo "$*" | tee -a "$LOG"; }
sec() { log ""; log "==================== $* ===================="; }
is_root() { [ "$(id -u)" -eq 0 ]; }
have_user() { id "$1" >/dev/null 2>&1; }
# 以 grid 用户执行任意 shell 命令
run_grid() {
local cmd="$1"
if is_root && have_user "$GRID_USER"; then
su - "$GRID_USER" -c "$cmd"
else
bash -lc "$cmd"
fi
}
# 以 oracle 用户执行任意 shell 命令
run_oracle_cmd() {
local cmd="$1"
if is_root && have_user "$DB_USER"; then
su - "$DB_USER" -c "$cmd"
else
bash -lc "$cmd"
fi
}
# 以 oracle 用户执行 SQL*Plus 脚本
run_db() {
run_oracle_cmd "export ORACLE_SID=${ORACLE_SID}; sqlplus -S -L / as sysdba @$1"
}
# ---------- 自动探测 ORACLE_SID ----------
detect_sid() {
local s=""
s=$(run_grid "srvctl config database -d ${DB_UNIQUE_NAME}" 2>/dev/null \
| awk -F: '/[Dd]atabase instance/{gsub(/[[:space:]]/,"",$2); split($2,a,","); print a[1]; exit}')
if [ -z "$s" ]; then
s=$(ps -ef | grep -E "ora_pmon_" | grep -v grep | awk '{print $NF}' \
| sed 's/ora_pmon_//' | grep -v '^+' | head -1)
fi
echo "$s"
}
if [ -z "$ORACLE_SID" ]; then
ORACLE_SID="$(detect_sid)"
fi
if [ -z "$ORACLE_SID" ]; then
echo "错误:未能自动探测 ORACLE_SID,请使用 -s 指定。"
exit 1
fi
log "巡检开始时间 : $(date '+%Y-%m-%d %H:%M:%S')"
log "DB_UNIQUE_NAME: ${DB_UNIQUE_NAME}"
log "ORACLE_SID : ${ORACLE_SID}"
log "输出目录 : ${OUT_DIR}"
log "执行用户 : $(id -un)"
# =============================================================================
# 1. 集群层(Grid)
# =============================================================================
sec "1. 集群层 (Grid Infrastructure)"
log "--- crsctl check crs"
run_grid "crsctl check crs" 2>&1 | tee -a "$LOG"
log "--- crsctl stat res -t"
run_grid "crsctl stat res -t" 2>&1 | tee -a "$LOG"
log "--- crsctl stat res -t -init"
run_grid "crsctl stat res -t -init" 2>&1 | tee -a "$LOG"
log "--- olsnodes -n -s -t"
run_grid "olsnodes -n -s -t" 2>&1 | tee -a "$LOG"
log "--- oifcfg getif"
run_grid "oifcfg getif" 2>&1 | tee -a "$LOG"
log "--- crsctl query css votedisk"
run_grid "crsctl query css votedisk" 2>&1 | tee -a "$LOG"
log "--- ocrcheck"
run_grid "ocrcheck" 2>&1 | tee -a "$LOG"
log "--- srvctl status database"
run_grid "srvctl status database -d ${DB_UNIQUE_NAME} -v" 2>&1 | tee -a "$LOG"
log "--- srvctl status service"
run_grid "srvctl status service -d ${DB_UNIQUE_NAME}" 2>&1 | tee -a "$LOG"
log "--- srvctl config database"
run_grid "srvctl config database -d ${DB_UNIQUE_NAME}" 2>&1 | tee -a "$LOG"
log "--- srvctl status asm"
run_grid "srvctl status asm -n $(hostname -s)" 2>&1 | tee -a "$LOG"
log "--- srvctl status listener / lsnrctl status"
run_grid "srvctl status listener" 2>&1 | tee -a "$LOG"
run_grid "lsnrctl status" 2>&1 | tee -a "$LOG"
log "--- asmcmd lsdg"
run_grid "asmcmd lsdg" 2>&1 | tee -a "$LOG"
log "--- asmcmd lsdsk --statistics"
run_grid "asmcmd lsdsk --statistics" 2>&1 | tee -a "$LOG"
# =============================================================================
# 2. 数据库 + ADG 巡检 SQL
# =============================================================================
sec "2. 数据库实例与 ADG 同步状态"
cat > "$OUT_DIR/sql/db.sql" <<'SQL'
SET PAGESIZE 300 LINESIZE 320 FEEDBACK OFF TRIMSPOOL ON
SET UNDERLINE =
ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS';
PROMPT ===== 实例状态 =====
SELECT inst_id, instance_number, instance_name, host_name, version,
status, database_status, startup_time, thread#
FROM gv$instance
ORDER BY inst_id;
PROMPT ===== 集群互连 =====
SELECT inst_id, name, ip_address, is_public, source
FROM gv$cluster_interconnects
ORDER BY inst_id;
PROMPT ===== 服务分布 =====
SELECT inst_id, name, network_name, goal, clb_goal
FROM gv$active_services
ORDER BY inst_id, name;
PROMPT ===== 在线日志 =====
SELECT thread#, group#, sequence#, bytes, status, archived
FROM v$log
ORDER BY thread#, group#;
PROMPT ===== 会话概况 =====
SELECT inst_id, COUNT(*) AS session_cnt,
SUM(CASE WHEN status='ACTIVE' THEN 1 ELSE 0 END) AS active_cnt
FROM gv$session
GROUP BY inst_id
ORDER BY inst_id;
PROMPT ===== 数据库角色与保护模式 =====
SELECT db_unique_name, database_role, protection_mode, open_mode,
switchover_status, force_logging, flashback_on, current_scn
FROM v$database;
PROMPT ===== 传输/应用延迟(仅备库有数据) =====
SELECT name, value, unit, time_computed, datum_time
FROM v$dataguard_stats
WHERE LOWER(name) IN ('apply lag','transport lag')
ORDER BY name;
PROMPT ===== 归档目标状态 =====
SELECT dest_id, status, type, database_mode, recovery_mode, destination,
error, gap_status, archived_thread#, archived_seq#,
applied_thread#, applied_seq#
FROM v$archive_dest_status
WHERE status <> 'INACTIVE'
ORDER BY dest_id;
PROMPT ===== 归档缺口 =====
SELECT thread#, low_sequence#, high_sequence#
FROM v$archive_gap
ORDER BY thread#;
PROMPT ===== Data Guard 进程(RAC 按实例) =====
SELECT inst_id, process, pid, status, client_process, client_pid,
thread#, sequence#, block#, delay_mins
FROM gv$managed_standby
ORDER BY inst_id, process;
PROMPT ===== 备库恢复/应用进度 =====
SELECT start_time, type, item, units, sofar, total, timestamp
FROM v$recovery_progress
WHERE type = 'MEDIA RECOVERY'
ORDER BY start_time DESC, item;
PROMPT ===== 备用日志组 =====
SELECT group#, thread#, sequence#, bytes, used, archived, status
FROM v$standby_log
ORDER BY thread#, group#;
PROMPT ===== 备库已应用序列 =====
SELECT thread#, MAX(sequence#) AS applied_seq
FROM v$archived_log
WHERE applied = 'YES'
GROUP BY thread#
ORDER BY thread#;
PROMPT ===== 主库当前日志序列 =====
SELECT thread#, MAX(sequence#) AS last_seq
FROM v$log
GROUP BY thread#
ORDER BY thread#;
PROMPT ===== 最近24小时 DG 告警 =====
SELECT *
FROM (
SELECT timestamp, facility, severity, dest_id, message_num, error_code, message
FROM v$dataguard_status
WHERE severity IN ('Warning','Error','Fatal')
AND timestamp >= SYSDATE - 1
ORDER BY timestamp DESC, message_num DESC
) WHERE ROWNUM <= 100;
PROMPT ===== ASM 磁盘组 =====
SELECT name, type, state, total_mb, free_mb,
required_mirror_free_mb, usable_file_mb, offline_disks
FROM v$asm_diskgroup_stat
ORDER BY name;
PROMPT ===== ASM 磁盘状态 =====
SELECT group_number, disk_number, name, path,
mount_status, header_status, mode_status, state
FROM v$asm_disk
ORDER BY group_number, disk_number;
EXIT
SQL
run_db "$OUT_DIR/sql/db.sql" 2>&1 | tee -a "$LOG"
# =============================================================================
# 3. 巡检结论自动判定
# =============================================================================
sec "3. 巡检结论汇总"
cat > "$OUT_DIR/sql/check.sql" <<'SQL'
SET HEADING OFF FEEDBACK OFF PAGESIZE 0 LINESIZE 300 TRIMSPOOL ON
SELECT 'CHK_ROLE='||DATABASE_ROLE FROM V$DATABASE;
SELECT 'CHK_PROTECTION='||PROTECTION_MODE FROM V$DATABASE;
SELECT 'CHK_OPEN='||OPEN_MODE FROM V$DATABASE;
SELECT 'CHK_FORCE_LOGGING='||FORCE_LOGGING FROM V$DATABASE;
SELECT 'CHK_ASM_OFFLINE='||NVL(SUM(offline_disks),0) FROM V$ASM_DISKGROUP_STAT;
SELECT 'CHK_ARCHIVE_GAP='||COUNT(*) FROM V$ARCHIVE_GAP;
SELECT 'CHK_MRP='||NVL(MAX(CASE WHEN PROCESS='MRP0' THEN STATUS END),'NONE')
FROM GV$MANAGED_STANDBY;
SELECT 'CHK_APPLY_LAG='||NVL(MAX(CASE WHEN LOWER(NAME)='apply lag' THEN VALUE END),'NA')
FROM V$DATAGUARD_STATS;
SELECT 'CHK_TRANSPORT_LAG='||NVL(MAX(CASE WHEN LOWER(NAME)='transport lag' THEN VALUE END),'NA')
FROM V$DATAGUARD_STATS;
SELECT 'CHK_INSTANCE_CNT='||COUNT(*) FROM GV$INSTANCE;
EXIT
SQL
CHKOUT="$OUT_DIR/check.out"
run_db "$OUT_DIR/sql/check.sql" > "$CHKOUT" 2>&1
get_chk() { grep -m1 "^CHK_$1=" "$CHKOUT" 2>/dev/null | cut -d= -f2-; }
RC=0
ok() { log " [PASS] $*"; }
warn() { log " [WARN] $*"; [ "$RC" -lt 1 ] && RC=1; }
bad() { log " [FAIL] $*"; RC=2; }
ROLE="$(get_chk ROLE)"
PROT="$(get_chk PROTECTION)"
OPEN="$(get_chk OPEN)"
FORCELOG="$(get_chk FORCE_LOGGING)"
ASMOFF="$(get_chk ASM_OFFLINE)"
GAP="$(get_chk ARCHIVE_GAP)"
MRP="$(get_chk MRP)"
ALAG="$(get_chk APPLY_LAG)"
TLAG="$(get_chk TRANSPORT_LAG)"
ICNT="$(get_chk INSTANCE_CNT)"
log " 识别角色: ${ROLE:-未知} / ${OPEN:-未知} / ${PROT:-未知}"
case "$ROLE" in
PRIMARY) log " [INFO] 当前为主库,跳过备库专用判定";;
*STANDBY*) log " [INFO] 当前为备库,执行同步层判定";;
*) warn "未能识别数据库角色";;
esac
[ "$FORCELOG" = "YES" ] && ok "FORCE_LOGGING = YES" || warn "FORCE_LOGGING = ${FORCELOG},建议开启"
if [ -n "$ASMOFF" ] && [ "$ASMOFF" != "0" ]; then
bad "ASM 存在离线磁盘: offline_disks=${ASMOFF}"
else
ok "ASM 无离线磁盘"
fi
case "$ROLE" in
*STANDBY*)
if [ "${GAP:-1}" = "0" ]; then ok "无归档缺口"; else bad "存在归档缺口: ${GAP} 条"; fi
case "$MRP" in
APPLYING_LOG) ok "MRP0 状态 APPLYING_LOG";;
WAIT_FOR_LOG|IDLE) warn "MRP0 状态 ${MRP},需确认是否有新日志";;
WAIT_FOR_GAP) bad "MRP0 状态 WAIT_FOR_GAP,应用被缺口阻断";;
NONE) bad "未发现 MRP0 进程,备库可能未应用日志";;
*) warn "MRP0 状态异常: ${MRP}";;
esac
case "$ALAG" in
NA|"") warn "未能读取 apply lag";;
*00:00:00*) ok "apply lag = ${ALAG}";;
*) warn "apply lag = ${ALAG},请持续观察";;
esac
case "$TLAG" in
NA|"") warn "未能读取 transport lag";;
*00:00:00*) ok "transport lag = ${TLAG}";;
*) warn "transport lag = ${TLAG},请持续观察";;
esac
;;
esac
[ -n "$ICNT" ] && [ "$ICNT" -ge 1 ] 2>/dev/null && ok "可见实例数 = ${ICNT}" || warn "实例数异常"
# =============================================================================
# 4. Data Guard Broker(可选)
# =============================================================================
if [ -n "${DG_BROKER_CONNECT:-}" ]; then
sec "4. Data Guard Broker"
log "--- show configuration verbose"
run_oracle_cmd "dgmgrl -silent '${DG_BROKER_CONNECT}' 'show configuration verbose'" 2>&1 | tee -a "$LOG"
log "--- show database verbose"
run_oracle_cmd "dgmgrl -silent '${DG_BROKER_CONNECT}' 'show database verbose ${DB_UNIQUE_NAME}'" 2>&1 | tee -a "$LOG"
log "--- validate database"
run_oracle_cmd "dgmgrl -silent '${DG_BROKER_CONNECT}' 'validate database ${DB_UNIQUE_NAME}'" 2>&1 | tee -a "$LOG"
else
log ""
log "[INFO] 未设置 DG_BROKER_CONNECT,跳过 Broker 巡检"
fi
# =============================================================================
# 5. 主机资源与告警日志
# =============================================================================
sec "5. 主机资源与日志"
log "--- top"
top -b -n1 2>/dev/null | head -20 | tee -a "$LOG"
log "--- vmstat"
vmstat 1 5 2>/dev/null | tee -a "$LOG"
log "--- iostat -x"
if command -v iostat >/dev/null 2>&1; then
iostat -x 1 3 2>/dev/null | tee -a "$LOG"
else
log "iostat 不可用"
fi
log "--- free -g"
free -g 2>/dev/null | tee -a "$LOG"
log "--- df -h"
df -h 2>/dev/null | tee -a "$LOG"
log "--- 关键进程"
ps -ef | grep -E "ora_(pmon|mrp|lns|arc|lgwr)|ora_asm" | grep -v grep | tee -a "$LOG"
log "--- alert log tail 100"
run_oracle_cmd "adrci -exec 'show alert -tail 100'" 2>&1 | tee -a "$LOG"
# =============================================================================
# 6. 结果
# =============================================================================
sec "6. 巡检结束"
case "$RC" in
0) log " 总体结论: 正常(PASS)";;
1) log " 总体结论: 存在告警项,建议人工复核(WARN)";;
2) log " 总体结论: 存在异常项,需立即处理(FAIL)";;
esac
log " 完整日志: ${LOG}"
log " 结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
exit "$RC"