Некоторое время назад потребовалось сделать бэкап базы данных на RAC. Задача, на самом деле, вполне тривиальная, если бы не несколько но: у заказчика паранойя по поводу безопасности (не удивительно и понятно), у заказчика нет своего DBA (ну так уж вышло) и у меня есть доступ только к консоли сервера с базой данных. Таким образом, пришлось отказаться от использования красивого и удобного Enterprise Manager и придумать банальные консольные скрипты. И вот тут-то и возникла загвоздка.
Стандартное решение: написать скрипты бэкапа, настроить cron, который будет их вызывать по расписанию, и на этом успокоиться до тех пор, пока база таки не упадёт. Но поскольку мы имеем дело с кластером из нескольких нод, встал вопрос, на которой из них дёргать бэкап. На всех — как-то многовато. На одной — а что, если именно она-то и упадёт, а все остальные останутся в строю? По очереди — а что, если упадёт именно та, чья очередь делать бэкап как раз и наступила? На каком-нибудь третьем сервере — нету лишней железки для дополнительной базы данных.
Тогда вспомнилась одна любопытная штука, которая появилась в версии 11.2 и с которой, кроме прочего, давно хотелось разобраться. А именно, внешние таблицы с возможностью предобработки подключаемого файла.
Понятно, что изобретённое решение — это нецелевое использование любопытной фичи. Но получилось забавно.
Сначала — скрипты (скрипт RMAN может быть любым, для примера пускай такой).
Скрипты бэкапа (run_backup.sh и backup.rmn) должны быть сложены на все имеющиеся ноды кластера по одинаковому пути (в данном примере — /u01/app/oracle/rman_backup/). И путь к утилите
После чего начинается интересное.
Предоставляем базе доступ к каталогу, в котором живут скрипты бэкапа.
Создаём пользователя, который будет дёргать скрипты бэкапа, и предоставляем ему необходимые привилегии.
Создаём внешнюю таблицу, основной смысл которой — запускать бэкап.
Затем — хранимку, которая будет дёргаться по расписанию (во второй части, где проверка успешности и оповещение DBA о результатах, можно, например, сообщение отправить на нужный адрес, но это немножко другая история).
Ну и, наконец, job, который будет нашу хранимку дёргать.
Такая вот штука. Oracle, оказывается, и так умеет.
Источники.
Oracle® Database Utilities, 11g Release 2 (11.2)
Preprocess External Tables, Arup Nanda
Using the Preprocessor Feature with External Tables in Oracle Database 11g Release 2
Стандартное решение: написать скрипты бэкапа, настроить cron, который будет их вызывать по расписанию, и на этом успокоиться до тех пор, пока база таки не упадёт. Но поскольку мы имеем дело с кластером из нескольких нод, встал вопрос, на которой из них дёргать бэкап. На всех — как-то многовато. На одной — а что, если именно она-то и упадёт, а все остальные останутся в строю? По очереди — а что, если упадёт именно та, чья очередь делать бэкап как раз и наступила? На каком-нибудь третьем сервере — нету лишней железки для дополнительной базы данных.
Тогда вспомнилась одна любопытная штука, которая появилась в версии 11.2 и с которой, кроме прочего, давно хотелось разобраться. А именно, внешние таблицы с возможностью предобработки подключаемого файла.
Понятно, что изобретённое решение — это нецелевое использование любопытной фичи. Но получилось забавно.
Сначала — скрипты (скрипт RMAN может быть любым, для примера пускай такой).
/u01/app/oracle/rman_backup/backup.rmn
RUN {
BACKUP
SPFILE FORMAT '+FRA/racdb/backupset/spfile_%d_%s_%T'
DATABASE INCLUDE CURRENT CONTROLFILE FORMAT '+FRA/racdb/backupset/data_%d_%s_%T'
ARCHIVELOG ALL FORMAT '+FRA/racdb/backupset/arc_%d_%s_%T';
BACKUP DEVICE TYPE DISK FORMAT '/backup/data_%d_%s_%T_%U' BACKUPSET COMPLETED AFTER 'SYSDATE - 1/24';
CROSSCHECK BACKUP;
CROSSCHECK BACKUPSET;
CROSSCHECK ARCHIVELOG ALL;
DELETE NOPROMPT EXPIRED ARCHIVELOG ALL;
DELETE NOPROMPT EXPIRED BACKUP;
DELETE NOPROMPT OBSOLETE;
}
/u01/app/oracle/rman_backup/run_backup.sh
#!/bin/bash
/u01/app/oracle/product/11.2.0/dbhome_1/bin/rman target / cmdfile=$*
Скрипты бэкапа (run_backup.sh и backup.rmn) должны быть сложены на все имеющиеся ноды кластера по одинаковому пути (в данном примере — /u01/app/oracle/rman_backup/). И путь к утилите
rman
должен быть указан полностью.После чего начинается интересное.
Предоставляем базе доступ к каталогу, в котором живут скрипты бэкапа.
$ sqlplus / as sysdba
SQL> create or replace directory bkp_dir as '/u01/app/oracle/rman_backup/';
Создаём пользователя, который будет дёргать скрипты бэкапа, и предоставляем ему необходимые привилегии.
$ sqlplus / as sysdba
SQL> create user bkp identified by rmanbkp;
SQL> grant connect to bkp;
SQL> grant create table to bkp;
SQL> grant create procedure to bkp;
SQL> grant create type to bkp;
SQL> grant create job to bkp;
SQL> grant read, write, execute on directory bkp_dir to bkp;
Создаём внешнюю таблицу, основной смысл которой — запускать бэкап.
SQL> connect bkp/rmanbkp
SQL> create table bkp (
2 line varchar2(255)
3 )
4 organization external (
5 type oracle_loader
6 default directory BKP_DIR
7 access parameters (
8 records delimited by newline
9 preprocessor BKP_DIR:'run_backup.sh'
10 fields terminated by ';' ldrtrim
11 reject rows with all null fields
12 )
13 location ('backup.rmn')
14 )
15 reject limit unlimited;
Затем — хранимку, которая будет дёргаться по расписанию (во второй части, где проверка успешности и оповещение DBA о результатах, можно, например, сообщение отправить на нужный адрес, но это немножко другая история).
SQL> create or replace procedure run_backup is
2 arrBackupLog backup_log;
3
4 cursor curBackupErrorsExist is
5 select 1 from dual
6 where exists (
7 select 1 from table (arrBackupLog)
8 where column_value like 'RMAN-%'
9 );
10
11 nDummy number(1);
12 bErrorsFound boolean;
13 begin
14 -- run weekly rman backup script
15 select line
16 bulk collect into arrBackupLog
17 from bkp;
18 -- report backup
19 open curBackupErrorsExist;
20 fetch curBackupErrorsExist into nDummy;
21 bErrorsFound := curBackupErrorsExist%found;
22 close curBackupErrorsExist;
23
24 if bErrorsFound then
25 dbms_output.put_line ('BACKUP FAILED');
26 else
27 dbms_output.put_line ('BACKUP SUCCEEDED');
28 end if;
29 end run_backup;
30 /
Ну и, наконец, job, который будет нашу хранимку дёргать.
SQL> begin
2 dbms_scheduler.create_job (
3 job_name => 'DAILY_BACKUP'
4 , job_type => 'STORED_PROCEDURE'
5 , job_action => 'run_backup'
6 , start_date => systimestamp
7 , repeat_interval => 'FREQ=DAILY;BYHOUR=04;BYMINUTE=0;BYSECOND=0'
8 , comments => 'Run daily RMAN backup'
9 , enabled => true
10 );
11 end;
12 /
Такая вот штука. Oracle, оказывается, и так умеет.
Источники.
Oracle® Database Utilities, 11g Release 2 (11.2)
Preprocess External Tables, Arup Nanda
Using the Preprocessor Feature with External Tables in Oracle Database 11g Release 2