CSS for code highlight

02 июня 2022

Скрипт восстановления базы данных Oracle на определенное время

Иногда приходится восстанавливать базу данных на какое-то конкретное время, делать это вручную весьма рутинно и скучно, поэтому решил таки автоматизировать и написать скриптик. Сразу скажу что rman'ом не пользуюсь, т.к. ну не dba я, потому просто и не довелось его освоить (буду исправляться! :)), на все нет времени, много других задач. Ежедневно делаю холодные копии и пакую архивлоги, обычно хватает холодных копий, но иногда все же требуется восстанавливать на конкретное время, поэтому восстанавливаю требуемую холодную копию/архивлоги/контроллы и используя sqlplus накатываю архивлоги. Скрипт аналогично сам находит требуемые копии, распаковывает, накатывает архивлоги, теперь вместо хх часов, буду тратить порядка 2 минут на запуск скрипта с нужными параметрами, ну если все резервные копии есть и хранятся в необходимом порядке, иначе скрипт выдаст ошибку и придется продолжить вручную...

Скрипт написан на bash. Писался и отлаживался на онлайн платформе onlinegdb.com. Для проверки скрипта была создана структура папок и файлов как на рабочем сервере (см. комментарии в скрипте), так же написаны функции-заглушки для sqlplus и sendmail. Потом слегка доделал скрипт после пары запусков на живом сервере.

PS: скрипт все же немного сырой, он не сработает если нужно восстановить в течении текущего дня, надо будет это исправить и скорее всего как-то надо его декомпозировать и сделать возможность пропуска каких-то этапов.

Ссылка на скрипт на onlinegdb.com

#!/bin/bash
##################################################
# recover-db-untiltime v1.3 #
# #
# Скрипт восстановления инстанса Oracle на дату #
# сохраняет текущее состояние БД, #
# находит холодный бекап и разворачивает его, #
# находит и разворачивает архивлоги, #
# накатывает архивлоги до требуемой даты. #
# автор: Захаров Сергей #
# email: s.a.zakharov@gmail.com #
# https://developer-core.blogspot.com/ #
##################################################
##################################################
# Настройки скрипта: #
# #
# имя инстанса
ORACLE_SID='KTSR19'
# дата на которую требуется восстановить инстанс
RECOVERY_DATE='2022-05-23 15:37:00'
EMAIL='admin@email.com'
SENDMAIL='/usr/sbin/sendmail'
# путь к инстансу БД
DB_PATH='/oradata/KTSR'
# путь резервного копирования базы данных
DB_BCK_PATH="$DB_PATH/backup"
# путь резервного копирования текущей базы данных
CUR_DB_BCK_PATH='/vol/data1/db-backups/current'
LOG_FILE="$DB_BCK_PATH/recovery.log"
# #
##################################################
##################################################
# подготовка тестового окружения для скрипта #
# удалить из рабочего скрипта!! #
rootfs='/dev/shm'
DB_BCK_PATH="${rootfs}${DB_BCK_PATH}"
DB_PATH="${rootfs}${DB_PATH}"
CUR_DB_BCK_PATH="${rootfs}${CUR_DB_BCK_PATH}"
LOG_FILE="${rootfs}${LOG_FILE}"
mkdir -p $DB_PATH
mkdir -p $DB_PATH/archive
touch $DB_PATH/archive/1_00001.dbf
mkdir -p $DB_PATH/ctl
touch $DB_PATH/ctl/control01.ctl
mkdir -p $DB_PATH/data
touch $DB_PATH/data/data.dbf
mkdir -p $DB_PATH/idx
touch $DB_PATH/idx/index.dbf
mkdir -p $DB_PATH/redo
touch $DB_PATH/redo/redo.dbf
mkdir -p $DB_PATH/undo
touch $DB_PATH/undo/undo.dbf
mkdir -p $DB_PATH/temp
touch $DB_PATH/temp/undo.dbf
mkdir -p $DB_PATH/replicate
mkdir -p $CUR_DB_BCK_PATH
mkdir -p $DB_BCK_PATH/202205210357.KTSR19.cold
mkdir -p $DB_BCK_PATH/202205220357.KTSR19.cold
mkdir -p $DB_BCK_PATH/202205230357.KTSR19.cold
mkdir -p $DB_BCK_PATH/202205240357.KTSR19.cold
mkdir -p $DB_BCK_PATH/202205250357.KTSR19.cold
mkdir -p $DB_BCK_PATH/202205260357.KTSR19.cold
mkdir -p $DB_BCK_PATH/202205270357.KTSR19.cold
mkdir -p $DB_BCK_PATH/KTSR19.cold
mkdir -p $DB_BCK_PATH/some_dir
touch $DB_BCK_PATH/ktsr19.base.[20200811].before_pay_st_close.tar.gz
tar -cv --exclude=$DB_PATH/backup* $DB_PATH 2>/dev/null | bzip2 -c1 > $DB_BCK_PATH/202205230357.KTSR19.cold/202205230357.KTSR19.base.tar.bz2
tar -cv $DB_PATH/archive 2>/dev/null | bzip2 -c1 > $DB_BCK_PATH/202205240357.KTSR19.cold/202205240357.KTSR19.archive.tar.bz2
## просто заглушки, чтобы скрипт работал
sqlplus(){
echo "sqlplus running with"
echo "(arguments): $@"
echo "(stdin):"
STDIN=`cat /dev/stdin`
echo "$STDIN"
}
sendmail(){
echo "sendmail running with"
echo "(arguments): $@"
echo "(stdin):"
STDIN=`cat /dev/stdin`
echo "$STDIN"
}
SENDMAIL='sendmail'
# #
##################################################
##################################################
exit_on_error(){
echo >> $LOG_FILE
echo "$1" >> $LOG_FILE
send_message
exit 1
}
send_message(){
dt_finish_script=$( date +%s )
echo >> $LOG_FILE
echo '------------------------------------------------------------' >> $LOG_FILE
echo "Script execution time: $(( ($dt_finish_script - $dt_start_script) /60 )) minutes" >> $LOG_FILE
$SENDMAIL -O DefaultCharSet=koi8-r -FServer_$ORACLE_SID $EMAIL <<END
To: $EMAIL
Subject: $ORACLE_SID - recovering database to $RECOVERY_DATE
X-Priority: 1
X-MSMail-Priority: High
$ORACLE_SID - recovering database to $RECOVERY_DATE complete
============================================================
$( cat $LOG_FILE )
============================================================
.
END
}
dt_start_script=$( date +%s )
##################################################
echo "Start at $( date +'%Y-%m-%d %H-%M' )" ... > $LOG_FILE
echo "Restoring $ORACLE_SID to $RECOVERY_DATE" >> $LOG_FILE
## Определяем даты использования резервных копий
echo >> $LOG_FILE
echo "STEP 1." >> $LOG_FILE
echo "Determine the dates of the backups used" >> $LOG_FILE
dt_recovery=$( date +%Y%m%d%H%M -d "${RECOVERY_DATE}" )
## Получаем список резервных копий, выделяем даты, заносим в массив
read -a arr_dt_dirs << EOF
$( ls -1 $DB_BCK_PATH/ | grep .${ORACLE_SID}.cold | sed -e ':a;N;$!ba;s/[\n]/ /g;s/\([0-9]\{12\}\)[^ ]\+/\1/g' )
EOF
dt_first_idx=''
dt_last_idx=''
## длина списка директорий
len=${#arr_dt_dirs[@]}
## определяем ближайших холодный бекап
for ((i=0; i < $len; i++))
do
if (( ${arr_dt_dirs[$i]} > dt_recovery )); then
break
fi
dt_first_idx=$i
if [ -f "$DB_BCK_PATH/${arr_dt_dirs[$i]}.${ORACLE_SID}.cold/${arr_dt_dirs[$i]}.${ORACLE_SID}.base.tar.bz2" ]; then
dt_first_idx=$i
fi
done
## выходим из скрипта если не найден
if [[ -z "$dt_first_idx" ]]; then
exit_on_error "--> Error: nearest cold backup not found"
fi
## определяем последующий бекап с архивлогами
for ((i=$len-1; i > -1; i--))
do
if (( ${arr_dt_dirs[$i]} < dt_recovery )); then
break
fi
if [ -f "$DB_BCK_PATH/${arr_dt_dirs[$i]}.${ORACLE_SID}.cold/${arr_dt_dirs[$i]}.${ORACLE_SID}.archive.tar.bz2" ]; then
dt_last_idx=$i
fi
done
## выходим из скрипта если не найден
if [[ -z "$dt_last_idx" ]]; then
exit_on_error "--> Error: archivelogs not found"
fi
echo "backups found:" >> $LOG_FILE
echo "first - nearest cold backup: ${arr_dt_dirs[$dt_first_idx]}" >> $LOG_FILE
echo "last - with archivelogs: ${arr_dt_dirs[$dt_last_idx]}" >> $LOG_FILE
##################################################
## копируем текущее состояние базы
echo >> $LOG_FILE
echo "STEP 2." >> $LOG_FILE
echo "Copying current DB state..." >> $LOG_FILE
## выключаем базу
echo >> $LOG_FILE
echo "Shutdown DB..." >> $LOG_FILE
sqlplus /nolog 2>1 1>> $LOG_FILE <<EOF
connect / as sysdba
shu immediate
exit
EOF
cd $DB_PATH/
echo >> $LOG_FILE
echo "Packing the current DB..." >> $LOG_FILE
tar cvh `ls -1X ctl*/* redo/* ; ls -1X data/* idx/* temp/* undo/*` 2>>$LOG_FILE | gzip -c1 > $CUR_DB_BCK_PATH/$ORACLE_SID.current-base.tar.gz
if (( $? != 0 )); then
exit_on_error
fi
echo >> $LOG_FILE
echo "Packing DB controlfiles and redo..." >> $LOG_FILE
tar cvh `ls -1X ctl*/* redo/*` 2>>$LOG_FILE | gzip -c1 > $CUR_DB_BCK_PATH/$ORACLE_SID.current-ctl+redo.tar.gz
if (( $? != 0 )); then
exit_on_error
fi
echo >> $LOG_FILE
ls -lh $CUR_DB_BCK_PATH/$ORACLE_SID.current-*.tar.gz >> $LOG_FILE
##################################################
## распаковываем ближайший холодный бекап
echo >> $LOG_FILE
echo "STEP 3." >> $LOG_FILE
echo "Unpack the nearest cold backup..." >> $LOG_FILE
# Ближайший холодный бэкап к дате восстановления
dt_backup=${arr_dt_dirs[$dt_first_idx]}
backup_file_path="$DB_BCK_PATH/$dt_backup.${ORACLE_SID}.cold/$dt_backup.${ORACLE_SID}.base.tar.bz2"
echo "The nearest cold backup to the restore date" >> $LOG_FILE
echo $backup_file_path >> $LOG_FILE
# Распаковываем холодный бекап
echo >> $LOG_FILE
echo "Unpack backup to the instance" >> $LOG_FILE
tar xvjf $backup_file_path -C / 2>1 1>>$LOG_FILE
if (( $? != 0 )); then
exit_on_error "--> Error while unpacking a cold backup"
fi
##################################################
## распаковываем архивлоги
echo >> $LOG_FILE
echo "STEP 4." >> $LOG_FILE
echo "Unpack archivelogs..." >> $LOG_FILE
for dt_backup in ${arr_dt_dirs[@]:$(($dt_first_idx+1)):$(($dt_last_idx-$dt_first_idx))}
do
backup_file_path="$DB_BCK_PATH/$dt_backup.${ORACLE_SID}.cold/$dt_backup.${ORACLE_SID}.archive.tar.bz2"
tar xvjf $backup_file_path -C / 2>1 1>>$LOG_FILE
if (( $? != 0 )); then
exit_on_error "--> Error while unpacking archivelogs"
fi
done
##################################################
## возвращаем обратно контрол файлы и реду логи
## с текущей базы данных
echo >> $LOG_FILE
echo "Return back control files and redu logs from the current database..." >> $LOG_FILE
tar xvzf $CUR_DB_BCK_PATH/$ORACLE_SID.current-ctl+redo.tar.gz -C $DB_PATH/ 2>1 1>>$LOG_FILE
if (( $? != 0 )); then
exit_on_error "--> Error while unpacking last current controlfiles and redo logs"
fi
cp -vpR $DB_PATH/redo $DB_PATH/redo2 2>1 1>>$LOG_FILE
if (( $? != 0 )); then
exit_on_error "--> Error while copying redo logs"
fi
cp -vpR $DB_PATH/redo $DB_PATH/redo3 2>1 1>>$LOG_FILE
if (( $? != 0 )); then
exit_on_error "--> Error while copying redo logs"
fi
##################################################
## удалим репликационные файлы с даты восстановления по настоящее время
## т.к. они будут неактуальны
echo >> $LOG_FILE
echo "STEP 5." >> $LOG_FILE
echo "Delete replication files from the recovery date to the present..." >> $LOG_FILE
MMIN=$(( ( $(date +%s ) - $(date +%s -d "$RECOVERY_DATE" ) )/60 ))
echo "delte from $MMIN minutes ago" >> $LOG_FILE
find $DB_PATH/replicate/ -type f -mmin -$MMIN -exec rm -vrf {} 2>1 1>>$LOG_FILE \;
##################################################
## накатываем архивлоги до указанной даты
echo >> $LOG_FILE
echo "STEP 6." >> $LOG_FILE
echo "Apply archivelogs until the specified date..." >> $LOG_FILE
UNTIL_TIME=${RECOVERY_DATE/ /:}
sqlplus /nolog 2>1 1>>$LOG_FILE <<EOF
prompt connect / as sysdba
connect / as sysdba
prompt set autorecovery on;
set autorecovery on;
prompt startup mount
startup mount
prompt recover database until time '$UNTIL_TIME';
recover database until time '$UNTIL_TIME';
prompt alter database open resetlogs;
alter database open resetlogs;
exit
EOF
##################################################
send_message

Комментариев нет:

Отправить комментарий

Последнее...

CURL вместо Postman / Swagger