Pull to refresh

Автоматизированные бэкапы для DB2 на *nix

Reading time5 min
Views2.3K
Не так давно на работе понадобилось настроить автоматизированные оффлайн бэкапы для DB2 на AIX. Tак как стандартным методом это сделать не получилось я решил попробавать настроить всё при помощи скриптов которые бы запускались с помощью крона. Простым методом тыка я выяснил что полные оффлайн бэкапы базы занимающие около нескольких гигабайт можно весьма успешно архивировать с помощью 7z. Конечные результаты занимают около 20-50 мб для баз размером в 2-3 гигабайта. Таким образом можно хранить полные бэкапы расфасованные по датам. Осталось всё это дело автоматизировать. Для этого я решил написать несколько скриптов которые бы автоматически убивали соединение к базе, делали бэкап схемы и самой базы.

Для примера расмотрим 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пасибо за внимание.
Tags:
Hubs:
+6
Comments6

Articles

Change theme settings