Не так давно на работе понадобилось настроить автоматизированные оффлайн бэкапы для 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ледующие записи.
Как видите мы обьявили две глобальные переменные. Мы будем их использовать во втором скрипте. Параметр параллелизм используется во время бэкапа db2 и уменьшает время бэкапа на многопроцессорных системах.export PATH=~/bin:$PATH export DB2_BACKUP_DIR=~/backups export DB2_BACKUP_PARALLELISM=4
Второй скрипт это 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 который мы будем вызывать кроном.
Для каждой базы которую вы хотите бэкапить нужно вызывать backupdb c именем базы которую вы хотите бэкапить и именем схемы бэкап которой вы хотите сохранить в .sql файле. Если вы не укажете схему то будет делатся только бэкап базы.#!/usr/bin/env bash # Loading Enviroment variables . /home/db2inst1/.profile # Backing up database backupdb DBNAME SCHEMANAME echo "Backup Completed"
Проверьте все скрипты и добавьте в крон вызов backup.sh, к примеру каждый день в 3 часа ночи.
# DB2 backups 0 3 * * * /home/db2inst1/bin/backup.sh
Cпасибо за внимание.
