Не так давно на работе понадобилось настроить автоматизированные оффлайн бэкапы для DB2 на AIX. Tак как стандартным методом это сделать не получилось я решил попробавать настроить всё при помощи скриптов которые бы запускались с помощью крона. Простым методом тыка я выяснил что полные оффлайн бэкапы базы занимающие около нескольких гигабайт можно весьма успешно архивировать с помощью 7z. Конечные результаты занимают около 20-50 мб для баз размером в 2-3 гигабайта. Таким образом можно хранить полные бэкапы расфасованные по датам. Осталось всё это дело автоматизировать. Для этого я решил написать несколько скриптов которые бы автоматически убивали соединение к базе, делали бэкап схемы и самой базы.
Для примера расмотрим DB2 9.7.4 Express Edition установленную на Centos 5.5. Для того чтобы вся эта система работала у вас должен быть установлён perl и 7za. База по умолчанию установлена в директорию /home/db2inst1. Cоздадим две директории, одну для скриптов а вторую для бэкапов.
Первый скрипт который нам понадобится это скрипт для убийства соединений к базе forcedb
Как видите функция getDBApplicationHandles берёт application handle-ы только у определённой базы, а forceDBApplication их закрывает. Этот скрипт не будет использоватся во время бэкапа но может вам пригодится в будущем. Его функции мы встроем во второй скрипт. Вы спросите почему мы встраиваем функции а не вызываем этот скрипт? Всё дело в скорости. Некоторые систему которые подключаются к db2 успевают создать новые соединения до того как скрипт бэкапа успевает запустить бэкап базы.
Для того чтобы этот скрипт работал добавим в .profile cледующие записи.
Второй скрипт это backupdb
Функция getTimeStamp выдаёт нам дату которая будет использоватся в имени файлов бэкапов. Функция getPathPrefix выдаёт нам путь по которому бэкапы будут расфасовыватся. Вы можете изменить эти функции на свой лад. Также вы можете поэксперементировать с параметрами архивации 7za в функции archiveDirectory. Если у вас возникнут проблемы с бэкапом схемы базы посмотрите параметры db2look в функции backupDB.
Добавим в bin ещё один скрипт backup.sh который мы будем вызывать кроном.
Проверьте все скрипты и добавьте в крон вызов backup.sh, к примеру каждый день в 3 часа ночи.
Cпасибо за внимание.
Для примера расмотрим DB2 9.7.4 Express Edition установленную на Centos 5.5. Для того чтобы вся эта система работала у вас должен быть установлён perl и 7za. База по умолчанию установлена в директорию /home/db2inst1. Cоздадим две директории, одну для скриптов а вторую для бэкапов.
mkdir bin
mkdir backups
Первый скрипт который нам понадобится это скрипт для убийства соединений к базе forcedb
#!/usr/bin/perl
sub getDBApplicationHandles
{
my $dbname = $_[0];
my @appHandles = ();
open (DB2, "db2 list application for database ".$dbname." |");
$f = 0;
while (<DB2>){
$l = $_;
if($l =~ /^\-\-\-/){
$f = 1;
}
if($f == 1 && !($l =~ /^\-\-\-/)){
for($i=0;$i<1000;$i++){
$l =~ s/ / /g;
}
my @val = split(' ', $l);
push(@appHandles, @val[2]);
}
}
close DB2;
return @appHandles;
}
sub forceDBApplication
{
my $dbname = $_[0];
my $forceS = "( ";
my $found = 0;
my @appHandles = getDBApplicationHandles($dbname);
foreach my $val (@appHandles) {
$val =~ s/^\s+//;
if(length($val) > 0){
$found = 1;
$forceS = $forceS . $val . ", ";
}
}
if($found == 1){
$forceS = substr($forceS, 0, length($forceS)-2) . " )";
print "db2 \"force application ".$forceS."\"\n";
system("db2 \"force application ".$forceS."\"");
}
}
if(length($ARGV[0]) == 0){
print "Please specify database name to force connections\n";
exit 1;
}
forceDBApplication($ARGV[0]);
Как видите функция getDBApplicationHandles берёт application handle-ы только у определённой базы, а forceDBApplication их закрывает. Этот скрипт не будет использоватся во время бэкапа но может вам пригодится в будущем. Его функции мы встроем во второй скрипт. Вы спросите почему мы встраиваем функции а не вызываем этот скрипт? Всё дело в скорости. Некоторые систему которые подключаются к db2 успевают создать новые соединения до того как скрипт бэкапа успевает запустить бэкап базы.
Для того чтобы этот скрипт работал добавим в .profile cледующие записи.
export PATH=~/bin:$PATH
export DB2_BACKUP_DIR=~/backups
export DB2_BACKUP_PARALLELISM=4
Как видите мы обьявили две глобальные переменные. Мы будем их использовать во втором скрипте. Параметр параллелизм используется во время бэкапа db2 и уменьшает время бэкапа на многопроцессорных системах.Второй скрипт это backupdb
#!/usr/bin/perl
use Time::localtime;
$globBackupDirectoryPath = $ENV{'DB2_BACKUP_DIR'};
$globBackupParallelism = $ENV{'DB2_BACKUP_PARALLELISM'};
sub getTimeStamp
{
$tm = localtime;
($DAY, $MONTH, $YEAR, $HOUR, $MINUTE) = ($tm->mday, $tm->mon, $tm->year, $tm->hour, $tm->min);
return $HOUR ."-". $MINUTE . "." . $DAY . "." . ($MONTH+1) . "." . ($YEAR+1900);
}
sub getPathPrefix
{
$tm = localtime;
return ($tm->year+1900). "/" . ($tm->mon+1);
}
sub archiveDirectory
{
$dirPath = $_[0];
$dbname = $_[1];
$tm = localtime;
($DAY, $MONTH, $YEAR, $HOUR, $MINUTE) = ($tm->mday, $tm->mon, $tm->year, $tm->hour, $tm->min);
$dirPathArch = $dirPath."/tmp/". $dbname .".". getTimeStamp() . ".7z";
if (-e $dirPathArch){
system("rm",$dirPathArch);
}
system("7za","a","-r","-mx=9","-mfb=64","-md=64m","-ms=on",$dirPathArch,$dirPath."/tmp/");
system("mv",$dirPathArch,$dirPath . "/db/" . getPathPrefix());
system("rm","-Rf",$dirPath."/tmp/");
print $dirPathArch . " archived\n";
}
sub backupDB
{
my $dbname = $_[0];
my $dbschema = $_[1];
my $dbparallelism = "".$globBackupParallelism;
my $backupDir = $globBackupDirectoryPath."/".$dbname;
# Schema backup
if(length($dbschema) > 0){
system("mkdir","-p",$backupDir . "/schema/" . getPathPrefix());
my $schemapath = $backupDir . "/schema/" . getPathPrefix() ."/". $dbname . "." . getTimeStamp().".sql";
system("db2look","-d",$dbname,"-z",$dbschema,"-e","-o",$schemapath,"-nofed");
print "db2 ".$dbname." schema ".$dbschema." backup completed\n";
}
# Database backup
system("mkdir","-p",$backupDir . "/db/" . getPathPrefix());
system("rm","-Rf",$backupDir."/tmp/");
system("mkdir","-p",$backupDir."/tmp/");
forceDBApplication($dbname);
system("db2","backup","database",$dbname,"to",$backupDir."/tmp/","parallelism",$dbparallelism);
archiveDirectory($backupDir,$dbname);
}
sub getDBApplicationHandles
{
my $dbname = $_[0];
my @appHandles = ();
open (DB2, "db2 list application for database ".$dbname." |");
$f = 0;
while (<DB2>){
$l = $_;
if($l =~ /^\-\-\-/){
$f = 1;
}
if($f == 1 && !($l =~ /^\-\-\-/)){
for($i=0;$i<1000;$i++){
$l =~ s/ / /g;
}
my @val = split(' ', $l);
push(@appHandles, @val[2]);
}
}
close DB2;
return @appHandles;
}
sub forceDBApplication
{
my $dbname = $_[0];
my $forceS = "( ";
my $found = 0;
my @appHandles = getDBApplicationHandles($dbname);
foreach my $val (@appHandles) {
$val =~ s/^\s+//;
if(length($val) > 0){
$found = 1;
$forceS = $forceS . $val . ", ";
}
}
if($found == 1){
$forceS = substr($forceS, 0, length($forceS)-2) . " )";
print "db2 \"force application ".$forceS."\"\n";
system("db2 \"force application ".$forceS."\"");
}
}
if(length($ARGV[0]) == 0){
print "Please specify database name and schema to backup\n";
exit 1;
}
backupDB($ARGV[0],$ARGV[1]);
Функция getTimeStamp выдаёт нам дату которая будет использоватся в имени файлов бэкапов. Функция getPathPrefix выдаёт нам путь по которому бэкапы будут расфасовыватся. Вы можете изменить эти функции на свой лад. Также вы можете поэксперементировать с параметрами архивации 7za в функции archiveDirectory. Если у вас возникнут проблемы с бэкапом схемы базы посмотрите параметры db2look в функции backupDB.
Добавим в bin ещё один скрипт backup.sh который мы будем вызывать кроном.
#!/usr/bin/env bash
# Loading Enviroment variables
. /home/db2inst1/.profile
# Backing up database
backupdb DBNAME SCHEMANAME
echo "Backup Completed"
Для каждой базы которую вы хотите бэкапить нужно вызывать backupdb c именем базы которую вы хотите бэкапить и именем схемы бэкап которой вы хотите сохранить в .sql файле. Если вы не укажете схему то будет делатся только бэкап базы. Проверьте все скрипты и добавьте в крон вызов backup.sh, к примеру каждый день в 3 часа ночи.
# DB2 backups
0 3 * * * /home/db2inst1/bin/backup.sh
Cпасибо за внимание.