Как запустить скрипт операционной системы из Oracle или Не совсем традиционный способ снятия бэкапа

Источник: habrahabr

Некоторое время назад потребовалось сделать бэкап базы данных на RAC. Задача, на самом деле, вполне тривиальная, если бы не несколько но: у заказчика паранойя по поводу безопасности (не удивительно и понятно), у заказчика нет своего DBA (ну так уж вышло) и у меня есть доступ только к консоли сервера с базой данных. Таким образом, пришлось отказаться от использования красивого и удобного Enterprise Manager и придумать банальные консольные скрипты. И вот тут-то и возникла загвоздка.

 Стандартное решение: написать скрипты бэкапа, настроить 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, оказывается, и так умеет.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=28057