Pull to refresh

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

Reading time3 min
Views9.5K
Некоторое время назад потребовалось сделать бэкап базы данных на 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, оказывается, и так умеет.

Источники.
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
Tags:
Hubs:
+4
Comments9

Articles