Oracle 主从切换脚本

news2024/11/17 0:34:46

一、 切换前预检查

1. dg_precheck_main_v1.4.sh

#!/bin/bash

#**********************************************************************************
# Author: Hehuyi_In
# Date: 2022年06月16日
# FileName: dg_precheck_main_v1.4.sh
# 
# For sys user, execute the script directly. 
# For other sysdba privileged users, you need to manually synchronize the password file to standby server in versions below 12.2.
# The script cannot be executed on the Linux 5 os.
#
# Example: 
# ./dg_precheck_main_v1.4.sh &> dg_precheck.log
# cat dg_precheck.log | grep --color=no -E "Warning|Error" ,or less -R dg_precheck.log
#
#
# Description:
# 2022-06-16 v_1.0 just get and print the infomation we want
# 2022-06-17 v_1.1 auto check the database role
# 2022-06-22 v_1.2 adjust the output format
# 2022-06-28 v_1.3 add compare function, compare two nodes at a time
# 2022-06-28 split to two scripts dg_precheck_main_v1.0.sh and dg_precheck_func_v1.0.sh
# 2022-07-06 dg_precheck_main_v1.1 check os info
# 2022-07-12 dg_precheck_main_v1.2 adjust the output format
# 2022-07-13 dg_precheck_main_v1.3 adjust os info check function
# 2022-10-19 dg_precheck_main_v1.4 add checking items: dblink,standby_file_management,flashback_on,db_flashback_retention_target,db_recovery_file_dest,db_recovery_file_dest_size 
#**********************************************************************************

source ./dg_precheck_func_v1.4.sh

# TNS列表
TNS_LIST=(test_dg test)

# 连接信息
DBUSER='sys/"xxxxx"'


# --------------------------- 检查并设置TNS_LIST中各tnsname对应数据库角色 ----------------------------------
auto_check_db_role

# 字典定义
declare -A PRIMARY_TNS_DIC
declare -A STANDBY_TNS_DIC


echo -e "--------------------------------------------------------------------------------"
echo -e "- 各节点主要参数检查                                                "
echo -e "--------------------------------------------------------------------------------\n"

# 内存参数 及 compatible 检查和对比
PARAMS_LIST=(sga_max_size sga_target pga_aggregate_target shared_pool_size db_cache_size streams_pool_size compatible standby_file_management)

for PARAM in ${PARAMS_LIST[@]};
do

PRIMARY_TNS_DIC+=([$PARAM]=`get_params_value $PRIMARY_TNS $PARAM`)
STANDBY_TNS_DIC+=([$PARAM]=`get_params_value $STANDBY_TNS $PARAM`)

# 主从库间对比参数是否相等
compare_value ${PRIMARY_TNS_DIC[$PARAM]} ${STANDBY_TNS_DIC[$PARAM]} $PARAM

done

# 获取其他参数值
PARAMS_LIST=(log_archive_max_processes db_create_file_dest db_file_name_convert log_file_name_convert fal_client fal_server log_archive_config db_recovery_file_dest db_recovery_file_dest_size db_flashback_retention_target)

for PARAM in ${PARAMS_LIST[@]};
do

PRIMARY_TNS_DIC+=([$PARAM]=`get_params_value $PRIMARY_TNS $PARAM`)
STANDBY_TNS_DIC+=([$PARAM]=`get_params_value $STANDBY_TNS $PARAM`)

done

echo -e "\n"

echo -e "--------------------------------------------------------------------------------"
echo -e "- 各节点主要文件数检查                                                          "
echo -e "--------------------------------------------------------------------------------\n"

PRIMARY_TNS_DIC+=([redo_count]=`get_file_count $PRIMARY_TNS "LOG"`)
PRIMARY_TNS_DIC+=([standby_count]=`get_file_count $PRIMARY_TNS "STANDBY_LOG"`)
PRIMARY_TNS_DIC+=([tempfile_count]=`get_file_count $PRIMARY_TNS "TEMPFILE"`)
STANDBY_TNS_DIC+=([redo_count]=`get_file_count $STANDBY_TNS "LOG"`)
STANDBY_TNS_DIC+=([standby_count]=`get_file_count $STANDBY_TNS "STANDBY_LOG"`)
STANDBY_TNS_DIC+=([tempfile_count]=`get_file_count $STANDBY_TNS "TEMPFILE"`)

PRIMARY_TNS_DIC+=([datafile_online_count]=`get_datafile_online_count $PRIMARY_TNS`)
STANDBY_TNS_DIC+=([datafile_online_count]=`get_datafile_online_count $STANDBY_TNS`)

compare_value ${PRIMARY_TNS_DIC[redo_count]} ${STANDBY_TNS_DIC[redo_count]} "redo_count"
compare_value ${PRIMARY_TNS_DIC[standby_count]} ${STANDBY_TNS_DIC[standby_count]} "standby_count"
compare_value ${PRIMARY_TNS_DIC[tempfile_count]} ${STANDBY_TNS_DIC[tempfile_count]} "tempfile_count"
compare_value ${PRIMARY_TNS_DIC[datafile_online_count]} ${STANDBY_TNS_DIC[datafile_online_count]} "datafile_online_count"

echo -e "\n"

echo -e "--------------------------------------------------------------------------------"
echo -e "- 从节点redo日志状态检查                                                        "
echo -e "--------------------------------------------------------------------------------\n"

# 只有从库需要检查
STANDBY_TNS_DIC+=([redo_status_count]=`get_redo_status_count $STANDBY_TNS`)
check_redo_status_count ${STANDBY_TNS_DIC[db_create_file_dest]} ${STANDBY_TNS_DIC[log_file_name_convert]} ${STANDBY_TNS_DIC[redo_status_count]} 
echo -e "\n"


echo -e "--------------------------------------------------------------------------------"
echo -e "- 闪回开启及保留时间检查                                                        "
echo -e "--------------------------------------------------------------------------------\n"

# --------------------------- flashback_on 与 db_flashback_retention_target -------------------------------

PRIMARY_TNS_DIC+=([flashback_on]=`get_flashback_on $PRIMARY_TNS`)
echo -e "- primary database                                                           "
compare_value ${PRIMARY_TNS_DIC[flashback_on]} "NO" "flashback_on"
compare_value ${PRIMARY_TNS_DIC[db_flashback_retention_target]} "4320" "db_flashback_retention_target"

STANDBY_TNS_DIC+=([flashback_on]=`get_flashback_on $STANDBY_TNS`)
echo -e "- standby database                                                                  "
compare_value ${STANDBY_TNS_DIC[flashback_on]} "YES" "flashback_on"
compare_value ${STANDBY_TNS_DIC[db_flashback_retention_target]} "4320" "db_flashback_retention_target"

echo -e "\n"

echo -e "--------------------------------------------------------------------------------"
echo -e "- FRA区设置检查                                                                   "
echo -e "--------------------------------------------------------------------------------\n"

# --------------------------- db_recovery_file_dest 与 db_recovery_file_dest_size -------------------------------
# 主从库设置是否相同
compare_value ${PRIMARY_TNS_DIC[db_recovery_file_dest]} ${STANDBY_TNS_DIC[db_recovery_file_dest]} "db_recovery_file_dest"
compare_value ${PRIMARY_TNS_DIC[db_recovery_file_dest_size]} ${STANDBY_TNS_DIC[db_recovery_file_dest_size]} "db_recovery_file_dest_size"

echo -e "\n"


echo -e "--------------------------------------------------------------------------------"
echo -e "- 各节点主从相关参数检查                                                        "
echo -e "--------------------------------------------------------------------------------\n"

# --------------------------- fal_client 与 fal_server ---------------------------

# fal_server的值应该包括对方,因为${PRIMARY_TNS_DIC[fal_server]}返回值包含逗号,因此需要在传参时加"",避免参数传入函数后被截断

# 主节点参数值
include_value  $PRIMARY_TNS "fal_server" "${PRIMARY_TNS_DIC[fal_server]}" $STANDBY_TNS
# 从节点参数值
include_value  $STANDBY_TNS "fal_server" "${STANDBY_TNS_DIC[fal_server]}" $PRIMARY_TNS

# fal_client的值应该包含自己
# 主节点参数值
include_value  $PRIMARY_TNS "fal_client" "${PRIMARY_TNS_DIC[fal_client]}" $PRIMARY_TNS
# 从节点参数值
include_value  $STANDBY_TNS "fal_client" "${STANDBY_TNS_DIC[fal_client]}" $STANDBY_TNS


# --------------------------- log_archive_config -------------------------------
# 主节点参数值
include_value $PRIMARY_TNS "log_archive_config" ${PRIMARY_TNS_DIC[log_archive_config]} $STANDBY_TNS
# 从节点参数值
include_value $STANDBY_TNS "log_archive_config" ${STANDBY_TNS_DIC[log_archive_config]} $PRIMARY_TNS

# --------------------------- log_archive_max_processes -------------------------------
check_log_archive_max_processes $PRIMARY_TNS ${PRIMARY_TNS_DIC[log_archive_max_processes]} 
check_log_archive_max_processes $STANDBY_TNS ${STANDBY_TNS_DIC[log_archive_max_processes]} 

# --------------------------- log_archive_dest_n 与 log_archive_dest_state_n -------------------------------

check_archive_dest $PRIMARY_TNS
check_archive_dest $STANDBY_TNS

check_archive_dest_state $PRIMARY_TNS
check_archive_dest_state $STANDBY_TNS

# --------------------------- OMF 与 db_file_name_convert,log_file_name_convert -------------------------------

check_omf_and_convert_params $PRIMARY_TNS ${PRIMARY_TNS_DIC[db_create_file_dest]} ${PRIMARY_TNS_DIC[db_file_name_convert]} ${PRIMARY_TNS_DIC[log_file_name_convert]}
check_omf_and_convert_params $STANDBY_TNS ${STANDBY_TNS_DIC[db_create_file_dest]} ${STANDBY_TNS_DIC[db_file_name_convert]} ${STANDBY_TNS_DIC[log_file_name_convert]}

echo -e "\n"


echo -e "--------------------------------------------------------------------------------"
echo -e "- 从节点延迟 MRP进程与触发器检查                                                "
echo -e "--------------------------------------------------------------------------------\n"

check_db_lag $STANDBY_TNS "transport lag"
check_db_lag $STANDBY_TNS "apply lag"

check_mrp_process $STANDBY_TNS

check_mrp_trigger $PRIMARY_TNS
check_mrp_trigger $STANDBY_TNS
echo -e "\n"

echo -e "--------------------------------------------------------------------------------"
echo -e "- dblink检查                                                                   "
echo -e "--------------------------------------------------------------------------------\n"

# --------------------------- dblink -------------------------------
# 检查主库即可
check_dblink $PRIMARY_TNS
echo -e "\n"

2. dg_precheck_func_v1.4.sh

#!/bin/bash

#**********************************************************************************
# Author: Hehuyi_In
# Date: 2022年06月16日
# FileName: dg_precheck_func_v1.4.sh
# 
# Description:
# 2022-06-16 v_1.0 just get and print the infomation we want
# 2022-06-17 v_1.1 auto check the database role
# 2022-06-22 v_1.2 adjust the output format
# 2022-06-28 v_1.3 add compare function, compare two nodes at a time
# 2022-06-28 split to two scripts dg_precheck_main_v1.0.sh and dg_precheck_func_v1.0.sh
# 2022-07-06 dg_precheck_func_v1.1 check os info
# 2022-07-12 dg_precheck_func_v1.2 adjust the output format
# 2022-07-13 dg_precheck_func_v1.3 adjust os info check function
# 2022-10-19 dg_precheck_func_v1.4 add checking items: dblink,standby_file_management,flashback_on,db_flashback_retention_target,db_recovery_file_dest,db_recovery_file_dest_size 
#**********************************************************************************

# 颜色定义
# 红色
ERROR_COLOR='\e[1;31m'
# 绿色
NARMAL_COLOR='\e[1;32m'
# 黄色
WARNING_COLOR='\e[1;33m'
# 重置
RESET_COLOR='\e[0m'

# ------------------------------------------------------------------------
# 检查TNS_LIST中各tnsname对应数据库角色
auto_check_db_role() {
    for TNS in ${TNS_LIST[@]}; do
        DB_ROLE=$(
            sqlplus -s $DBUSER@$TNS as sysdba <<EOF

set heading off feedback off pagesize 0 verify off echo off
select database_role from v\$database;
EOF
        )
        # 主库,则赋值给 PRIMARY_TNS
        if [[ "$DB_ROLE" = "PRIMARY" ]]; then
            PRIMARY_TNS=$TNS
            # 从库,则赋值给 STANDBY_TNS
        elif [[ "$DB_ROLE" = "PHYSICAL STANDBY" ]]; then
            STANDBY_TNS=$TNS
        fi        
    done

    if [[ "$PRIMARY_TNS" = "" ]]; then
       echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! There is no primary database tns ${RESET_COLOR}"
       exit 1
    elif [[ "$STANDBY_TNS" = "" ]]; then
       echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! There is no standby database tns ${RESET_COLOR}"
       exit 1
    fi
}

# ----------------------------------获取操作系统信息--------------------------------------
# 参数1:TNS连接串

# get_cpu_info() {
#     sqlplus -s $DBUSER@$1 as sysdba <<EOF
# !cat /proc/cpuinfo | grep "processor" | wc -l
# EOF
# }

# get_memory_info() {
#     sqlplus -s $DBUSER@$1 as sysdba <<EOF
# !free -h | grep 'Mem:' | awk '{print \$2}'
# EOF
# }

# get_disk_info() {
#     sqlplus -s $DBUSER@$1 as sysdba <<EOF
# !df -m | grep -v 'Filesystem' | awk '{sum+=\$2} END {print sum}'
# EOF
# }

# 获取Oracle参数值
# 参数1:TNS连接串,参数2:待检查参数名(不区分大小写)

get_params_value() {
    if [[ "$1" != "" && "$2" != "" ]]; then
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
select value from v\$parameter where upper(name)=upper('$2');
EOF
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! get_params_value() parameter 1 and 2 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# log_archive_max_processes 参数检查
# 参数1:TNS连接串,参数2:待检查参数值

check_log_archive_max_processes() {
    if [[ "$1" != "" && "$2" != "" ]]; then
        if [[ "$2" -ge 4 ]]; then
            printf "${NARMAL_COLOR}%-10s | Success | log_archive_max_processes >= 4${RESET_COLOR}\n" $1
        else
            printf "${WARNING_COLOR}%-10s | Warning | log_archive_max_processes < 4${RESET_COLOR}\n" $1
        fi
    else
        echo "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! check_log_archive_max_processes() parameter 1 and 2 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 检查log_archive_dest_n参数,是否有设置DELAY关键字(延迟从库)
# 参数1:TNS连接串

check_archive_dest() {
    if [[ "$1" != "" ]]; then
            COUNT=$(
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
select count(*) FROM v\$parameter where name like 'log_archive_dest%' and upper(value) like '%DELAY%';
EOF
       )
        if [[ "$COUNT" -eq 0 ]]; then
            printf "${NARMAL_COLOR}%-10s | Success | There is no DELAY attribute in log_archive_dest_n${RESET_COLOR}\n" $1
        else
            printf "${WARNING_COLOR}%-10s | Warning | There is DELAY attribute in log_archive_dest_n${RESET_COLOR}\n" $1
        fi
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! check_archive_dest() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 检查log_archive_dest_state_n参数,是否有设置DEFER
# 参数1:TNS连接串

check_archive_dest_state() {
    if [[ "$1" != "" ]]; then
            COUNT=$(
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
select count(*) FROM v\$parameter where name like 'log_archive_dest_state%' and upper(value)='DEFER';
EOF
       )
        if [[ "$COUNT" -eq 0 ]]; then
            printf "${NARMAL_COLOR}%-10s | Success | There is no DEFER value in log_archive_dest_state_n${RESET_COLOR}\n" $1 
        else
            printf "${WARNING_COLOR}%-10s | Warning | There is DEFER value in log_archive_dest_state_n${RESET_COLOR}\n" $1
        fi
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! check_archive_dest_state() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# OMF与convert参数检查
# 参数1:TNS连接串;参数2:db_create_file_dest参数值;参数3:db_file_name_convert参数值;参数4:log_file_name_convert参数值

check_omf_and_convert_params() {
    if [[ "$2" != ""  ]]; then
             printf "${NARMAL_COLOR}%-10s | Success | db_create_file_dest parameter was set${RESET_COLOR}\n" $1
    else
        if [[ "$3" != "" && "$4" != "" ]]; then
            printf "${NARMAL_COLOR}%-10s | Success | dbfile and logfile convert parameters were set${RESET_COLOR}\n" $1
        else
            printf "${WARNING_COLOR}%-10s | Warning | db_create_file_dest,dbfile and logfile convert parameters were set${RESET_COLOR}\n" $1
        fi
    fi
}

# 获取各类文件数 
# 参数1:TNS连接串,参数2:待查询视图名(V$LOG V$STANDBY_LOG V$TEMPFILE)

get_file_count() {
    if [[ "$1" != "" ]]; then
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
SELECT count(*) FROM V\$$2;
EOF
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! get_file_count() parameter 1 and 2 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 获取指定状态的redo文件数
# 参数1:TNS连接串

get_redo_status_count() {
    if [[ "$1" != "" ]]; then
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
SELECT count(*) FROM V\$LOG WHERE STATUS NOT IN ('UNUSED', 'CLEARING','CLEARING_CURRENT');
EOF
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! get_redo_status_count() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 检查指定状态的redo文件数
# 参数1:db_create_file_dest参数值;参数2:log_file_name_convert参数值;参数3:${STANDBY_TNS_DIC[redo_status_count]} 

check_redo_status_count() {
    # 如果设置了OMF或者LOG_FILE_NAME_CONVERT,则可以跳过此步骤
    if [[ "$1" != "" || "$2" != "" ]]; then
            printf "${NARMAL_COLOR}%-30s | %-8s | There is no need to check this item${RESET_COLOR}\n" redo_status_count Success
    else
        # 否则,指定状态的redo文件数应该为0
        if [[ "$3" -eq 0 ]]; then
            printf "${WARNING_COLOR}%-30s | %-8s | redo_status_count value is 0${RESET_COLOR}\n" redo_status_count Warning
        else
            # 否则,发出告警
            printf "${WARNING_COLOR}%-30s | %-8s | redo_status_count value is not 0${RESET_COLOR}\n" $1 Warning
        fi
    fi
}

# 获取online状态的数据文件数量
# 参数1:TNS连接串

get_datafile_online_count() {
    if [[ "$1" != "" ]]; then
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
SELECT count(*) FROM V\$DATAFILE WHERE STATUS='ONLINE';
EOF
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! get_datafile_online_count() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 获取数据库闪回功能开启状态
# 参数1:TNS连接串
get_flashback_on() {
    if [[ "$1" != "" ]]; then
        sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
SELECT FLASHBACK_ON FROM V\$DATABASE;
EOF
    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! get_flashback_on() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# mrp触发器检查
# 参数1:TNS连接串

check_mrp_trigger() {
    if [[ "$1" != "" ]]; then
        COUNT=$(
            sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
select count(*) FROM dba_triggers where trigger_name in ('AUTO_START_STANDBY_MRP','AUTO_START_STANDBY_MRP_PDB');
EOF
       )
        if [[ "$COUNT" -eq 0 ]]; then
            printf "${ERROR_COLOR}%-10s | %-8s | There is no MRP trigger${RESET_COLOR}\n" $1 Error
        else
            printf "${NARMAL_COLOR}%-10s | %-8s | MRP trigger was found${RESET_COLOR}\n" $1 Success
        fi
    else
        echo -e "${ERROR_COLOR}$(date "+%Y-%m-%d %H:%M:%S") | Error! check_mrp_trigger() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# mrp进程检查
# 参数1:TNS连接串

check_mrp_process() {
    if [[ "$1" != "" ]]; then
        COUNT=$(
            sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
select count(*) FROM v\$managed_standby where process like 'MRP%';
EOF
        )
        if [[ "$COUNT" -eq 0 ]]; then
            printf "${ERROR_COLOR}%-10s | %-8s | There is no MRP process${RESET_COLOR}\n" $1 Error
        else
            printf "${NARMAL_COLOR}%-10s | %-8s | MRP process was found${RESET_COLOR}\n" $1 Success
        fi

    else
        echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! check_mrp_process() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 检查从库延迟(transport lag,apply lag )
# 参数1:TNS连接串,参数2:待检查的lag类型

check_db_lag(){
if [[ "$1" != "" && "$2" != "" ]]	
then
DB_LAG=`sqlplus -s $DBUSER@$1 as sysdba << EOF 

set heading off feedback off pagesize 0 verify off echo off
select (extract(second from to_dsinterval(value)) + 
       extract(minute from to_dsinterval(value)) * 60 +
       extract(hour from to_dsinterval(value)) * 60 * 60 +
       extract(day from to_dsinterval(value)) * 60 * 60 * 24) as retvalue
from v\\$dataguard_stats 
where name = '$2';

EOF`

#去除左侧空格
DB_LAG=`eval echo $DB_LAG`

if [[ "$DB_LAG" -gt 30 ]]
then
    printf "${WARNING_COLOR}%-10s | %-8s | the $2 is too large($DB_LAG seconds)${RESET_COLOR}\n" $1 Warning
else 
    printf "${NARMAL_COLOR}%-10s | %-8s | the $2 is $DB_LAG second(s)${RESET_COLOR}\n" $1 Success
fi

else
    echo -e "${ERROR_COLOR} $(date "+%Y-%m-%d %H:%M:%S") | Error! check_db_lag() parameter 1 and 2 can't be null ${RESET_COLOR}"
    exit 1  
fi
}

# dblink检查
# 参数1:TNS连接串

check_dblink() {
    if [[ "$1" != "" ]]; then
        COUNT=$(
            sqlplus -s $DBUSER@$1 as sysdba <<EOF
set heading off feedback off pagesize 0 verify off echo off
select count(*) FROM dba_db_links;
EOF
       )
        if [[ "$COUNT" -eq 0 ]]; then
            printf "${NARMAL_COLOR}%-10s | %-8s | There is no dblink in our database${RESET_COLOR}\n" $1 Success
        else
            printf "${WARNING_COLOR}%-10s | %-8s | dblink was found,please check the tns file and firewall${RESET_COLOR}\n" $1 Warning
        fi
    else
        echo -e "${ERROR_COLOR}$(date "+%Y-%m-%d %H:%M:%S") | Error! check_mrp_trigger() parameter 1 can't be null ${RESET_COLOR}"
        exit 1
    fi
}

# 对比两个值是否相等
# 参数1:待比较值1,参数2:待比较值2
compare_value() {

    if [[ "$1" = "$2" ]]; then
        printf "${NARMAL_COLOR}%-30s | %-8s | The value is $1 ${RESET_COLOR}\n" $3 Success
    else
        printf "${WARNING_COLOR}%-30s | %-8s | The value 1 is: $1,but value 2 is: $2 ${RESET_COLOR}\n" $3 Warning
    fi
}


# 待判断参数值中是否包含给定的子字符串
# 参数1:TNS连接串;参数2:待判断参数名;参数3:待判断参数值;参数3:子字符串
# 例如:include_value $PRIMARY_TNS "fal_server" "${PRIMARY_TNS_DIC[fal_server]}" $STANDBY_TNS

include_value(){
    # 过滤结果
    cnt=`echo $3 | grep $4 | wc -l`

    if [[ "$cnt" -gt 0 ]]; then
        printf "${NARMAL_COLOR}%-10s | %-8s| %-20s | The value is $3,include $4 ${RESET_COLOR}\n" $1 Success $2
    else
        printf "${WARNING_COLOR}%-10s | %-8s| %-20s | The value is $3,doesn't include $4 ${RESET_COLOR}\n" $1 Warning $2
    fi
}

3. 执行效果

二、 执行切换

1. dg_switchover_v1.4.sh

#!/bin/bash

#**********************************************************************************
# Author: Hehuyi_In
# Date: 2022年06月10日
# FileName: dg_switchover_v1.4.sh
# 
# For sys user, execute the script directly. 
# For other sysdba privileged users, you need to manually synchronize the password file to standby server and chown file mode to 640 in versions below 12.2.
#
# Example: ./dg_switchover_v1.4.sh or ./dg_switchover_v1.4.sh &>> dg_switchover.log 
# Description: 
# 2022-06-10 v_1.0 only oracle dataguard switchover
# 2022-06-10 v_1.1 check instance status and db role after switchover.
# 2022-06-10 v_1.2 check transport lag,apply lag,switchover_status before switchover.
# 2022-06-13 v_1.3 add logging for main steps 
# 2022-06-14 v_1.4 auto check and select the target primary and standby database.
#**********************************************************************************

# 连接信息
DBUSER='sys/xxxxx'

# TNS列表,越靠前的被选为目标主库的概率较高
TNS_LIST=(mrptest mrptest_dg mrpuat)

# 列表元素个数检查,目前仅支持一主一从或一主两从
if [[ ${#TNS_LIST[@]} -lt 2 || ${#TNS_LIST[@]} -gt 3 ]]
then
	echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! The number of standby db is less than 1 or greater than 2."
fi

# ------------------------------------------------------------------------
# 判断 TNS_LIST 中每个连接串角色,并设置各切换目标参数
# 若为主库,则作为TARGET_STANDBY_TNS
# 若为从库,则检查lag。如果lag均为0,则作为TARGET_PRIMARY_TNS,剩余的从库作为OUTLOOKING_STANDBY_TNS
# 如果所有从库lag都大于0,则报错退出。

auto_set_target_db_tns(){
	
	for TNS in ${TNS_LIST[@]}
	do
	    # 获取TNS对应db角色
		DB_ROLE=`check_db_role $TNS`

		# 若为主库,则赋值给TARGET_STANDBY_TNS
		if [[ "$DB_ROLE" = "PRIMARY" ]]
		then
			TARGET_STANDBY_TNS=$TNS			
		# 若为从库
		elif [[ "$DB_ROLE" = "PHYSICAL STANDBY" ]]
		then
			    # 如果TARGET_PRIMARY_TNS不为空,说明已经设置过了目标主库,直接设置旁观从库即可
				if [[ "$TARGET_PRIMARY_TNS" != "" ]]
				then
					OUTLOOKING_STANDBY_TNS=$TNS				
				else 
				    # 若TARGET_PRIMARY_TNS为空,则需要再检查lag
					DB_TRAN_LAG=`check_db_lag $TNS "transport lag"`
					DB_APPLY_LAG=`check_db_lag $TNS "apply lag"`
			
				    # 如果lag均为0,则赋值给TARGET_PRIMARY_TNS
					if [[ "$DB_TRAN_LAG" -eq 0 && "$DB_APPLY_LAG" -eq 0 ]]
					then
						TARGET_PRIMARY_TNS=$TNS
					fi	
				fi
		fi
	done

    # 设置后检查,若TARGET_PRIMARY_TNS或TARGET_STANDBY_TNS为空,直接报错退出
	if [[ "$TARGET_PRIMARY_TNS" = "" || "$TARGET_STANDBY_TNS" = "" ]]	
    then
		echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! TARGET_PRIMARY_TNS and TARGET_STANDBY_TNS can't be null."
  		exit 1  		
	fi

}

# ------------------------------------------------------------------------
# 检查从库延迟(transport lag,apply lag )
# 参数1为db对应的tnsname,参数2为待检查的lag
check_db_lag(){
if [[ "$1" != "" && "$2" != "" ]]	
then
sqlplus -s $DBUSER@$1 as sysdba << EOF 
set heading off feedback off pagesize 0 verify off echo off
select (extract(second from to_dsinterval(value)) + 
       extract(minute from to_dsinterval(value)) * 60 +
       extract(hour from to_dsinterval(value)) * 60 * 60 +
       extract(day from to_dsinterval(value)) * 60 * 60 * 24) as retvalue
from v\$dataguard_stats 
where name = '$2';
EOF

else
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! parameter 1,2 can't be null."
    exit 1  
fi
}

# ------------------------------------------------------------------------

# 检查切换前switchover_status
# 参数1为db对应的tnsname,参数2为待检查的状态
check_switchover_status(){
if [[ "$1" != "" && "$2" != "" ]]	
then
STATUS=`sqlplus -s $DBUSER@$1 as sysdba << EOF 
set heading off feedback off pagesize 0 verify off echo off
select switchover_status from v\\$database;
EOF`

if [[ "$STATUS" = $2 || "$STATUS" = "SESSIONS ACTIVE" ]]
then
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Success! switchover_status of $1 is $STATUS."
else 
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! switchover_status of $1 is $STATUS (should be $2 or SESSIONS ACTIVE)."
    exit 1
fi

else
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! parameter 1 and 2 can't be null."
    exit 1  
fi
}

# ------------------------------------------------------------------------
# 检查db角色,参数1为db对应的tnsname

check_db_role(){
if [[ "$1" != "" ]]	
then
sqlplus -s $DBUSER@$1 as sysdba << EOF 

set heading off feedback off pagesize 0 verify off echo off
select database_role from v\$database;
EOF

else
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! parameter 1 should be tnsname."
    exit 1  
fi
}

# ------------------------------------------------------------------------
# 检查切换后实例状态
# 参数1为db对应的tnsname

check_instance_status(){
if [[ "$1" != "" ]]	
then
STATUS=`sqlplus -s $DBUSER@$1 as sysdba << EOF 

set heading off feedback off pagesize 0 verify off echo off
select status from v\\$instance;

EOF`

if [[ "$STATUS" != "OPEN" ]]
then
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! instance status of $1 is $STATUS after switchover."
    exit 1
else 
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Success! instance status of $1 is $STATUS after switchover."
fi

else
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! parameter 1 can't be null."
    exit 1  
fi
}

# ------------------------------- main() -----------------------------------------

echo "------------------------------------------------------------------------"
echo "`date "+%Y-%m-%d %H:%M:%S"` | Precheck before switchover..."

# 判断 TNS_LIST 中每个连接串角色,并设置各切换目标参数
auto_set_target_db_tns

# 设置后各目标参数值
echo "`date "+%Y-%m-%d %H:%M:%S"` | TARGET_PRIMARY_TNS: $TARGET_PRIMARY_TNS"
echo "`date "+%Y-%m-%d %H:%M:%S"` | TARGET_STANDBY_TNS: $TARGET_STANDBY_TNS"
echo "`date "+%Y-%m-%d %H:%M:%S"` | OUTLOOKING_STANDBY_TNS: $OUTLOOKING_STANDBY_TNS"

# 切换前switchover_status检查
check_switchover_status $TARGET_STANDBY_TNS "TO STANDBY"

# 目标从库(原主库)执行:将原主库转切为从库
echo "`date "+%Y-%m-%d %H:%M:%S"` | Begin switchover $TARGET_STANDBY_TNS to standby database..."
    
sqlplus -s $DBUSER@$TARGET_STANDBY_TNS as sysdba << EOF 
alter database commit to switchover to standby with session shutdown;
shutdown immediate
EOF

echo "`date "+%Y-%m-%d %H:%M:%S"` | Restarting $TARGET_STANDBY_TNS database..."

# 重新启动目标从库
sqlplus -s $DBUSER@$TARGET_STANDBY_TNS as sysdba << EOF 
startup
EOF

# 切换后角色检查
DB_ROLE=`check_db_role $TARGET_STANDBY_TNS`

if [[ "$DB_ROLE" != "PHYSICAL STANDBY" ]]; 
then
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! db role of $TARGET_STANDBY_TNS is $DB_ROLE(should be PHYSICAL STANDBY) after switchover."
    exit 1
else 
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Success! db role of $TARGET_STANDBY_TNS is $DB_ROLE after switchover."
fi

# 切换后实例状态检查
check_instance_status $TARGET_STANDBY_TNS

echo "`date "+%Y-%m-%d %H:%M:%S"` | Finish switchover $TARGET_STANDBY_TNS to standby database."

# ------------------------------------------------------------------------

echo "`date "+%Y-%m-%d %H:%M:%S"` | Precheck before switchover $TARGET_PRIMARY_TNS to primary database..."

# 切换前switchover_status检查
check_switchover_status $TARGET_PRIMARY_TNS "TO PRIMARY"

# 目标主库(原从库)执行
# 将原从库切为主库

echo "`date "+%Y-%m-%d %H:%M:%S"` | Begin switchover $TARGET_PRIMARY_TNS to primary database..."

sqlplus -s $DBUSER@$TARGET_PRIMARY_TNS as sysdba << EOF 
alter database commit to switchover to primary with session shutdown;
ALTER DATABASE OPEN;
EOF

# 切换后角色检查
DB_ROLE=`check_db_role $TARGET_PRIMARY_TNS`

if [[ "$DB_ROLE" != "PRIMARY" ]]; 
then
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Error! db role of $1 is $DB_ROLE(should be PRIMARY) after switchover."
    exit 1
else 
    echo "`date "+%Y-%m-%d %H:%M:%S"` | Success! db role of $1 is $DB_ROLE after switchover."
fi

# 切换后实例状态检查
check_instance_status $TARGET_PRIMARY_TNS

# 切换后从库lag检查
DB_TRAN_LAG=`check_db_lag $TARGET_STANDBY_TNS "transport lag"`
DB_APPLY_LAG=`check_db_lag $TARGET_STANDBY_TNS "apply lag"`

echo "`date "+%Y-%m-%d %H:%M:%S"` | After switchover: DB_TRAN_LAG is $DB_TRAN_LAG second(s), DB_APPLY_LAG is $DB_APPLY_LAG second(s)"

echo "`date "+%Y-%m-%d %H:%M:%S"` | Finish switchover $TARGET_PRIMARY_TNS to primary database."

# ------------------------------------------------------------------------
# 如果有旁观从库
if [ "$OUTLOOKING_STANDBY_TNS" != "" ]; then

echo "`date "+%Y-%m-%d %H:%M:%S"` | Begin onlooking standby database setting..."

# 目标主库调整参数,指向旁观从库,先清空再设置,否则有可能报参数冲突
sqlplus -s $DBUSER@$TARGET_PRIMARY_TNS as sysdba << EOF 
alter system set LOG_ARCHIVE_DEST_2='';
alter system set LOG_ARCHIVE_DEST_3='';
alter system set LOG_ARCHIVE_DEST_2='SERVICE=$TARGET_STANDBY_TNS ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=$TARGET_STANDBY_TNS';
alter system set LOG_ARCHIVE_DEST_3='SERVICE=$OUTLOOKING_STANDBY_TNS ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=$OUTLOOKING_STANDBY_TNS';
EOF

# 目标从库调整参数,断开到旁观从库的连接
sqlplus -s $DBUSER@$TARGET_STANDBY_TNS as sysdba << EOF 
alter system set LOG_ARCHIVE_DEST_3='';
alter system set LOG_ARCHIVE_DEST_2='SERVICE=$TARGET_PRIMARY_TNS ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=$TARGET_PRIMARY_TNS';
EOF

# 旁观从库调整参数,指向新主库
sqlplus -s $DBUSER@$OUTLOOKING_STANDBY_TNS as sysdba  << EOF 
alter system set LOG_ARCHIVE_DEST_3='';
alter system set LOG_ARCHIVE_DEST_2='SERVICE=$TARGET_PRIMARY_TNS ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=$TARGET_PRIMARY_TNS';
EOF

# 切换后旁观从库lag检查
DB_TRAN_LAG=`check_db_lag $OUTLOOKING_STANDBY_TNS "transport lag"`
DB_APPLY_LAG=`check_db_lag $OUTLOOKING_STANDBY_TNS "apply lag"`

echo "`date "+%Y-%m-%d %H:%M:%S"` | After setting: DB_TRAN_LAG is $DB_TRAN_LAG second(s), DB_APPLY_LAG is $DB_APPLY_LAG second(s)"

echo "`date "+%Y-%m-%d %H:%M:%S"` | Finish onlooking standby database setting."

else # 没有旁观从库
echo "`date "+%Y-%m-%d %H:%M:%S"` | There is no onlooking standby database."
fi

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1515535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue.js+SpringBoot开发考研专业课程管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 考研高校模块2.3 高校教师管理模块2.4 考研专业模块2.5 考研政策模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 考研高校表3.2.2 高校教师表3.2.3 考研专业表3.2.4 考研政策表 四、系统展示五、核…

java数据结构与算法刷题-----LeetCode491. 非递减子序列

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路&#xff1a;时间复杂度O( n 2 ∗ n n^2*n n2∗n),空间复…

COSCUP 2024 正式启动议题征集,开源社专属邀请通道开启,欢迎报名参加!

COSCUP 是由台湾开放原始码社群联合推动的年度研讨会&#xff0c;起源于 2006 年&#xff0c;是台湾自由软体运动 (FOSSM) 重要的推动者之一。活动包括有讲座、摊位、社团同乐会等&#xff0c;除了邀请国际的重量级演讲者之外&#xff0c;台湾本土的自由软体推动者也经常在此发…

深入学习React开发:从基础到实战

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 引言 React是一款流行的JavaScript库&#xf…

基于R语言piecewiseSEM结构方程模型在生态环境领域技术教程

原文链接&#xff1a;基于R语言piecewiseSEM结构方程模型在生态环境领域技术应用https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247597092&idx7&sn176695e746eccff68e04edda6521f131&chksmfa823dc3cdf5b4d5b77181eb1bd9a2d659ff38e23c7ea78d33bc1cc7d0…

HSE化工应急安全生产管理平台:衢州某巨大型化工企业的成功应用

在化工行业中&#xff0c;安全生产一直是至关重要的议题。为了提高生产安全性、降低成本并提升企业形象&#xff0c;衢州某巨大型化工企业引入了HSE化工应急安全生产管理平台&#xff0c;取得了显著的改善和获益。 该平台的核心功能包括风险管理和应急预案制定。通过对化工生产…

活动图高阶讲解-03

1 00:00:00,000 --> 00:00:06,260 刚才我们讲了活动图的历史 2 00:00:06,260 --> 00:00:11,460 那我们来看这个活动图 3 00:00:11,460 --> 00:00:15,260 如果用来建模的话怎么用 4 00:00:15,260 --> 00:00:20,100 按照我们前面讲的软件方法的工作流 5 00:00:20…

vite ts vue 项目提示 . Projects must list all files or use an include pattern.

vite ts vue 项目提示 . Projects must list all files or use an include pattern. 在引用一个 ts 的时候&#xff0c;提示如下&#xff1a; 需要在 tsconfig.node.json 文件中添加&#xff1a; {"compilerOptions": {"composite": true,"skipLibC…

微信小程序一次性订阅requestSubscribeMessage授权和操作详解

一次性订阅&#xff1a;用户订阅一次发一次通知 一、授权 — requestSubscribeMessage Taro.requestSubscribeMessage({tmplIds: [], // 需要订阅的消息模板的id的集合success (res) {console.log("同意授权", res)},fail(res) {console.log(拒绝授权, res)}})点击或…

拼图小游戏制作教程:用HTML5和JavaScript打造经典游戏

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

图片太大上传不了怎么调整?教你快速解决的办法

在日常工作学习中&#xff0c;经常有用到证件照的地方&#xff0c;比如考试报名证件照&#xff0c;办理各种证件、开学入学照、护照等等&#xff0c;有时候会因为证件照文件太大了、尺寸不符合导致上传不成功&#xff0c;今天就来教大家几个压缩图片大小的方法。 关于图片太大上…

C#Winform中DataBinding实现数据绑定实例

本文实例演示Winform中如何使用DataBinding实现数据绑定。 在我们实例开发中,经常会出现一个数据需要在多个界面中显示和使用。这时如果使用常规方法更新显示时,每个控件得更新都要在程序中体现,比较麻烦还容易遗漏。更好用的方法是使用DataBinding属性,常用的控件是label…

COOH-PEG-Galactose 羧基-聚乙二醇-半乳糖 Galactose 靶向肝肿瘤细胞

在生物体内&#xff0c;正常细胞通过有氧呼吸将糖类等物质分解代谢产生能量&#xff0c;从而供给细胞的增殖和生 长。而癌细胞似乎更为“蛮横”&#xff0c;它们主要依靠糖酵解作用为生&#xff0c;因此癌细胞代谢葡萄糖的速度比正 常细胞要快得多。值得注意的是&#xff0c;…

5.Python从入门到精通—Python 运算符

5.Python从入门到精通—Python 运算符 Python 运算符算术运算符比较&#xff08;关系&#xff09;运算符赋值运算符逻辑运算符位运算符成员运算符身份运算符运算符优先级 Python 运算符 Python语言支持以下类型的运算符: 算术运算符比较&#xff08;关系&#xff09;运算符赋…

【网络安全】手机不幸被远程监控,该如何破解,如何预防?

手机如果不幸被远程监控了&#xff0c;用三招就可以轻松破解&#xff0c;再用三招可以防范于未然。 三招可破解可解除手机被远程监控 1、恢复出厂设置 这一招是手机解决软件故障和系统故障的终极大招。只要点了恢复出厂设置&#xff0c;你手机里后装的各种APP全部将灰飞烟灭…

基于Spring boot+Vue的校园社团管理系统

目录 一、 绪论1.1 开发背景1.2 开发技术1.2.1 Spring Boot 框架1.2.2 Vue.js 框架 1.3 开发环境1.3.1 IntelliJ IDEA1.3.2 MySQL 二、系统分析2.1需求分析2.1.1管理员需求分析2.1.2社员需求法分析2.1.3社长管理员功能需求 2.2用例分析2.2.1管理员用例分析2.2.2社员用例分析2.2…

已经会用stm32做各种小东西了,下一步学什么,研究stm32的内部吗?

今天看到了一个提问&#xff0c;原话如下&#xff1a; 这个问题&#xff0c;我能装逼。 曾经干了10年单片机工程师&#xff0c;对工程师从入门&#xff0c;到入行&#xff0c;再到普通&#xff0c;再到高级&#xff0c;整个路径还算清晰&#xff0c;比如什么阶段&#xff0c;会…

一键导入Figma,让团队文件管理更加便捷安全!

如何将Figma引入国内软件已成为人们关注的话题。本文将分享两种Figma导入方法&#xff0c;使您的设计文件更加安全。 两种方法&#xff0c;一键导入Figma文件 即时设计是一种基于云的设计工具&#xff0c;在功能和特性上与Figma非常相似。如果你熟悉Figma的界面&#xff0c;即…

GPT-4.5 Turbo:意外曝光且可能在六月份推出

国外网络媒体THE DECODER的联合创始人兼出版人Matthias认为&#xff0c;人工智能技术将彻底改变人类和计算机的互动方式。最新消息显示&#xff0c;OpenAI的最新力作GPT-4.5 Turbo已经在网络上意外曝光。首批发现此信息的是Bing和DuckDuck Go等搜索引擎&#xff0c;它们在官方发…

探索Java高并发编程之道:理论与实践

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 简介 随着互联网和信息技术的快速发展&#x…