SSJ Tech Lab - Oracle Database テクニカルラボ

シェル-RACコールドバックアップ

対応バージョン

Oracle Database 10.2 - 11.2

2015-06-01記事公開

よく使用する便利ツールの公開です。
※ カスタマイズなどのご要望がございましたら、お見積りいたしますので、弊社までお問い合わせください。

シェル概要

RAC環境にて指定したデータベースのコールドバックアップを取得します。
シングルインスタンス環境では動作しません。

テスト環境

OS: Oracle Linux 6
Database: Oracle Database 11cR2 Real Application Cluseter

使用方法

rootユーザにて以下のように実行します。(rootユーザ又はsudoにて実行)

$ rac_backup_rmancold.sh [引数1:バックアップ対象DB]

実行例

# su -
# chmod 755 /tmp/rac_backup_rmancold.sh
# /tmp/rac_backup_rmancold.sh orcl

シェル内容

以下に rac_backup_rmancold.sh シェルの内容を示します。


#!/bin/bash

# Copyright 2015 SIX SQUARE JAPAN.co.,ltd. All Rights Reserved.
# 本著作物は例を示したサンプルであり、資料の内容を利用した場合について、
# 弊社が一切の責任を負うものではありません。

# Version:
#   Ver 1.0 - 2015/06/01 - 新規作成
# Description:
#   引数に与えられたOracle RAC データベースのコールドバックアップを取得する。
#   データベースの起動・停止を行う。
#   実行はrootユーザまたはsudoにて実行を行う。
# Usage:
#   parameter1: バックアップ対象DBの DB_NAME
###############################################################################

# SYSDBA権限のグループをもつOSユーザ
SYSDBA_OSUSER=oracle
# レポートファイルの出力先定義
BACKUP_DIRECOTRY=/mnt/dbbackup/oraclerman
# バックアップ世代数(手動管理)
BACKUP_RETENTION=1
# RMAN実行チャネルの数(パラレル度)
NUM_PARALLEL=16

ORACLE_BASE=""
ORACLE_HOME=""

NORMAL_CODE=0
ERROR_CODE=1

# バックアップオプション [ON/OFF]
BACKUP_SET_OPTION_COMPRESS=ON
BACKUP_SET_OPTION_ENCRYPTION=ON

# SYSDBA_OSUSER に設定したユーザの.bash_profile読み込み
L_ORACLE_BASH_PROFILE=`eval ls -1 ~${SYSDBA_OSUSER}/.bash_profile`
if [ -f ${L_ORACLE_BASH_PROFILE} ]; then
  source ${L_ORACLE_BASH_PROFILE}
fi

export LANG=C
export NLS_LANG=American_America.AL32UTF8

# ORACLE_BASEが空の場合、.bash_profileから取得
if [ -z "${ORACLE_BASE}" ]; then
  ORACLE_BASE=`grep "ORACLE_BASE=" ${L_ORACLE_BASH_PROFILE} | grep -v "^[       ]*#" \
               | cut -d'=' -f2- | sed -e 's@#.*$@@'`
  # ORACLE_BASEが取得できない場合は必須でないためそのまま実行
  if [ -z "${ORACLE_BASE}" ]; then
    echo "`date '+%Y/%m/%d %H:%M:%S'` Warning: ORACLE_BASE is NULL"
  fi
fi

# ORACLE_HOMEが空の場合、.bash_profileから取得
if [ -z "${ORACLE_HOME}" ]; then
  ORACLE_HOME=`grep "ORACLE_HOME=" ${L_ORACLE_BASH_PROFILE} | grep -v "^[       ]*#" \
               | cut -d'=' -f2- | sed -e 's@#.*$@@'`
  if [ -z "${ORACLE_HOME}" ]; then
    # ORACLE_HOMEが取得できない場合は異常終了
    echo "ORACLE_HOME of environment variable Not found."
    exit ${ERROR_CODE}
  fi
fi

export ORACLE_BASE ORACLE_HOME

PATH=${ORACLE_HOME}/bin:${PATH}:/usr/bin:/usr/sbin:/bin:/sbin
export PATH

echo "`date '+%Y/%m/%d %H:%M:%S'` Started shell[`basename $0`]"
# ユーザの確認
case "${USER}" in
  "root" )
    echo "`date '+%Y/%m/%d %H:%M:%S'` User is ${USER}. [Pass]"
  ;;
  * )
    echo "`date '+%Y/%m/%d %H:%M:%S'` User is ${USER}. Switch user to [root]."
    exit ${ERROR_CODE}
  ;;
esac

# 引数の数のチェック
if [ $# -ne 1 ]; then
  echo "Usage: $0 [Backup DB Name]"
  echo "   ex: $0 orcl"
  exit ${ERROR_CODE}
fi

# 第一引数に指定された値をバックアップ対象のDBとして設定
BKDB=$1

# RMANファイルのバックアップディレクトリ設定
L_BACKUP_DIRECOTRY_RMANFILE=${BACKUP_DIRECOTRY}/${BKDB}_cold

# Gridが動作しているORACLE_HOMEを特定
GRID_OCSSDBIN=`ps -ef | awk '{print $8}' | grep "/ocssd.bin$" | head -1`
export PATH=`dirname ${GRID_OCSSDBIN}`:${PATH}

# シェル実行ノードのデータベースのステータスを取得
L_DB_STATUS=`srvctl status database -d ${BKDB} | grep " \`uname -n | cut -d'.' -f1\`$"`

if [ "${L_DB_STATUS}" = "" ]; then
  # シェル実行ノードで稼働しているインスタンスが取得できなかった場合
  echo "`date '+%Y/%m/%d %H:%M:%S'` Instance was not exists. [${BKDB} on `uname -n | cut -d'.' -f1`]"
  exit ${ERROR_CODE}
fi

# インスタンス名を取得
L_TGT_INSTANCE=`echo "${L_DB_STATUS}" | awk '{print $2}'`

# 古いバックアップの削除
if [ -d ${L_BACKUP_DIRECOTRY_RMANFILE} ]; then
  # バックアップディレクトリ付与番号最大値の取得
  L_NUM_DIRECTORY_MAX=`expr ${BACKUP_RETENTION} - 1`
  for L_NUM_DIRECTORY in `ls -1d ${L_BACKUP_DIRECOTRY_RMANFILE}.[0-9]* 2>/dev/null \
                          | sed -e "s@${L_BACKUP_DIRECOTRY_RMANFILE}.@@" | sort -r`
  do
    # 取得したディレクトリ番号の数値チェック
    echo "${L_NUM_DIRECTORY}" | grep -q "[0-9]*[0-9]"
    if [ $? -ne 0 ]; then
      # 取得したディレクトリ番号が数値以外の場合
      echo "[${L_NUM_DIRECTORY}] is not numeric data."
    else
      # 決められたフォーマットのものはローテーションを行う
      if [ ${L_NUM_DIRECTORY} -ge ${L_NUM_DIRECTORY_MAX} ]; then
        # 取得したディレクトリ番号が世代最大値より大きいか等しい場合
        echo "`date '+%Y/%m/%d %H:%M:%S'` Remove [${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY}]"
        rm -rf ${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY}
      else
        # 取得したディレクトリ番号が世代最大値より小さい場合
        L_NUM_DIRECTORY_MOVE=`expr ${L_NUM_DIRECTORY} + 1`
        echo -n "`date '+%Y/%m/%d %H:%M:%S'` Move [${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY}] to"
        echo    " [${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY_MOVE}]"
        mv -f ${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY} ${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY_MOVE}
      fi

      # ローテーション対象のディレクトリが残存していた場合
      if [ -d ${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY} ]; then
        echo "`date '+%Y/%m/%d %H:%M:%S'` Remove directory failed. [${L_BACKUP_DIRECOTRY_RMANFILE}.${L_NUM_DIRECTORY}]"
        exit ${ERROR_CODE}
      fi
    fi
  done

  # 前回バックアップの処理
  if [ ${L_NUM_DIRECTORY_MAX} -ge 1 ]; then
    # 最大ローテーション番号が1より大きい場合(二世代以上)は、前回バックアップを .1 に変更
    echo "`date '+%Y/%m/%d %H:%M:%S'` Move [${L_BACKUP_DIRECOTRY_RMANFILE}] to [${L_BACKUP_DIRECOTRY_RMANFILE}.1]"
    mv -f ${L_BACKUP_DIRECOTRY_RMANFILE} ${L_BACKUP_DIRECOTRY_RMANFILE}.1
  else
    # 1世代保存の場合、前回のバックアップを削除
    echo "`date '+%Y/%m/%d %H:%M:%S'` Remove [${L_BACKUP_DIRECOTRY_RMANFILE}]"
    rm -rf ${L_BACKUP_DIRECOTRY_RMANFILE}
  fi
  # ローテーション処理のチェック
  if [ -d ${L_BACKUP_DIRECOTRY_RMANFILE} ]; then
    echo "`date '+%Y/%m/%d %H:%M:%S'` Remove directory failed. [${L_BACKUP_DIRECOTRY_RMANFILE}]"
    exit ${ERROR_CODE}
  fi
fi

# データベースをマウントモードへ変更
echo "`date '+%Y/%m/%d %H:%M:%S'` Stop database [${BKDB}]"
L_DB_STATUS=`su - ${SYSDBA_OSUSER} -c "export ORACLE_HOME=${ORACLE_HOME}; ${ORACLE_HOME}/bin/srvctl stop database -d ${BKDB}"`
echo "`date '+%Y/%m/%d %H:%M:%S'` Mount database [${BKDB}]"
L_DB_STATUS=`su - ${SYSDBA_OSUSER} -c "export ORACLE_HOME=${ORACLE_HOME}; \
             ${ORACLE_HOME}/bin/srvctl start instance -d ${BKDB} -i ${L_TGT_INSTANCE} -o mount"`

# データベースのオープン状態を取得
L_MNTSTATUS=`su - ${SYSDBA_OSUSER} -c "export LANG=C; export NLS_LANG=American_America.AL32UTF8; \
             export ORACLE_HOME=${ORACLE_HOME}; export ORACLE_SID=${L_TGT_INSTANCE}; \
             ${ORACLE_HOME}/bin/sqlplus -s -L / as sysdba << SQL_EOF
  whenever sqlerror exit 2
  whenever oserror exit 2
  set pages 0 line 1200 trim on trims on feed off heading off timing off verify off
  select OPEN_MODE from v\\\\\\\$database;
  exit
SQL_EOF"`

echo "`date '+%Y/%m/%d %H:%M:%S'` The status of database:${BKDB} is [${L_MNTSTATUS}]"

# データベースのオープン状態がコールドバックアップの前提であるマウントモードになっていることの確認
if [ "${L_MNTSTATUS}" != "MOUNTED" ]; then
  echo "`date '+%Y/%m/%d %H:%M:%S'` Status of database[${BKDB}] is not MOUNTED. Change status was failed."
  exit ${ERROR_CODE}
fi

# バックアップディレクトリの作成
mkdir -m777 ${L_BACKUP_DIRECOTRY_RMANFILE}
if [ ! -d ${L_BACKUP_DIRECOTRY_RMANFILE} ]; then
  echo "`date '+%Y/%m/%d %H:%M:%S'` mkdir failed.[${L_BACKUP_DIRECOTRY_RMANFILE}]"
  exit ${ERROR_CODE}
fi

# 指定されたパラレル度に応じて、チャネル宣言の生成
for  L_NUM_CH in `seq 1 ${NUM_PARALLEL}`
do
  L_ALLOCATE_CHANNEL="  ALLOCATE CHANNEL ch${L_NUM_CH} DEVICE TYPE DISK FORMAT '${L_BACKUP_DIRECOTRY_RMANFILE}/DATA_%d_DBID%I_%U';"
  L_ALLOCATE_CHANNELS=`printf "%s\n%s" "${L_ALLOCATE_CHANNELS}" "${L_ALLOCATE_CHANNEL}"`
done

# 圧縮オプションがONの場合は圧縮処理を付与
if [ "${BACKUP_SET_OPTION_COMPRESS}" == "ON" ]; then
  L_BACKUP_SET_OPTION=`printf "%s\n%s" "${L_BACKUP_SET_OPTION}" "SET COMPRESSION ALGORITHM 'LOW';"`
  L_BACKUP_FTYPE="AS COMPRESSED BACKUPSET"
else
  L_BACKUP_FTYPE="AS BACKUPSET"
fi

# 暗号化オプションがONの場合は暗号化処理を付与
if [ "${BACKUP_SET_OPTION_ENCRYPTION}" == "ON" ]; then
  L_BACKUP_SET_OPTION=`printf "%s\n%s" "${L_BACKUP_SET_OPTION}" "SET ENCRYPTION ALGORITHM 'AES128' ON;"`
fi

# RMAN コマンドファイル生成
L_RMANCMDFILE=${L_BACKUP_DIRECOTRY_RMANFILE}/rman_`date '+%Y%m%d%H%M%S'`.cmd

echo "CROSSCHECK BACKUP;"                                      >> ${L_RMANCMDFILE}
echo "DELETE NOPROMPT EXPIRED BACKUP;"                         >> ${L_RMANCMDFILE}
echo "${L_BACKUP_SET_OPTION}"                                  >> ${L_RMANCMDFILE}
echo "run {"                                                   >> ${L_RMANCMDFILE}
echo "  ${L_ALLOCATE_CHANNELS}"                                >> ${L_RMANCMDFILE}
echo "  backup ${L_BACKUP_FTYPE} database SECTION SIZE 50G;"   >> ${L_RMANCMDFILE}
echo "}"                                                       >> ${L_RMANCMDFILE}
echo -n "backup AS BACKUPSET DEVICE TYPE DISK FORMAT "         >> ${L_RMANCMDFILE}
echo "'${L_BACKUP_DIRECOTRY_RMANFILE}/SPFL_%d_DBID%I_%U' spfile;" >> ${L_RMANCMDFILE}
echo -n "backup AS BACKUPSET DEVICE TYPE DISK FORMAT "         >> ${L_RMANCMDFILE}
echo "'${L_BACKUP_DIRECOTRY_RMANFILE}/CTRL_%d_DBID%I_%U' current controlfile;" >> ${L_RMANCMDFILE}
echo "exit"                                                    >> ${L_RMANCMDFILE}

chmod 666 ${L_RMANCMDFILE}

# RMAN コマンドファイルの作成確認
if [ ! -f "${L_RMANCMDFILE}" ]; then
  echo "`date '+%Y/%m/%d %H:%M:%S'` Create rman command file failed.[${L_RMANCMDFILE}]"
  exit ${ERROR_CODE}
fi

# RMAN コールドバックアップの実行
su - ${SYSDBA_OSUSER} -c "export NLS_DATE_FORMAT='YYYY/MM/DD HH24:MI:SS'; export ORACLE_HOME=${ORACLE_HOME}; \
                          export ORACLE_SID=${L_TGT_INSTANCE}; \
                          ${ORACLE_HOME}/bin/rman target / nocatalog @'${L_RMANCMDFILE}'" \
| tee -a ${L_BACKUP_DIRECOTRY_RMANFILE}/rman_`date '+%Y%m%d%H%M%S'`.log

echo "`date '+%Y/%m/%d %H:%M:%S'` Ended backup database[${BKDB}]"

# バックアップ後データベースの再起動
echo "`date '+%Y/%m/%d %H:%M:%S'` Stop database [${BKDB}]"
L_DB_STATUS=`su - ${SYSDBA_OSUSER} -c "export ORACLE_HOME=${ORACLE_HOME}; ${ORACLE_HOME}/bin/srvctl stop database -d ${BKDB}"`

echo "`date '+%Y/%m/%d %H:%M:%S'` Start database [${BKDB}]"
L_DB_STATUS=`su - ${SYSDBA_OSUSER} -c "export ORACLE_HOME=${ORACLE_HOME}; ${ORACLE_HOME}/bin/srvctl start database -d ${BKDB}"`

# データベース再起動後のステータス確認
srvctl status database -d ${BKDB} 2>&1 | grep -qv "is running on node"
L_DBSTATUS=$?
if [ ${L_DBSTATUS} -ne 1 ]; then
  echo "`date '+%Y/%m/%d %H:%M:%S'` Status of database was wrong.[${BKDB}]"
  srvctl status database -d ${BKDB}
  exit ${ERROR_CODE}
fi

exit ${NORMAL_CODE}

関連記事

関連記事が存在しません