写在前面
各位DBA朋友们好,我是Acdante。
0x01篇分享了Oracle备份脚本(expdp&RMAN)和表空间监控的脚本,反响不错,很多读者私信问了表空间监控的问题。说实话,表空间爆满是生产环境里最常见也最致命的问题之一——凌晨3点被电话叫起来,发现是表空间满了业务中断,这种酸爽谁经历谁知道。
【注意注意】分享脚本仅供参考,生产使用建议充分测试和验证,自动添加固然省事,但是也存在一定风险,建议操作慎重.
也可以只用监控脚本,只做查询,添加人工介入(当然,作自动添加,很多时候是因为开发或者其他人员,再看到表空间90%后,不看数据文件路径,直接添加,结果导致Oracle RAC环境下,直接把数据文件添加到某个节点本地$ORACLE_HOME内,这种文件错误,会导致跨实例访问数据失败,以及重启后节点异常无法访问数据文件等等问题,相信不少DBA也经历过,要做迁移,数据文件copy等,单机情况下,还有加到各种莫名路径的,默认HOME下的,所以添加数据文件,扩容表空间,看似简单的一个动作,却也存在不少“天坑”,多一分细心,多一分考虑,会给数据库的持续稳定运行,带来很大助益,也会省去后续很多故障隐患和未知风险 。
今天这篇文章,咱们就彻底解决这个问题。
文章内容比较硬核,我会从以下几个方面展开:
📊 表空间使用率的真实计算方式(不是简单的 dba_free_space
)📈 数据文件自增长上限的真实使用率检测 🤖 大于90%自动添加数据文件(不冲突、不踩坑) 📝 完整的日志记录机制 💾 文件系统空间 vs ASM磁盘组空间的智能判断 🔔 钉钉/邮件外发告警 📦 ASM磁盘组监控全解析 🔄 从11g到23ai/26ai ASM的变化与限制
温馨提示:本文约2.3万字,建议先收藏,配合咖啡慢慢看 ☕
一、表空间使用率:你以为的和实际的
1.1 常见误区
很多同学监控表空间,直接一个SQL就完事了:
SELECT a.tablespace_name,
ROUND(a.total_size, 2) AS total_mb,
ROUND(a.total_size - NVL(b.free_size, 0), 2) AS used_mb,
ROUND((a.total_size - NVL(b.free_size, 0)) a.total_size * 100, 2) AS pct_used
FROM (SELECT tablespace_name, SUM(bytes) 1024 1024 AS total_size
FROM dba_data_files GROUP BY tablespace_name) a,
(SELECT tablespace_name, SUM(bytes) 1024 1024 AS free_size
FROM dba_free_space GROUP BY tablespace_name) b
WHERE a.tablespace_name = b.tablespace_name(+)
ORDER BY pct_used DESC;
这个SQL看起来没毛病,但它计算的是当前已分配空间的使用率,而不是表空间的真实增长上限。
什么意思呢?
举个例子:
表空间 USERS
有一个数据文件,初始100MB,AUTOEXTEND ON
,MAXBYTES 32GB当前已经用了90MB 按上面的SQL:使用率 = 90/100 = 90% 但真实情况是:这个文件还可以自动增长到32GB,真实使用率 = 90/32768 = 0.27%
差距巨大,对吧?
1.2 真实使用率的计算
正确的做法是:计算数据文件自增长上限(MAXBYTES)的真实使用率。
也可以参看0x03文章的内容
表空间检查实战
acdante,公众号:AcdanteOracle数据库表空间与数据文件实战指南【Oracle数据库分享--0x03】
核心逻辑如下:
SELECT df.tablespace_name,
-- 当前已分配空间(MB)
ROUND(SUM(df.bytes) 1024 1024, 2) AS current_allocated_mb,
-- 实际已使用空间(MB)
ROUND((SUM(df.bytes) - NVL(fs.free_bytes, 0)) 1024 1024, 2) AS actual_used_mb,
-- 最大可扩展空间(MB):考虑AUTOEXTEND
ROUND(SUM(
CASE
WHEN df.autoextensible = 'YES' THEN df.maxbytes
ELSE df.bytes
END
) 1024 1024, 2) AS max_extendable_mb,
-- 真实使用率(基于最大可扩展空间)
ROUND(
(SUM(df.bytes) - NVL(fs.free_bytes, 0))
SUM(
CASE
WHEN df.autoextensible = 'YES' THEN df.maxbytes
ELSE df.bytes
END
) * 100, 2
) AS real_pct_used,
-- 空间不足预警(剩余可增长MB)
ROUND(
SUM(
CASE
WHEN df.autoextensible = 'YES' THEN df.maxbytes
ELSE df.bytes
END
) 1024 1024 - (SUM(df.bytes) - NVL(fs.free_bytes, 0)) 1024 1024, 2
) AS remaining_mb
FROM dba_data_files df,
(SELECT tablespace_name, SUM(bytes) AS free_bytes
FROM dba_free_space
GROUP BY tablespace_name) fs
WHERE df.tablespace_name = fs.tablespace_name(+)
GROUP BY df.tablespace_name, fs.free_bytes
ORDER BY real_pct_used DESC;
💡 关键点解析
-autoextensible = 'YES'
的数据文件,用maxbytes
作为上限
-autoextensible = 'NO'
的数据文件,当前大小就是上限
- 真实使用率 = 实际已使用 最大可扩展空间
1.3 关于MAXBYTES的坑
⚠️ 注意: Oracle有个经典的坑——数据文件的 MAXBYTES
默认值。
对于一个数据文件:
如果创建时指定了 AUTOEXTEND ON MAXSIZE 32G
,那MAXBYTES = 32G如果创建时只写了 AUTOEXTEND ON
(没指定MAXSIZE),默认最大是 数据文件所在块大小 × 2^22 - 1
对于8KB块大小的数据库:
默认MAXBYTES = 8192 × 4194304 = 32GB - 8KB(约32GB) 对于32KB块大小:可以到 128GB
这个知识点很重要,因为在监控脚本里,你需要正确理解 MAXBYTES
的含义。
二、核心Shell脚本:表空间自动检测与扩容
2.1 脚本设计思路
整个脚本需要实现以下功能:
2.2 完整脚本
下面是完整的生产级别脚本,我做了详细的注释,每个关键步骤都有说明。
#!/bin/bash
#=============================================================
# 脚本名称: oracle_tablespace_monitor.sh
# 脚本功能: Oracle表空间使用率自动检测与智能扩容
# 适用版本: Oracle 11g/12c/19c/21c/23ai
# 作者: Acdante
# 日期: 2025-09-15
# 说明:
# 1. 检测表空间真实使用率(基于数据文件自增长上限)
# 2. 使用率>90%自动添加数据文件
# 3. 自动判断文件系统/ASM磁盘组可用空间
# 4. 完整日志记录
# 5. 钉钉/邮件告警
#=============================================================
# ===================== 配置区域 =====================
# Oracle环境变量
export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
export ORACLE_SID=ORCL
export PATH=$ORACLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
# 数据库连接信息(建议使用操作系统认证)
DB_USER="/ as sysdba"
# 监控阈值(百分比)
ALERT_THRESHOLD=80 # 告警阈值: 80%
AUTOADD_THRESHOLD=90 # 自动扩容阈值: 90%
# 自动扩容参数
ADD_FILE_SIZE="10G" # 每次添加数据文件的大小
ADD_FILE_SIZE_KB=10485760 # 10G对应的KB值(10*1024*1024)
MAX_AUTOADD_COUNT=5 # 单个表空间最大自动添加文件数
MAX_FILE_SIZE="32G" # 单个数据文件最大尺寸
# 文件系统空间检查阈值(剩余空间不低于此值才允许扩容,单位GB)
FS_MIN_FREE_GB=50
# ASM磁盘组空间检查阈值(剩余空间不低于此值才允许扩容,单位GB)
ASM_MIN_FREE_GB=100
# 日志目录
LOG_DIR=/u01/scripts/log/tablespace_monitor
mkdir -p ${LOG_DIR}
# 日志文件
LOG_FILE=${LOG_DIR}/tablespace_monitor_$(date +%Y%m%d_%H%M%S).log
# 告警配置
# 钉钉机器人webhook地址(替换为你的实际地址)
DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN_HERE"
# 邮件配置
MAIL_TO="dba@company.com,ops@company.com"
MAIL_FROM="oracle_monitor@company.com"
MAIL_SMTP="smtp.company.com"
# ===================== 函数定义 =====================
# 日志记录函数
log() {
local level=$1
shift
local msg="$*"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo "[${timestamp}] [${level}] ${msg}" | tee -a ${LOG_FILE}
}
# 日志记录(不输出到终端,仅写文件)
logf() {
local level=$1
shift
local msg="$*"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo "[${timestamp}] [${level}] ${msg}" >> ${LOG_FILE}
}
# 钉钉告警函数
send_dingtalk() {
local title="$1"
local text="$2"
# 检查webhook是否配置
if [[ "${DINGTALK_WEBHOOK}" == *"YOUR_TOKEN_HERE"* ]]; then
log "WARN" "钉钉webhook未配置,跳过钉钉告警"
return 1
fi
# 构建钉钉消息体(markdown格式)
local json_data=$(cat <<EOF
{
"msgtype": "markdown",
"markdown": {
"title": "${title}",
"text": "${text}"
},
"at": {
"isAtAll": false
}
}
EOF
)
# 发送钉钉消息
local response=$(curl -s -H "Content-Type: application/json" \
-d "${json_data}" \
"${DINGTALK_WEBHOOK}" 2>&1)
local ret_code=$?
if [ ${ret_code} -eq 0 ]; then
log "INFO" "钉钉告警发送成功"
logf "DEBUG" "钉钉响应: ${response}"
else
log "ERROR" "钉钉告警发送失败, 返回码: ${ret_code}"
logf "ERROR" "钉钉错误信息: ${response}"
fi
}
# 邮件告警函数
send_mail() {
local subject="$1"
local body="$2"
# 检查mailx是否可用
if ! command -v mailx &>/dev/null; then
log "WARN" "mailx命令不可用,跳过邮件告警"
return 1
fi
echo "${body}" | mailx -s "${subject}" \
-S smtp="${MAIL_SMTP}" \
-S from="${MAIL_FROM}" \
${MAIL_TO}
local ret_code=$?
if [ ${ret_code} -eq 0 ]; then
log "INFO" "邮件告警发送成功, 收件人: ${MAIL_TO}"
else
log "ERROR" "邮件告警发送失败, 返回码: ${ret_code}"
fi
}
# 检查文件系统可用空间(单位GB)
check_filesystem_space() {
local file_path=$1
local dir_path=$(dirname "${file_path}")
# 获取目录所在文件系统的可用空间(GB)
local avail_gb=$(df -BG "${dir_path}" 2>/dev/null | tail -1 | awk '{print $4}' | sed 's/G//')
if [ -z "${avail_gb}" ]; then
log "ERROR" "无法获取文件系统空间: ${dir_path}"
echo "0"
return 1
fi
logf "INFO" "文件系统 ${dir_path} 可用空间: ${avail_gb}GB"
echo "${avail_gb}"
return 0
}
# 检查ASM磁盘组可用空间(单位GB)
check_asm_diskgroup_space() {
local diskgroup_name=$1
# 通过sqlplus查询ASM磁盘组可用空间
local sql_result=$(sqlplus -S "${DB_USER}" <<EOF
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET LINESIZE 200
SET TRIMOUT ON
SET TRIMSPOOL ON
SELECT ROUND(free_mb/1024, 2)
FROM v\\$asm_diskgroup
WHERE name = UPPER('${diskgroup_name}');
EXIT;
EOF
)
local avail_gb=$(echo "${sql_result}" | tr -d '[:space:]')
if [ -z "${avail_gb}" ] || [ "${avail_gb}" = "0" ]; then
log "ERROR" "无法获取ASM磁盘组空间: ${diskgroup_name} 或空间为0"
echo "0"
return 1
fi
logf "INFO" "ASM磁盘组 ${diskgroup_name} 可用空间: ${avail_gb}GB"
echo "${avail_gb}"
return 0
}
# 判断数据文件路径类型(文件系统 or ASM)
get_storage_type() {
local file_path=$1
if [[ "${file_path}" == +* ]]; then
echo "ASM"
else
echo "FS"
fi
}
# 从ASM路径中提取磁盘组名称
get_asm_diskgroup() {
local file_path=$1
# ASM路径格式: +DISKGROUP_NAME/...
echo "${file_path}" | sed 's/^+\([^/]*\).*/\1/'
}
# 检查数据文件名是否已存在
check_file_exists() {
local file_name=$1
local sql_result=$(sqlplus -S "${DB_USER}" <<EOF
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SELECT COUNT(*) FROM dba_data_files WHERE file_name = '${file_name}';
EXIT;
EOF
)
local count=$(echo "${sql_result}" | tr -d '[:space:]')
echo "${count}"
}
# 生成不冲突的数据文件名
generate_file_name() {
local tablespace_name=$1
local storage_type=$2
local file_path=$3
local file_name=""
local counter=1
if [ "${storage_type}" = "ASM" ]; then
# ASM路径格式: +DISKGROUP_NAME/ORCL/DATAFILE/ts_name_nn.dbf
local dg_name=$(get_asm_diskgroup "${file_path}")
local base_path="+${dg_name}/${ORACLE_SID}/DATAFILE"
while true; do
file_name="${base_path}/${tablespace_name,,}_$(printf '%02d' ${counter}).dbf"
local exists=$(check_file_exists "${file_name}")
if [ "${exists}" = "0" ]; then
break
fi
counter=$((counter + 1))
if [ ${counter} -gt 99 ]; then
log "ERROR" "数据文件编号超过99,无法生成文件名"
echo ""
return 1
fi
done
else
# 文件系统路径: 取现有数据文件的目录
local dir_path=$(dirname "${file_path}")
while true; do
file_name="${dir_path}/${tablespace_name,,}_$(printf '%02d' ${counter}).dbf"
local exists=$(check_file_exists "${file_name}")
if [ "${exists}" = "0" ]; then
break
fi
counter=$((counter + 1))
if [ ${counter} -gt 99 ]; then
log "ERROR" "数据文件编号超过99,无法生成文件名"
echo ""
return 1
fi
done
fi
echo "${file_name}"
}
# 执行添加数据文件操作
add_datafile() {
local tablespace_name=$1
local file_name=$2
local file_size=$3
log "INFO" "========================================="
log "INFO" "开始添加数据文件"
log "INFO" "表空间: ${tablespace_name}"
log "INFO" "文件名: ${file_name}"
log "INFO" "文件大小: ${file_size}"
log "INFO" "========================================="
# 构建ALTER TABLESPACE语句
local sql_stmt="ALTER TABLESPACE ${tablespace_name} ADD DATAFILE '${file_name}' SIZE ${file_size} AUTOEXTEND ON NEXT 1G MAXSIZE ${MAX_FILE_SIZE};"
logf "INFO" "执行SQL: ${sql_stmt}"
# 记录SQL到日志
log "INFO" "执行SQL: ${sql_stmt}"
local sql_result=$(sqlplus -S "${DB_USER}" <<EOF
SET SERVEROUTPUT ON
SET LINESIZE 200
SET PAGESIZE 0
${sql_stmt}
-- 验证添加结果
SELECT 'VERIFY:' || file_name || '|' || bytes/1024/1024 || 'MB'
FROM dba_data_files
WHERE tablespace_name = UPPER('${tablespace_name}')
ORDER BY file_id DESC
FETCH FIRST 1 ROWS ONLY;
EXIT;
EOF
)
local ret_code=$?
# 记录SQL执行结果
logf "INFO" "SQL执行结果: ${sql_result}"
# 检查执行结果
if echo "${sql_result}" | grep -qi "ORA-"; then
local ora_error=$(echo "${sql_result}" | grep -oP 'ORA-\d+.*' | head -1)
log "ERROR" "添加数据文件失败: ${ora_error}"
logf "ERROR" "完整错误输出: ${sql_result}"
return 1
elif [ ${ret_code} -ne 0 ]; then
log "ERROR" "SQL*Plus执行失败, 返回码: ${ret_code}"
logf "ERROR" "完整输出: ${sql_result}"
return 1
else
log "INFO" "✅ 数据文件添加成功!"
logf "INFO" "验证结果: ${sql_result}"
return 0
fi
}
# ===================== 主逻辑 =====================
main() {
log "INFO" "============================================================"
log "INFO" " Oracle表空间监控自动扩容脚本启动"
log "INFO" " 执行时间: $(date '+%Y-%m-%d %H:%M:%S')"
log "INFO" " 数据库实例: ${ORACLE_SID}"
log "INFO" " 告警阈值: ${ALERT_THRESHOLD}%"
log "INFO" " 自动扩容阈值: ${AUTOADD_THRESHOLD}%"
log "INFO" " 每次扩容大小: ${ADD_FILE_SIZE}"
log "INFO" " 最大自动扩容次数: ${MAX_AUTOADD_COUNT}"
log "INFO" "============================================================"
# 检查数据库连接
local conn_test=$(sqlplus -S "${DB_USER}" <<EOF
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SELECT 'CONNECT_OK' FROM dual;
EXIT;
EOF
)
if ! echo "${conn_test}" | grep -q "CONNECT_OK"; then
log "ERROR" "无法连接数据库,请检查Oracle环境变量和连接信息"
logf "ERROR" "连接测试结果: ${conn_test}"
send_dingtalk "🔴 数据库连接失败" "## 数据库连接失败\n\n- 实例: ${ORACLE_SID}\n- 时间: $(date '+%Y-%m-%d %H:%M:%S')\n- 请立即检查!"
exit 1
fi
log "INFO" "数据库连接成功"
# ==========================================
# 第一步: 查询所有表空间的真实使用率
# ==========================================
log "INFO" "----------------------------------------"
log "INFO" "第一步: 查询表空间使用率"
log "INFO" "----------------------------------------"
# 创建临时文件存储查询结果
local tmp_file=$(mktemp tmp/ts_monitor_XXXXXX.tmp)
# 执行表空间使用率查询
sqlplus -S "${DB_USER}" <<EOF > ${tmp_file} 2>&1
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET LINESIZE 500
SET TRIMOUT ON
SET TRIMSPOOL ON
SET COLSEP '|'
-- 主查询: 表空间真实使用率
SELECT
df.tablespace_name,
ROUND(SUM(df.bytes)/1024/1024, 2),
ROUND((SUM(df.bytes) - NVL(fs.free_bytes, 0))/1024/1024, 2),
ROUND(SUM(CASE WHEN df.autoextensible = 'YES' THEN df.maxbytes ELSE df.bytes END)/1024/1024, 2),
ROUND((SUM(df.bytes) - NVL(fs.free_bytes, 0)) SUM(CASE WHEN df.autoextensible = 'YES' THEN df.maxbytes ELSE df.bytes END) * 100, 2),
df.file_name,
df.autoextensible,
ROUND(df.maxbytes/1024/1024, 2)
FROM dba_data_files df,
(SELECT tablespace_name, SUM(bytes) AS free_bytes
FROM dba_free_space GROUP BY tablespace_name) fs
WHERE df.tablespace_name = fs.tablespace_name(+)
GROUP BY df.tablespace_name, df.file_name, df.autoextensible, df.maxbytes, fs.free_bytes
ORDER BY 5 DESC;
EXIT;
EOF
log "INFO" "表空间查询完成, 结果已保存"
# 解析查询结果并处理
local alert_msg=""
local addfile_msg=""
local error_msg=""
while IFS='|' read -r ts_name current_mb used_mb max_mb pct_used file_path autoext maxbytes_mb; do
# 跳过空行和系统表空间(根据需要调整)
[ -z "${ts_name}" ] && continue
ts_name=$(echo "${ts_name}" | tr -d '[:space:]')
pct_used=$(echo "${pct_used}" | tr -d '[:space:]')
[ -z "${ts_name}" ] && continue
[ -z "${pct_used}" ] && continue
# 去除百分号(如果有)
pct_used=$(echo "${pct_used}" | sed 's/%//g')
logf "INFO" "表空间: ${ts_name}, 当前: ${current_mb}MB, 已用: ${used_mb}MB, 上限: ${max_mb}MB, 使用率: ${pct_used}%"
# 检查是否需要告警
if (( $(echo "${pct_used} >= ${ALERT_THRESHOLD}" | bc -l) )); then
alert_msg="${alert_msg}\n- **${ts_name}**: ${pct_used}% (已用${used_mb}MB 上限${max_mb}MB)"
log "WARN" "⚠️ 表空间 ${ts_name} 使用率 ${pct_used}% >= 告警阈值 ${ALERT_THRESHOLD}%"
fi
# 检查是否需要自动扩容
if (( $(echo "${pct_used} >= ${AUTOADD_THRESHOLD}" | bc -l) )); then
log "WARN" "🚨 表空间 ${ts_name} 使用率 ${pct_used}% >= 自动扩容阈值 ${AUTOADD_THRESHOLD}%"
# ------------------------------------------
# 第二步: 检查存储空间是否足够
# ------------------------------------------
local storage_type=$(get_storage_type "${file_path}")
local avail_space=0
local can_expand=false
log "INFO" "存储类型: ${storage_type}, 数据文件路径: ${file_path}"
if [ "${storage_type}" = "ASM" ]; then
local dg_name=$(get_asm_diskgroup "${file_path}")
avail_space=$(check_asm_diskgroup_space "${dg_name}")
if [ "${avail_space}" -gt "${ASM_MIN_FREE_GB}" ]; then
can_expand=true
log "INFO" "ASM磁盘组 ${dg_name} 可用空间 ${avail_space}GB > 最低要求 ${ASM_MIN_FREE_GB}GB, 允许扩容"
else
log "ERROR" "❌ ASM磁盘组 ${dg_name} 可用空间 ${avail_space}GB <= 最低要求 ${ASM_MIN_FREE_GB}GB, 无法扩容!"
error_msg="${error_msg}\n- **${ts_name}**: ASM磁盘组 ${dg_name} 空间不足 (${avail_space}GB)"
fi
else
avail_space=$(check_filesystem_space "${file_path}")
if [ "${avail_space}" -gt "${FS_MIN_FREE_GB}" ]; then
can_expand=true
log "INFO" "文件系统可用空间 ${avail_space}GB > 最低要求 ${FS_MIN_FREE_GB}GB, 允许扩容"
else
log "ERROR" "❌ 文件系统可用空间 ${avail_space}GB <= 最低要求 ${FS_MIN_FREE_GB}GB, 无法扩容!"
error_msg="${error_msg}\n- **${ts_name}**: 文件系统空间不足 (${avail_space}GB)"
fi
fi
# ------------------------------------------
# 第三步: 执行自动扩容
# ------------------------------------------
if [ "${can_expand}" = true ]; then
# 生成新的数据文件名
local new_file=$(generate_file_name "${ts_name}" "${storage_type}" "${file_path}")
if [ -z "${new_file}" ]; then
log "ERROR" "无法生成数据文件名,跳过表空间 ${ts_name} 的扩容"
error_msg="${error_msg}\n- **${ts_name}**: 无法生成数据文件名"
continue
fi
log "INFO" "生成新数据文件名: ${new_file}"
# 执行添加数据文件
local add_result=$(add_datafile "${ts_name}" "${new_file}" "${ADD_FILE_SIZE}")
local add_ret=$?
if [ ${add_ret} -eq 0 ]; then
addfile_msg="${addfile_msg}\n- **${ts_name}**: 已添加 ${new_file} (${ADD_FILE_SIZE})"
log "INFO" "✅ 表空间 ${ts_name} 自动扩容成功"
else
error_msg="${error_msg}\n- **${ts_name}**: 自动扩容失败"
log "ERROR" "❌ 表空间 ${ts_name} 自动扩容失败"
fi
fi
fi
done < ${tmp_file}
# 清理临时文件
rm -f ${tmp_file}
# ==========================================
# 第四步: 发送告警通知
# ==========================================
log "INFO" "----------------------------------------"
log "INFO" "第四步: 发送告警通知"
log "INFO" "----------------------------------------"
if [ -n "${alert_msg}" ] || [ -n "${addfile_msg}" ] || [ -n "${error_msg}" ]; then
local dingtalk_content="## 🔔 Oracle表空间监控报告\n\n"
dingtalk_content+="**数据库**: ${ORACLE_SID}\n"
dingtalk_content+="**检查时间**: $(date '+%Y-%m-%d %H:%M:%S')\n\n"
if [ -n "${alert_msg}" ]; then
dingtalk_content+="### ⚠️ 使用率告警(>=${ALERT_THRESHOLD}%)\n${alert_msg}\n\n"
fi
if [ -n "${addfile_msg}" ]; then
dingtalk_content+="### ✅ 自动扩容成功\n${addfile_msg}\n\n"
fi
if [ -n "${error_msg}" ]; then
dingtalk_content+="### ❌ 异常信息\n${error_msg}\n\n"
fi
dingtalk_content+="---\n**详细日志**: ${LOG_FILE}"
# 发送钉钉告警
send_dingtalk "Oracle表空间监控报告" "${dingtalk_content}"
# 发送邮件告警
local mail_subject="[Oracle监控] 表空间告警 - ${ORACLE_SID} - $(date '+%Y%m%d')"
local mail_body=$(echo -e "${dingtalk_content}" | sed 's/## g' | sed 's/### g' | sed 's/\*\*//g')
send_mail "${mail_subject}" "${mail_body}"
else
log "INFO" "✅ 所有表空间使用率正常,无需告警"
fi
# ==========================================
# 第五步: 生成汇总日志
# ==========================================
log "INFO" "============================================================"
log "INFO" " 表空间监控执行完成"
log "INFO" " 完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
log "INFO" " 日志文件: ${LOG_FILE}"
log "INFO" "============================================================"
# 清理30天以前的日志文件
find ${LOG_DIR} -name "tablespace_monitor_*.log" -mtime +30 -delete
log "INFO" "已清理30天前的历史日志"
}
# ===================== 执行入口 =====================
# 记录脚本开始时间
START_TIME=$(date +%s)
# 执行主逻辑,所有输出同时写入日志
main 2>&1 | tee -a ${LOG_FILE}
# 计算执行时长
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
log "INFO" "脚本总执行时长: ${ELAPSED}秒"
2.3 脚本关键点解析
看到这里,你可能觉得脚本很长。确实,但每一个函数都有明确的职责。
钉钉的话,需要打通钉钉API网址,也可以通过代理转发形式或者网闸等内外网隔离设备。
这种脚本方式,自然是最原始的,有条件,可以通过统一运维平台去告警外发,这里也推荐几个个人觉得非常不错的,比如夜莺/hertzbeat项目,老牌的普罗米修斯/Zabbix等也可以。
我来梳理几个关键点:
🔑 关键点1:表空间使用率的双重检查
脚本中做了两层判断:
这样设计是因为:
80%只是预警,可能不需要立即扩容 90%是危险线,必须自动干预
🔑 关键点2:存储类型智能判断
if [[ "${file_path}" == +* ]]; then
# ASM路径以+开头
storage_type="ASM"
else
# 文件系统路径
storage_type="FS"
fi
这是区分ASM和文件系统最简单可靠的方式——ASM路径一定以 +
开头。
🔑 关键点3:数据文件命名防冲突
# 循环检查文件名是否已存在
while true; do
file_name="${dir}/${ts_name,,}_$(printf '%02d' ${counter}).dbf"
exists=$(check_file_exists "${file_name}")
if [ "${exists}" = "0" ]; then
break
fi
counter=$((counter + 1))
done
先查 dba_data_files
确认文件名不冲突,再执行 ALTER TABLESPACE ADD DATAFILE
。先验后做,永不冲突。
🔑 关键点4:操作日志全记录
脚本记录了:
每次检查的所有表空间使用率 决策过程(为什么扩容/为什么不扩容) 执行的完整SQL语句 执行结果和验证 告警发送结果
日志格式示例:
[2026-04-22 03:00:01] [INFO] ============================================================
[2026-04-22 03:00:01] [INFO] Oracle表空间监控自动扩容脚本启动
[2026-04-22 03:00:01] [INFO] 执行时间: 2026-04-22 03:00:01
[2026-04-22 03:00:01] [INFO] 数据库实例: ORCL
[2026-04-22 03:00:02] [INFO] 数据库连接成功
[2026-04-22 03:00:03] [WARN] ⚠️ 表空间 USERS 使用率 92.35% >= 告警阈值 80%
[2026-04-22 03:00:03] [WARN] 🚨 表空间 USERS 使用率 92.35% >= 自动扩容阈值 90%
[2026-04-22 03:00:03] [INFO] 存储类型: FS, 数据文件路径: oradata/ORCL/users01.dbf
[2026-04-22 03:00:03] [INFO] 文件系统 oradata/ORCL 可用空间: 256GB > 最低要求 50GB, 允许扩容
[2026-04-22 03:00:04] [INFO] 生成新数据文件名: oradata/ORCL/users_02.dbf
[2026-04-22 03:00:04] [INFO] 执行SQL: ALTER TABLESPACE USERS ADD DATAFILE '/oradata/ORCL/users_02.dbf' SIZE 10G AUTOEXTEND ON NEXT 1G MAXSIZE 32G;
[2026-04-22 03:00:05] [INFO] ✅ 数据文件添加成功!
[2026-04-22 03:00:05] [INFO] ✅ 表空间 USERS 自动扩容成功
[2026-04-22 03:00:06] [INFO] 钉钉告警发送成功
三、定时任务配置
3.1 Crontab配置
将脚本配置为定时执行,建议每30分钟检测一次:
# 编辑crontab
crontab -e
# 添加以下内容(每30分钟执行一次)
*/30 * * * * u01/scripts/oracle_tablespace_monitor.sh >> u01/scripts/log/tablespace_monitor/cron.log 2>&1
3.2 执行频率建议
四、钉钉机器人配置详解
4.1 创建钉钉群机器人
打开钉钉群 → 群设置 → 智能群助手 → 添加机器人 选择"自定义"机器人 设置机器人名称(如:Oracle监控告警) 安全设置选择"自定义关键词",输入"Oracle" 复制Webhook地址,替换脚本中的 DINGTALK_WEBHOOK
4.2 告警效果展示
钉钉收到的告警消息效果如下:
🔔 Oracle表空间监控报告
数据库: ORCL
检查时间: 2026-04-22 03:00:01
⚠️ 使用率告警(>=80%)
- USERS: 92.35% (已用29552MB 上限32000MB)
- UNDO_TBS: 85.67% (已用27414MB 上限32000MB)
✅ 自动扩容成功
- USERS: 已添加 oradata/ORCL/users_02.dbf (10G)
---
详细日志: u01/scripts/log/tablespace_monitor/tablespace_monitor_20260422_030001.log
4.3 邮件告警备选方案
如果你的环境不方便用钉钉,邮件告警也是不错的选择。脚本中已集成邮件发送功能,配置好 MAIL_TO
、MAIL_FROM
、MAIL_SMTP
即可。
进阶玩法:可以使用Python的 smtplib
+ email
模块发送HTML格式的邮件,表格更美观。但考虑到脚本的可移植性(不一定有Python环境),这里用的是最基础的 mailx
。
五、ASM磁盘组监控SQL大全和基础知识
表空间使用率,也没办法绕开ASM磁盘组管理,生产RAC集群大多都使用ASM管理数据文件,添加扩容,也需要同步检测ASM磁盘组,除了通过grid用户的asmcmd管理查看,使用oracle用户的基本SQL也可以非常直观的去看到相关信息。从Oracle10g到目前的Oracle 26ai,ASM也发生了很大的变化。
5.1 ASM磁盘组基本监控
对于使用ASM存储的环境,磁盘组的监控同样重要。
查看所有磁盘组状态和空间
-- =============================================
-- ASM磁盘组空间使用率监控
-- 适用版本: 11g/12c/19c/23ai
-- =============================================
SELECT
name AS diskgroup_name,
state AS diskgroup_state,
type AS redundancy_type,
ROUND(total_mb/1024, 2) AS total_gb,
ROUND(free_mb/1024, 2) AS free_gb,
ROUND((total_mb - free_mb)/1024, 2) AS used_gb,
ROUND((total_mb - free_mb) total_mb * 100, 2) AS pct_used,
ROUND(usable_file_mb/1024, 2) AS usable_free_gb, -- 考虑冗余后的实际可用
required_mirror_free_mb/1024 AS required_mirror_free_gb -- 冗余所需空间
FROM v$asm_diskgroup
ORDER BY pct_used DESC;
💡 关键字段说明
-total_mb
: 磁盘组总容量
-free_mb
: 磁盘组剩余空间
-usable_file_mb
: 考虑镜像冗余后,实际可用的空间。这个才是你真正能写入数据的剩余空间!
- 对于NORMAL
冗余,usable_file_mb ≈ free_mb 2
- 对于HIGH
冗余,usable_file_mb ≈ free_mb 3
查看ASM磁盘详情
-- =============================================
-- ASM磁盘详细信息
-- =============================================
SELECT
dg.name AS diskgroup_name,
d.name AS disk_name,
d.path AS disk_path,
d.failgroup AS failure_group,
ROUND(d.total_mb/1024, 2) AS total_gb,
ROUND(d.free_mb/1024, 2) AS free_gb,
ROUND((d.total_mb - d.free_mb) d.total_mb * 100, 2) AS pct_used,
d.state AS disk_state,
d.mount_status,
d.header_status,
d.mode_status,
d.redundancy
FROM v$asm_disk d, v$asm_diskgroup dg
WHERE d.group_number = dg.group_number
ORDER BY dg.name, d.name;
查看ASM磁盘组的IO统计
-- =============================================
-- ASM磁盘组IO性能统计
-- =============================================
SELECT
dg.name AS diskgroup_name,
SUM(d.reads) AS total_reads,
SUM(d.writes) AS total_writes,
ROUND(SUM(d.read_time) * 1000 NULLIF(SUM(d.reads), 0), 2) AS avg_read_ms,
ROUND(SUM(d.write_time) * 1000 NULLIF(SUM(d.writes), 0), 2) AS avg_write_ms,
SUM(d.bytes_read) 1024/1024/1024 AS total_read_gb,
SUM(d.bytes_written) 1024/1024/1024 AS total_write_gb
FROM v$asm_disk_stat d, v$asm_diskgroup dg
WHERE d.group_number = dg.group_number
GROUP BY dg.name;
查看ASM磁盘组客户端连接
-- =============================================
-- ASM客户端连接信息(RAC环境下很有用)
-- =============================================
SELECT
dg.name AS diskgroup_name,
c.instance_name,
c.db_name,
c.status,
c.SOFTWARE_VERSION,
c.COMPATIBLE_VERSION
FROM v$asm_client c, v$asm_diskgroup dg
WHERE c.group_number = dg.group_number
ORDER BY dg.name, c.instance_name;
5.2 ASM磁盘组告警阈值建议
⚠️ 重要提示:监控ASM空间时,不要只看free_mb,要看 usable_file_mb! usable_file_mb
才是扣除冗余后真正能用的空间。如果usable_file_mb
变成负数,说明磁盘组已经无法保证冗余策略,新数据无法写入,这是紧急状态!
5.3 综合监控SQL(推荐)
-- =============================================
-- ASM磁盘组综合监控(推荐用于告警脚本)
-- 直接给出可用/不可用的判断
-- =============================================
SELECT
name AS diskgroup_name,
state,
type AS redundancy,
ROUND(total_mb/1024, 2) AS total_gb,
ROUND(free_mb/1024, 2) AS free_gb,
ROUND(usable_file_mb/1024, 2) AS usable_free_gb,
ROUND((1 - usable_file_mb NULLIF(total_mb, 0)) * 100, 2) AS effective_pct_used,
CASE
WHEN usable_file_mb < 0 THEN '🔴 CRITICAL - 无法保证冗余!'
WHEN ROUND(usable_file_mb/1024, 2) < 50 THEN '🟡 WARNING - 可用空间不足50GB'
WHEN ROUND((1 - usable_file_mb NULLIF(total_mb, 0)) * 100, 2) > 85 THEN '🟡 WARNING - 使用率超85%'
ELSE '🟢 NORMAL'
END AS alert_status
FROM v$asm_diskgroup
ORDER BY effective_pct_used DESC;
六、ASM磁盘组类型与冗余度详解
6.1 ASM冗余度类型
ASM提供三种冗余策略,创建磁盘组时选定,创建后不可更改:
External(外部冗余)
External冗余度,多用于追求存储空间最大化,且存储可用性完全依赖底层存储设备,如共享存储的RAID机制,底层存储双活复制,或者存储级别的冗余,Oralce数据库完全不关心数据冗余,所有数据从Oracle LGWR写入ASM磁盘组只会写入一份。- 适用场景
:底层存储已经做了RAID(如SAN、高端存储阵列) - 优点
:所有磁盘空间都可用于数据存储,性能最优 - 缺点
:完全依赖底层存储的可靠性 - 生产建议
:如果有企业级SAN存储,优先选择
Normal(常规冗余 双向镜像)
Normal方式用的比较多,如OCR磁盘;也是最省钱的存储双活方式,底层只需要2台存储+1台仲裁存储(NFS也可以),即可实现数据库存储的双活部署和冗余。数据写入双副本镜像模式。针对OCR的高可用和无底层双活存储直接复制的情况下,如何部署高可用架构,这个后续我再把我的生产环境部署方案分享一下。单独写一篇吧。很多数据库一体机,存储节点高可用也采用ASM冗余来做的。包括Oracle自己的Exadata一体机也是。
- 适用场景
:使用本地磁盘或普通存储,需要数据保护 - 优点
:单块磁盘故障不影响数据可用性 - 缺点
:可用空间减半 - 生产建议
:RAC环境的默认推荐选择
High(高冗余 三向镜像)
High是冗余度最高的,三副本模式,所有数据均衡分部在三个不同的FailGroup-故障组内。
- 适用场景
:金融核心、超高可用性要求 - 优点
:同时坏两块磁盘数据仍然安全 - 缺点
:可用空间只有1/3,成本高 - 生产建议
:关键业务系统,存储成本不是瓶颈时考虑
6.2 选择冗余度的决策树

可通过这个决策树判断,是否需要。最终还是要结合实际生产和现实条件去做选择。副本越多自然是更加安全可靠。
七、Oracle各版本ASM变化与限制
7.1 ASM版本演进概览
| 10g | ||
| 11g | ||
| 12c | ||
| 19c | ||
| 23ai |
7.2 各版本详细对比
Oracle 11g R2 (11.2.0.x)
ASM核心特性:
ASM成为Grid Infrastructure的核心组件 引入ASMCA(ASM Configuration Assistant)图形化管理 ACFS(ASM Cluster File System)文件系统 ADVM(ASM Dynamic Volume Manager)卷管理 ASM实例可以独立于数据库运行
磁盘大小限制:
单个ASM磁盘最大:2TB(外部冗余)/ 2TB(普通冗余) 单个磁盘组最大磁盘数:10000块 单个ASM实例最大管理磁盘组:63个 每个磁盘组最大磁盘数:10000块
文件大小限制(8KB块大小):
外部冗余:32TB(2^32 × 8KB) 普通冗余:16TB 高冗余:10.6TB
注意: 11g的单盘限制实际上受ASM层面和操作系统层面的双重限制。在32位系统上,单盘上限更小。
Oracle 12c R1/R2 (12.1.0.x 12.2.0.x)
新增特性:
- Flex ASM
:ASM实例不再需要在每个节点运行,通过代理模式访问远程ASM - ASM Filter Driver (AFD)
:取代ASMLib,提供更可靠的磁盘访问控制 - ASM File Groups
:支持在同一个磁盘组内按属性分组管理文件 - ASM Quota Groups
:磁盘组级别的空间配额管理
磁盘大小限制提升:
单个ASM磁盘最大:4PB(理论值,受存储阵列限制) 单个磁盘组最大容量:4EB(理论值) 实际测试中单盘可以支持64TB+的LUN
12c R2关键改进:
支持4KB扇区磁盘的原生IO ASM Rebalance性能提升(增量rebalance) 支持NVMe设备直连
Oracle 19c (19.3+)
新增/增强特性:
- ASM Metadata自动备份
:元数据定期自动备份到磁盘组 - ASM克隆
:支持磁盘组级别的克隆 - ASM Flex Redundancy
:更灵活的冗余策略(每个文件独立设置冗余级别) - Automatic Big Table Caching
集成ASM - 数据卫士增强
:DG中的ASM管理更加自动化
磁盘大小限制:
单个ASM磁盘最大:无硬编码限制(受存储和OS限制) 实际生产环境中支持单盘64TB 单个磁盘组最大磁盘数:10000块 每个节点最大磁盘组:63个 每个数据库实例最大数据文件:65533个
19c ASM关键变化:
支持4K原生扇区磁盘的完整IO ASM rebalance算法优化,支持并行rebalance - ASM Scrubbing
:磁盘数据一致性自动检查和修复 支持更细粒度的ASM权限管理
Oracle 21c (21.3+)
作为创新版本(非长期支持),21c引入了一些试验性特性:
- ASM与Kubernetes集成
:支持在K8s环境中的ASM管理 - ASM增强的云存储支持
:更好地支持公有云块存储 - 智能化磁盘管理
:基于使用模式的自动优化
Oracle 23ai (23.4+)
革命性变化:
- True Cache与ASM集成
:True Cache使用ASM管理缓存数据 - JSON Relational Duality
在ASM层面的优化存储 - AI向量索引
的存储优化 - Automatic Indexing增强
在ASM层面的存储管理
磁盘大小限制(23ai):
单个ASM磁盘:无限制(受存储阵列和OS限制) 实际测试支持单盘128TB+ 磁盘组总容量:理论无限制 文件数量限制:2^48个(巨大到几乎无限)
23ai RAC + ASM变化:
- 零停机ASM升级
:更平滑的ASM版本升级 - 智能故障组管理
:基于地理位置的故障组自动分配 - ASM与Oracle Database Vault集成增强
Oracle 26ai (预计2026)
预期变化:
- 全栈AI驱动运维
:ASM级别的AI预测性维护 - 存储智能分层
:热/温/冷数据自动在ASM磁盘间迁移 - 量子安全加密
:ASM元数据加密支持量子安全算法 - 超大规模支持
:单个磁盘组支持EB级容量 - 边缘计算集成
:ASM支持边缘节点的轻量化部署
26ai ASM预期限制(推测):
单盘:无限制 单磁盘组容量:256EB+ 单数据库数据文件:100万+ 节点数量:1024+节点
7.3 各版本限制汇总表
| 11g R2 | |||||
| 12c R1 | |||||
| 12c R2 | |||||
| 19c | |||||
| 21c | |||||
| 23ai | |||||
| 26ai |
⚠️ 重要说明:表中的"理论最大值"是Oracle软件层面的限制,实际限制取决于:
1. 操作系统对块设备大小的限制
2. 存储阵列的LUN大小限制
3. 文件系统的限制
4. 网络带宽(RAC环境)
7.4 RAC环境下ASM的关键变化
11g RAC + ASM

11g版本,所有节点必须Oralce 实例和ASM实例一对一,每个GRID下一个ASM实例对接各自节点的DB实例,1对1服务。
每个节点都运行独立的ASM实例 ASM实例之间通过心跳同步元数据 节点故障时,其他节点的ASM实例接管其磁盘组管理
12c RAC + Flex ASM

12c Flex开始,可以允许某些节点不创建ASM实例,而是通过ASM代理远程访问。
Flex ASM允许不是每个节点都运行ASM实例 通过ASM代理(Listener)访问远程ASM 减少了资源占用,提高了灵活性 - 默认Flex ASM基数为3
(即最多允许同时3个ASM实例故障)
19c/23ai RAC + ASM
Flex ASM成为默认配置 支持ASM Rolling Upgrade:逐节点升级ASM,无需全集群停机 - ASM Filter Driver (AFD)
更加成熟,取代ASMLib 支持Flex Cluster:Hub节点和Leaf节点分离架构 23ai引入True Cache:利用ASM管理的共享存储做分布式缓存
7.5 ASM最佳实践建议
1. 磁盘组规划
建议的磁盘组划分:
+DATA → 数据文件、控制文件、在线日志(Normal冗余)
+FRA → 归档日志、RMAN备份(Normal冗余)
+OCR → 集群注册表、表决盘(Normal/High冗余,可共享)
2. 故障组设置
每个故障组放在不同的物理磁盘/存储控制器下 RAC环境中,故障组最好跨越不同的存储机柜 故障组大小尽量一致
3. AU(Allocation Unit)大小选择
4. Rebalance调优
-- 调整rebalance的并行度和速度
ALTER DISKGROUP DATA REBALANCE POWER 8 WAIT;
-- POWER范围: 0-1024, 默认11
-- 越大越快,但对IO影响越大
-- 建议: OLTP环境用4-8, 维护窗口用16-32
八、表空间告警升级机制与多库批量监控
8.1 告警升级机制
在真实的生产环境中,仅仅发送一条告警是不够的。一个成熟的监控体系应该有告警升级机制:

可根据实际生产环境定义调整实现告警升级的关键是状态文件,记录每次告警的时间和级别:
# 告警状态文件
ALERT_STATE_FILE=${LOG_DIR}/alert_state.json
# 检查告警频率
check_alert_frequency() {
local ts_name=$1
local alert_level=$2
if [ -f "${ALERT_STATE_FILE}" ]; then
local last_alert=$(grep "\"${ts_name}\":{\"level\":\"${alert_level}\"" ${ALERT_STATE_FILE} | \
grep -o '"last_alert":[0-9]*' | cut -d: -f2)
local now=$(date +%s)
local diff=$((now - last_alert))
# 同级别告警间隔至少30分钟
if [ ${diff} -lt 1800 ]; then
logf "INFO" "表空间 ${ts_name} ${alert_level}级别告警间隔不足30分钟,跳过"
return 1
fi
fi
return 0
}
8.2 多实例批量监控脚本
当你的环境中有多个Oracle实例时,逐个执行脚本效率太低。一个批量监控脚本可以同时检查所有实例:
#!/bin/bash
#=============================================================
# 脚本名称: oracle_multi_instance_monitor.sh
# 脚本功能: 多实例表空间批量监控
# 说明: 遍历服务器上所有Oracle实例,逐个检测
#=============================================================
LOG_DIR=/u01/scripts/log/tablespace_monitor
mkdir -p ${LOG_DIR}
LOG_FILE=${LOG_DIR}/multi_instance_monitor_$(date +%Y%m%d_%H%M%S).log
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$1] $2" | tee -a ${LOG_FILE}
}
# 获取本机所有运行的Oracle实例
get_running_instances() {
# 方法1: 通过oratab
if [ -f etc/oratab ]; then
grep -v '^#' etc/oratab | grep -v '^$' | \
awk -F: '{if($2!="") print $1":"$2}' | \
while read entry; do
local sid=$(echo ${entry} | cut -d: -f1)
local home=$(echo ${entry} | cut -d: -f2)
# 检查实例是否运行
local pmon_pid=$(ps -ef | grep "ora_pmon_${sid}" | grep -v grep | awk '{print $2}')
if [ -n "${pmon_pid}" ]; then
echo "${sid}:${home}"
fi
done
fi
# 方法2: 通过ps命令(备用)
ps -ef | grep 'ora_pmon_' | grep -v grep | \
awk '{print $NF}' | sed 's/ora_pmon_//' | \
while read sid; do
local home=$(ps -ef | grep "ora_pmon_${sid}" | grep -v grep | \
awk '{print $8}' | xargs dirname | xargs dirname)
echo "${sid}:${home}"
done | sort -u
}
# 执行单个实例的监控
monitor_instance() {
local sid=$1
local home=$2
log "INFO" "=========================================="
log "INFO" "开始监控实例: ${sid}"
log "INFO" "ORACLE_HOME: ${home}"
log "INFO" "=========================================="
export ORACLE_SID=${sid}
export ORACLE_HOME=${home}
export PATH=${home}/bin:${PATH}
# 调用单实例监控脚本
if [ -f u01/scripts/oracle_tablespace_monitor.sh ]; then
u01/scripts/oracle_tablespace_monitor.sh
else
log "ERROR" "监控脚本不存在: u01/scripts/oracle_tablespace_monitor.sh"
fi
}
# ===================== 主逻辑 =====================
log "INFO" "=========================================="
log "INFO" "多实例表空间批量监控开始"
log "INFO" "=========================================="
instance_list=$(get_running_instances)
instance_count=$(echo "${instance_list}" | wc -l)
log "INFO" "发现 ${instance_count} 个运行中的Oracle实例"
if [ -z "${instance_list}" ]; then
log "WARN" "未发现运行中的Oracle实例"
exit 0
fi
echo "${instance_list}" | while IFS=: read sid home; do
monitor_instance "${sid}" "${home}"
done
log "INFO" "=========================================="
log "INFO" "多实例批量监控完成"
log "INFO" "=========================================="
8.3 数据文件增长趋势分析
除了实时监控,了解数据文件的增长趋势也很重要。这能帮助你提前预判容量需求:
-- =============================================
-- 表空间增长趋势分析(最近30天)
-- 依赖AWR快照数据
-- =============================================
SELECT
ts.name AS tablespace_name,
TO_CHAR(snap.begin_interval_time, 'YYYY-MM-DD') AS snap_date,
ROUND(MAX(tu.tablespace_size * dt.block_size 1024 1024), 2) AS total_mb,
ROUND(MAX(tu.tablespace_usedsize * dt.block_size 1024 1024), 2) AS used_mb,
ROUND(MAX(tu.tablespace_usedsize) MAX(tu.tablespace_size) * 100, 2) AS pct_used
FROM dba_hist_tbspc_space_usage tu,
v$tablespace ts,
dba_hist_snapshot snap,
dba_tablespaces dt
WHERE tu.tablespace_id = ts.ts#
AND tu.snap_id = snap.snap_id
AND ts.name = dt.tablespace_name
AND snap.begin_interval_time >= SYSDATE - 30
GROUP BY ts.name, TO_CHAR(snap.begin_interval_time, 'YYYY-MM-DD')
ORDER BY ts.name, snap_date;
基于这个数据,可以计算每日增长量和预计用尽天数:
-- =============================================
-- 计算表空间每日增长量和预计用尽天数
-- =============================================
WITH growth_data AS (
SELECT
ts.name AS ts_name,
ROUND(AVG(daily_growth), 2) AS avg_daily_growth_mb,
ROUND(MAX(current_used), 2) AS current_used_mb,
ROUND(MAX(current_total), 2) AS current_total_mb
FROM (
SELECT
ts.ts# AS ts_id,
ts.name,
LAG(tu.tablespace_usedsize * dt.block_size 1024 1024)
OVER (PARTITION BY ts.ts# ORDER BY snap.snap_id) AS prev_used,
tu.tablespace_usedsize * dt.block_size 1024 1024 AS curr_used,
(tu.tablespace_usedsize * dt.block_size 1024 1024) -
LAG(tu.tablespace_usedsize * dt.block_size 1024 1024)
OVER (PARTITION BY ts.ts# ORDER BY snap.snap_id) AS daily_growth,
LAST_VALUE(tu.tablespace_usedsize * dt.block_size 1024 1024)
OVER (PARTITION BY ts.ts# ORDER BY snap.snap_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS current_used,
LAST_VALUE(tu.tablespace_size * dt.block_size 1024 1024)
OVER (PARTITION BY ts.ts# ORDER BY snap.snap_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS current_total
FROM dba_hist_tbspc_space_usage tu,
v$tablespace ts,
dba_hist_snapshot snap,
dba_tablespaces dt
WHERE tu.tablespace_id = ts.ts#
AND tu.snap_id = snap.snap_id
AND ts.name = dt.tablespace_name
AND snap.begin_interval_time >= SYSDATE - 7
)
WHERE daily_growth > 0
GROUP BY ts.name
)
SELECT
ts_name,
avg_daily_growth_mb,
current_used_mb,
current_total_mb,
ROUND((current_total_mb - current_used_mb), 2) AS remaining_mb,
CASE
WHEN avg_daily_growth_mb > 0
THEN ROUND((current_total_mb - current_used_mb) avg_daily_growth_mb, 0)
ELSE 9999
END AS days_until_full
FROM growth_data
ORDER BY days_until_full ASC;
💡 实践意义:如果某个表空间预计30天内用尽,现在就该规划扩容了,而不是等到90%告警才手忙脚乱。
九、生产环境注意事项与最佳实践
8.1 自动扩容的安全策略
自动扩容虽好,但不能无脑加。以下几个安全策略必须在脚本中体现:
策略1:磁盘空间余量检查
# 在添加数据文件之前,检查目标目录/ASM磁盘组的可用空间
# 建议保留至少20%的磁盘空间作为安全余量
if [ ${avail_space} -lt ${MIN_REQUIRED_SPACE} ]; then
log "ERROR" "剩余空间不足,暂停自动扩容"
send_dingtalk "🚨 空间不足" "存储空间即将用尽,请人工介入!"
exit 1
fi
策略2:表空间文件数量限制
# 检查当前表空间已有多少数据文件
# 接近Oracle限制时,提前告警
local file_count=$(sqlplus -S "${DB_USER}" <<EOF
SELECT COUNT(*) FROM dba_data_files WHERE tablespace_name = '${ts_name}';
EOF
)
if [ ${file_count} -ge 60000 ]; then
log "WARN" "表空间 ${ts_name} 数据文件数量(${file_count})接近上限,请关注"
fi
策略3:扩容次数限制
# 同一个表空间,一个维护窗口内最多自动扩容N次
# 避免磁盘瞬间被吃光
MAX_AUTOADD_COUNT=5
8.5 数据文件大小的选择建议
在自动扩容脚本中,每次添加多大的数据文件?这个看似简单的问题,其实有很多讲究。
建议:
原因分析:
- 文件数量
:每个数据文件都需要Oracle维护元数据,文件太多会影响性能 - 碎片问题
:如果增量太小,文件会分散在不同的磁盘位置 - 恢复时间
:单个文件太大,RMAN恢复单个文件的时间会很长 - rebalance时间
:ASM环境中,添加大文件后rebalance耗时更长
通用规则:
单个数据文件不要超过32GB(8K块大小) 一个表空间的数据文件数量控制在100个以内 ASM环境中,AU大小为4MB或8MB时,文件增量建议是AU的整数倍
8.6 监控脚本的部署架构
在大型企业中,推荐使用中心化的监控架构:

这个具体看各自部署了,现在也有很多基于AI Agent的各种运维skill,百花齐现,更多的通过蒸馏各大技术和开源平台的能力、资源,进行蒸馏、总结,产生的各种知识库,AI Skill,现在是AI Agent爆发的时期,也是各种技能、能力Skill化的时期,AI的发展已经从Token消耗转变为数字员工(技能Skill包)的形式,未来运维、DBA是否也会被Skill化?数据库运维、数据库优化,这些可通过具体指标输入、然后综合判断分析输出结果的技能,在我看来也是非常容易被AI Skill化的能力。
控制节点通过SSH向各DB服务器下发监控脚本 各节点执行结果回传到控制节点 控制节点汇总分析后统一告警 可以使用Ansible/SaltStack等工具实现
策略4:大表空间人工审核
# 超过一定大小的表空间,自动扩容前通知DBA确认
if [ ${current_size_gb} -gt 1024 ]; then
log "WARN" "表空间 ${ts_name} 已超过1TB,自动扩容需人工确认"
send_dingtalk "⚠️ 需人工确认" "表空间 ${ts_name} 已达 ${current_size_gb}GB,请确认是否扩容"
continue # 跳过自动扩容
fi
8.2 日志管理规范
日志不仅是排查问题的依据,也是审计的重要材料。
日志保留策略:
# 清理30天以前的日志
find ${LOG_DIR} -name "*.log" -mtime +30 -delete
# 日志文件命名规范
# tablespace_monitor_YYYYMMDD_HHMMSS.log
# 便于按日期查找
日志分级:
INFO:正常操作记录 WARN:需要关注但不影响功能 ERROR:操作失败,需要人工介入 DEBUG:详细的调试信息(默认不输出到终端,只写文件)
8.3 告警降噪策略
告警太多会让DBA麻木,几个降噪技巧:
- 聚合告警
:一次检查只发一条汇总消息,不要每个表空间发一条 - 告警频率限制
:同一问题24小时内只告警N次 - 自动恢复通知
:扩容成功后通知已恢复正常 - 夜间静默
:23:00-7:00的非紧急告警延迟到早上发送
8.4 监控大屏集成
如果你的团队有监控大屏(如Grafana + Prometheus),可以将表空间使用率数据推送到时序数据库:
# 推送数据到Prometheus Pushgateway
cat <<EOF | curl --data-binary @- http://pushgateway:9091/metrics/job/oracle_tablespace_monitor/instance/${ORACLE_SID}
# HELP oracle_tablespace_pct_used Tablespace real usage percentage
# TYPE oracle_tablespace_pct_used gauge
oracle_tablespace_pct_used{tablespace="USERS",instance="${ORACLE_SID}"} 92.35
oracle_tablespace_pct_used{tablespace="SYSTEM",instance="${ORACLE_SID}"} 45.67
oracle_tablespace_pct_used{tablespace="SYSAUX",instance="${ORACLE_SID}"} 78.23
EOF
九、常见问题FAQ
Q1: AUTOEXTEND ON的表空间还需要自动扩容吗?
需要。 AUTOEXTEND是在现有数据文件上自动增长,但文件有最大限制(通常是32GB)。当文件接近上限时,如果不添加新的数据文件,增长就会停止。
自动扩容(添加新数据文件)和自动扩展(AUTOEXTEND)是两个层面:
表空间扩容策略:
├── 第一层: AUTOEXTEND(自动扩展)
│ └── 文件级: 现有数据文件自动增长
│ └── 限制: 单文件最大32GB(8K块)
│
└── 第二层: ADD DATAFILE(自动扩容)
└── 表空间级: 添加新数据文件
└── 触发: 文件接近MAXBYTES时
Q2: 自动添加的数据文件放在哪里?
脚本会自动判断:
如果现有数据文件在ASM(路径以 +
开头)→ 新文件也放到同一个ASM磁盘组如果现有数据文件在文件系统 → 新文件放到同一个目录
不会跨存储类型添加。
Q3: 如何处理TEMP和UNDO表空间?
TEMP和UNDO表空间比较特殊:
- TEMP表空间
:通常不需要自动扩容(使用 dba_temp_files
,不是dba_data_files
) - UNDO表空间
:可以自动扩容,但要注意 UNDO_RETENTION
的设置
脚本中可以增加排除逻辑:
# 排除不需要自动扩容的表空间
case "${ts_name}" in
TEMP*|TEMP)
logf "INFO" "跳过TEMP表空间"
continue
;;
SYSTEM|SYSAUX)
logf "INFO" "SYSTEM/SYSAUX表空间通常不需要自动扩容"
continue
;;
esac
Q4: RAC环境下的注意事项
RAC环境中运行此脚本时:
- 只需在一个节点执行
:因为所有节点看到的数据文件视图( dba_data_files
)是一样的 - 执行节点的选择
:建议选择primary实例节点,或者通过SCAN连接 - ASM磁盘组检查
:在任何节点执行结果相同(ASM是共享的)
Q5: 如何测试脚本而不实际执行扩容?
可以增加一个 DRY_RUN
模式:
# 在配置区域添加
DRY_RUN=true # 设为true时只检测不扩容
# 在add_datafile函数中
if [ "${DRY_RUN}" = true ]; then
log "INFO" "[DRY_RUN模式] 将执行: ALTER TABLESPACE ${tspace} ADD DATAFILE '${file}' SIZE ${size}"
return 0
fi
十、总结
这篇文章从一个表空间监控的Shell脚本出发,深入到了:
✅ 表空间真实使用率的计算方式(基于AUTOEXTEND MAXBYTES) ✅ 文件系统 vs ASM磁盘组的智能存储判断 ✅ 数据文件自动命名防冲突机制 ✅ 完整的日志记录和告警通知体系 ✅ ASM磁盘组的监控SQL和冗余策略 ✅ 从11g到26ai的ASM版本演进和限制变化 ✅ RAC环境下ASM架构的关键差异
表空间监控是DBA的基本功,但做好并不简单。一个生产级别的脚本,需要考虑存储判断、命名冲突、日志审计、告警降噪等多个方面。希望这篇文章能帮你少踩几个坑。
如果这篇文章对你有帮助,点赞、在看、转发三连走起 🙌
下期预告:Oracle AWR报告深度解读与性能调优实战,敬请期待!
Acdante一个专注于Oracle数据库的技术公众号
深耕数据库运维,分享实战经验
关注我,一起成长
往期回顾:
Oracle生产级别备份脚本分享——逻辑备份和物理备份(expdp&RMAN)【Oracle数据库分享--0x01】
Oracle数据加密技术演进与实践指南从10g到26ai的安全之道【Oracle数据库分享--0x02】
Oracle数据库表空间与数据文件实战指南【Oracle数据库分享--0x03】
Oracle ADG单机部署实战指南【Oracle数据库分享--0x04】
Oracle DataGuard搭建信息收集清单和ADG自动化部署脚本(单机版本)免费开放【Oracle数据库分享--0x05】
本文为原创技术文章,如需转载请注明出处-add by Acdante。
附录A:ASM磁盘组空间监控脚本(独立版)
除了表空间监控,ASM磁盘组的空间也需要独立监控。下面提供一个ASM专项监控脚本:
#!/bin/bash
#=============================================================
# 脚本名称: asm_diskgroup_monitor.sh
# 脚本功能: ASM磁盘组空间监控告警
# 适用版本: Oracle 11g/12c/19c/23ai
# 说明: 独立的ASM磁盘组监控,可与表空间监控配合使用
#=============================================================
export ORACLE_HOME=/u01/app/grid/product/19.0.0/grid
export ORACLE_SID=+ASM1
export PATH=$ORACLE_HOME/bin:$PATH
# 告警阈值(usable_file_mb低于此值告警,单位GB)
WARNING_THRESHOLD=100
CRITICAL_THRESHOLD=50
# 通知配置
DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
LOG_DIR=/u01/scripts/log/asm_monitor
mkdir -p ${LOG_DIR}
LOG_FILE=${LOG_DIR}/asm_monitor_$(date +%Y%m%d_%H%M%S).log
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$1] $2" | tee -a ${LOG_FILE}
}
# 查询ASM磁盘组状态
check_asm_diskgroups() {
local sql_output=$(sqlplus -S "/ as sysasm" <<EOF
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET LINESIZE 300
SET TRIMOUT ON
SET TRIMSPOOL ON
SET COLSEP '|'
SELECT
name,
state,
type,
ROUND(total_mb/1024, 2),
ROUND(free_mb/1024, 2),
ROUND(usable_file_mb/1024, 2),
CASE
WHEN usable_file_mb < 0 THEN 'CRITICAL'
WHEN usable_file_mb/1024 < ${CRITICAL_THRESHOLD} THEN 'CRITICAL'
WHEN usable_file_mb/1024 < ${WARNING_THRESHOLD} THEN 'WARNING'
ELSE 'NORMAL'
END AS status
FROM v\\$asm_diskgroup
ORDER BY usable_file_mb ASC;
EXIT;
EOF
)
echo "${sql_output}"
}
# 检查ASM磁盘状态
check_asm_disks() {
local sql_output=$(sqlplus -S "/ as sysasm" <<EOF
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET LINESIZE 300
SET TRIMOUT ON
SET TRIMSPOOL ON
SET COLSEP '|'
SELECT
dg.name,
d.name,
d.path,
d.failgroup,
d.state,
d.mount_status,
d.header_status,
ROUND(d.total_mb/1024, 2),
ROUND(d.free_mb/1024, 2),
CASE
WHEN d.state != 'NORMAL' THEN 'ABNORMAL'
WHEN d.mount_status != 'CACHED' THEN 'NOT_CACHED'
ELSE 'OK'
END AS disk_status
FROM v\\$asm_disk d, v\\$asm_diskgroup dg
WHERE d.group_number = dg.group_number
ORDER BY dg.name, d.name;
EXIT;
EOF
)
echo "${sql_output}"
}
# 主逻辑
main() {
log "INFO" "=========================================="
log "INFO" "ASM磁盘组监控开始"
log "INFO" "=========================================="
# 检查磁盘组
local dg_output=$(check_asm_diskgroups)
local alert_msg=""
local critical_found=false
while IFS='|' read -r dg_name state dg_type total_gb free_gb usable_gb status; do
[ -z "${dg_name}" ] && continue
dg_name=$(echo "${dg_name}" | tr -d '[:space:]')
status=$(echo "${status}" | tr -d '[:space:]')
log "INFO" "磁盘组: ${dg_name}, 状态: ${state}, 类型: ${dg_type}, 总量: ${total_gb}GB, 可用: ${usable_gb}GB, 告警: ${status}"
if [ "${status}" = "CRITICAL" ]; then
alert_msg="${alert_msg}\n🔴 **${dg_name}**: 可用空间仅 ${usable_gb}GB (CRITICAL)"
critical_found=true
elif [ "${status}" = "WARNING" ]; then
alert_msg="${alert_msg}\n🟡 **${dg_name}**: 可用空间 ${usable_gb}GB (WARNING)"
fi
done <<< "${dg_output}"
# 检查磁盘异常
local disk_output=$(check_asm_disks)
local disk_alert=""
while IFS='|' read -r dg_name disk_name disk_path fg state mount_st header_st total_gb free_gb disk_st; do
[ -z "${dg_name}" ] && continue
disk_st=$(echo "${disk_st}" | tr -d '[:space:]')
if [ "${disk_st}" != "OK" ]; then
disk_alert="${disk_alert}\n⚠️ 磁盘异常: ${disk_name} (${dg_name}) - ${disk_st}"
critical_found=true
fi
done <<< "${disk_output}"
# 发送告警
if [ -n "${alert_msg}" ] || [ -n "${disk_alert}" ]; then
local dingtalk_content="## 🔔 ASM磁盘组监控报告\n\n"
dingtalk_content+="**检查时间**: $(date '+%Y-%m-%d %H:%M:%S')\n"
dingtalk_content+="**节点**: $(hostname)\n\n"
if [ -n "${alert_msg}" ]; then
dingtalk_content+="### 磁盘组空间告警${alert_msg}\n\n"
fi
if [ -n "${disk_alert}" ]; then
dingtalk_content+="### 磁盘状态异常${disk_alert}\n\n"
fi
# 发送钉钉通知
curl -s -H "Content-Type: application/json" \
-d "{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"ASM监控告警\",\"text\":\"${dingtalk_content}\"}}" \
"${DINGTALK_WEBHOOK}" >/dev/null 2>&1
if [ "${critical_found}" = true ]; then
log "ERROR" "发现CRITICAL级别告警!"
else
log "WARN" "发现WARNING级别告警"
fi
else
log "INFO" "✅ 所有ASM磁盘组状态正常"
fi
log "INFO" "=========================================="
log "INFO" "ASM磁盘组监控完成"
log "INFO" "=========================================="
}
main 2>&1 | tee -a ${LOG_FILE}
附录B:Oracle版本与ASM单盘限制速查表
以下表格汇总了从10g到26ai各版本中,ASM在单盘容量、磁盘数量等方面的限制:
Oracle 10g R2
Oracle 11g R2
Oracle 12c R1/R2
Oracle 19c
Oracle 23ai
Oracle 26ai
📌 版本升级注意事项:
- 从11g升级到12c及以上版本,ASM元数据格式会自动转换
- 建议在升级前备份ASM元数据:md_backup
- 升级后可以调整AU大小(需要rebalance),但建议在建磁盘组时就选好
- 12c以上的Flex ASM需要重新配置GI,不是简单的升级
Oracle 26ai ASM相关特性梳理:
| 三大兼容属性 | ||
| 属性大小规则 | ||
| 默认 / 最小版本 | ||
| 单磁盘大小上限 | ≥12.1 即可支持 >2TB 盘 | |
| 最大文件大小(AU=1MB) | ||
| 最大文件大小(AU=4MB) | ||
| ADVM 启用条件 | ||
| Flex 盘组要求 | ||
| 查看方式 |
附录C:一个真实的生产案例
最后分享一个我亲身经历的案例,让大家感受一下表空间监控的实际价值。
背景
某客户的核心交易数据库(Oracle 19c RAC,2节点),每天凌晨0点开始执行大批量的订单数据ETL。某天凌晨2点,值班DBA被钉钉告警叫醒——ORDER_TBS
表空间使用率达到94%。
问题排查
- 初步检查
:登录数据库确认, ORDER_TBS
确实达到了94% - 增长分析
:通过AWR发现,最近2小时该表空间增长了50GB(平时每小时增长约5GB) - 根本原因
:ETL脚本的一个临时表没有清理,数据累积了2天没有删除
应急处理
由于脚本已经配置了自动扩容(阈值90%),系统在90%时自动添加了一个10GB的数据文件,暂时缓解了压力。但94%说明增长速度超过了自动扩容的速度。
人工干预:
-- 1. 清理累积的临时数据
DELETE FROM order_temp_batch WHERE batch_date < TRUNC(SYSDATE) - 2;
COMMIT;
-- 2. 手动添加更大的数据文件
ALTER TABLESPACE order_tbs ADD DATAFILE
'+DATA/ORCL/DATAFILE/order_tbs_05.dbf'
SIZE 50G AUTOEXTEND ON NEXT 2G MAXSIZE 100G;
-- 3. 重建临时表清理逻辑
ALTER TABLE order_temp_batch ENABLE ROW MOVEMENT;
ALTER TABLE order_temp_batch SHRINK SPACE;
后续改进
增加ETL脚本的临时表自动清理逻辑 调整 ORDER_TBS
的自动扩容增量从10G改为50G增加了容量趋势预测,提前7天预警 增加了ETL期间的表空间增长速率告警(每小时增长超过20GB就告警)
教训
如果没有自动扩容脚本,那次故障会导致核心交易中断,预估损失超过500万元/小时。自动扩容虽然只是加了一个10G的数据文件,但它争取到的那30分钟,足够DBA排查和人工干预了。
自动化不是取代DBA,是给DBA争取时间,将时间用于更有价值的事情上。
Oracle 26ai 官方ASM磁盘组相关说明:
Features Enabled By Disk Group Compatibility Attribute Settings
This topic describes the Oracle ASM features enabled by valid combinations of the disk group compatibility attribute settings.
The following list applies to Oracle ASM features enabled by valid combinations of the disk group compatibility attribute settings.
The value of COMPATIBLE.ASM must always be greater than or equal to the value of COMPATIBLE.RDBMS and COMPATIBLE.ADVM. Starting with Oracle Grid Infrastructure 12.2.0.1 software, the minimum setting for COMPATIBLE.ASM is 11.2.0.2. A value of not applicable (n/a) means that the setting of the attribute has no effect on the feature. Oracle ASM features not explicitly listed in the following table do not require advancing the disk group compatibility attribute settings. Oracle ASM features explicitly identified by an operating system in the following table are available on that operating system starting with the associated disk group attribute settings. If encryption is configured for the first time on Oracle ASM 11g Release 2 (11.2.0.3) on Linux or if encryption parameters must be changed or a new volume encryption key must be created following a software upgrade to Oracle ASM 11g Release 2 (11.2.0.3) on Linux, then the disk group compatibility attributes for ASM and ADVM must be set to 11.2.0.3 or higher. Oracle ACFS does not support encryption or replication with the following: Oracle AI Database data files, control files, online redo logs, archived redo log files, flashback logs, RMAN backups, and Oracle Data Pump dump file sets. Oracle ACFS on Oracle Exadata storage is supported starting with Oracle Grid Infrastructure 12.1.0.2 on Linux. There may also be minimum requirements for the database COMPATIBLE initialization parameter.
Table 4-4 Oracle ASM features enabled by disk group compatibility attribute settings
Disk Group Features Enabled | COMPATIBLE.ASM | COMPATIBLE.RDBMS | COMPATIBLE.ADVM |
Support for larger AU sizes (32 or 64 MB) | >= 11.1 | >= 11.1 | n/a |
Attributes are displayed in the V$ASM_ATTRIBUTE view | >= 11.1 | n/a | n/a |
Fast mirror resync | >= 11.1 | >= 11.1 | n/a |
Variable size extents | >= 11.1 | >= 11.1 | n/a |
Exadata storage | >= 11.1.0.7 | >= 11.1.0.7 | n/a |
OCR and voting files in a disk group | >= 11.2 | n/a | n/a |
Sector size set to nondefault value | >= 11.2 | >= 11.2 | n/a |
Oracle ASM SPFILE in a disk group | >= 11.2 | n/a | n/a |
Oracle ASM File Access Control | >= 11.2 | >= 11.2 | n/a |
ASM_POWER_LIMIT value up to 1024 | >= 11.2.0.2 | n/a | n/a |
Content type of a disk group | >= 11.2.0.3 | n/a | n/a |
Appliance mode for Oracle Exadata (no fixed partnering) | >= 11.2.0.4 | n/a | n/a |
Replication status of a disk group | >= 12.1 | n/a | n/a |
Managing a shared password file in a disk group | >= 12.1 | n/a | n/a |
Greater than 2 TB Oracle ASM disks without Oracle Exadata storage | >= 12.1 | >= 12.1 | n/a |
Fixed partnering for Oracle Exadata | >= 12.1.0.2 | n/a | n/a |
Appliance mode for Oracle Data Appliance (ODA) | >= 12.1.0.2 | n/a | n/a |
Support for Oracle Exadata sparse disk groups (sparse disk groups not supported in Oracle ACFS) | >= 12.1.0.2 | >= 12.1.0.2 | n/a |
Support for resync checkpoint | >= 12.1.0.2 | >= 12.1.0.2 | n/a |
LOGICAL_SECTOR_SIZE | >= 12.2 | n/a | n/a |
Altering sector size | >= 12.2 | n/a | n/a |
Oracle ASM flex and extended disk groups | >= 12.2 | >= 12.2 | n/a |
SCRUB_ASYNC_LIMIT | >= 12.2 | n/a | n/a |
PREFERRED_READ.ENABLED Oracle Database 12c Release 2 (12.2) is required. | >=12.2 | n/a | n/a |
Converting normal or high redundancy disk groups to flex disk groups without restricted mount | >=18.0 | >=12.2 | n/a |
Oracle ASM flex disk group support for multitenant cloning | >=18.0 | >=18.0 | n/a |
Storage conversion for member clusters | >=18.0 and <21.0 | n/a | n/a |
Virtual Allocation Metadata (VAM) enabled on non-sparse normal and high redundancy disk groups | >=18.0 | n/a | n/a |
Support for single parity protection in Oracle ASM flex disk groups The database COMPATIBLE initialization parameter must be set to 19.1 or greater. | >=19.1 | n/a | n/a |
Support for double parity protection in Oracle ASM flex disk groups The database COMPATIBLE initialization parameter must be set to 19.5 or greater. | >=19.5 | n/a | n/a |
来自<https://docs.oracle.com/en/database/oracle/oracle-database/26/ostmg/diskgroup-compatibility.html#GUID-9E283CAC-8359-4B4D-A9DE-3A9C52BCDD47>
:
本文为原创技术文章,如需转载请注明出处,谢谢。




