私はバックアップの方法を変更することを検討しており、postgreqlクラスター内のどのデータベースが最近変更されていないかを判別する方法があるかどうか疑問に思っていますか?
Pg_dumpallを使用する代わりに、pg_dumpを使用して、最後のバックアップ以降に変更されたデータベースのみをダンプします(一部のデータベースは頻繁に更新されません)。変更がない場合、現在のバックアップは、まだ良い。
特定のデータベースが最後に更新/変更された時期を判別する方法を知っている人はいますか?
ありがとう...
更新:
1つの特定のクラスターでのデータベースの作成(データベース内でのdbオブジェクトの作成はもちろん)を制御できないため、場所全体にトリガーを記述する必要がないことを望んでいました。
さらに掘り下げてみると、$ PGDATA/global/pg_databaseファイルの内容(特に2番目のフィールド)と$ PGDATA/baseの下のディレクトリ名の間に相関関係があるようです。
実際、pg_databaseファイルの2番目のフィールドはデータベースoidであり、各データベースには$ PGDATA/baseの下に独自のサブディレクトリがある(サブディレクトリ名にはoidが付いている)と思います。あれは正しいですか?もしそうなら、バックアップを必要とするトリガーとして$ PGDATA/base/*の下のファイルからのファイルのタイムスタンプを使用することは理にかなっていますか?
...またはより良い方法はありますか?
再度、感謝します...
@Jack Douglasが提案したselect datname, xact_commit from pg_stat_database;
の使用は(自動バキュームが原因で)まったく機能しませんが、select datname, tup_inserted, tup_updated, tup_deleted from pg_stat_database
は機能しているようです。 DMLとDDLの両方の変更により、tup_ *列の値は変更されますが、vacuum
は変更されません(一方でvacuum analyze
...)。
これが他のユーザーに役立つ可能性がある場合は、私が配置したバックアップスクリプトを含めます。これはPg 8.4.xでは機能しますが、8.2.xでは機能しません-使用するPgのバージョンによってはYMMVです。
#!/usr/bin/env Perl
=head1 Synopsis
pg_backup -- selectively backup a postgresql database cluster
=head1 Description
Perform backups (pg_dump*) of postgresql databases in a cluster on an
as needed basis.
For some database clusters, there may be databases that are:
a. rarely updated/changed and therefore shouldn't require dumping as
often as those databases that are frequently changed/updated.
b. are large enough that dumping them without need is undesirable.
The global data is always dumped without regard to whether any
individual databses need backing up or not.
=head1 Usage
pg_backup [OPTION]...
General options:
-F, --format=c|t|p output file format for data dumps
(custom, tar, plain text) (default is custom)
-a, --all backup (pg_dump) all databases in the cluster
(default is to only pg_dump databases that have
changed since the last backup)
--backup-dir directory to place backup files in
(default is ./backups)
-v, --verbose verbose mode
--help show this help, then exit
Connection options:
-h, --Host=HOSTNAME database server Host or socket directory
-p, --port=PORT database server port number
-U, --username=NAME connect as specified database user
-d, --database=NAME connect to database name for global data
=head1 Notes
This utility has been developed against PostgreSQL version 8.4.x. Older
versions of PostgreSQL may not work.
`vacuum` does not appear to trigger a backup unless there is actually
something to vacuum whereas `vacuum analyze` appears to always trigger a
backup.
=head1 Copyright and License
Copyright (C) 2011 by Gregory Siems
This library is free software; you can redistribute it and/or modify it
under the same terms as PostgreSQL itself, either PostgreSQL version
8.4 or, at your option, any later version of PostgreSQL you may have
available.
=cut
use strict;
use warnings;
use Getopt::Long;
use Data::Dumper;
use POSIX qw(strftime);
my %opts = get_options();
my $connect_options = '';
$connect_options .= "--$_=$opts{$_} " for (qw(username Host port));
my $shared_dump_args = ($opts{verbose})
? $connect_options . ' --verbose '
: $connect_options;
my $backup_prefix = (exists $opts{Host} && $opts{Host} ne 'localhost')
? $opts{backup_dir} . '/' . $opts{Host} . '-'
: $opts{backup_dir} . '/';
do_main();
########################################################################
sub do_main {
backup_globals();
my $last_stats_file = $backup_prefix . 'last_stats';
# get the previous pg_stat_database data
my %last_stats;
if ( -f $last_stats_file) {
%last_stats = parse_stats (split "\n", Slurp_file ($last_stats_file));
}
# get the current pg_stat_database data
my $cmd = 'psql ' . $connect_options;
$cmd .= " $opts{database} " if (exists $opts{database});
$cmd .= "-Atc \"
select date_trunc('minute', now()), datid, datname,
xact_commit, tup_inserted, tup_updated, tup_deleted
from pg_stat_database
where datname not in ('template0','template1','postgres'); \"";
$cmd =~ s/\ns+/ /g;
my @stats = `$cmd`;
my %curr_stats = parse_stats (@stats);
# do a backup if needed
foreach my $datname (sort keys %curr_stats) {
my $needs_backup = 0;
if ($opts{all}) {
$needs_backup = 1;
}
elsif ( ! exists $last_stats{$datname} ) {
$needs_backup = 1;
warn "no last stats for $datname\n" if ($opts{debug});
}
else {
for (qw (tup_inserted tup_updated tup_deleted)) {
if ($last_stats{$datname}{$_} != $curr_stats{$datname}{$_}) {
$needs_backup = 1;
warn "$_ stats do not match for $datname\n" if ($opts{debug});
}
}
}
if ($needs_backup) {
backup_db ($datname);
}
else {
chitchat ("Database \"$datname\" does not currently require backing up.");
}
}
# update the pg_stat_database data
open my $fh, '>', $last_stats_file || die "Could not open $last_stats_file for output. !$\n";
print $fh @stats;
close $fh;
}
sub parse_stats {
my @in = @_;
my %stats;
chomp @in;
foreach my $line (@in) {
my @ary = split /\|/, $line;
my $datname = $ary[2];
next unless ($datname);
foreach my $key (qw(tmsp datid datname xact_commit tup_inserted tup_updated tup_deleted)) {
my $val = shift @ary;
$stats{$datname}{$key} = $val;
}
}
return %stats;
}
sub backup_globals {
chitchat ("Backing up the global data.");
my $backup_file = $backup_prefix . 'globals-only.backup.gz';
my $cmd = 'pg_dumpall --globals-only ' . $shared_dump_args;
$cmd .= " --database=$opts{database} " if (exists $opts{database});
do_dump ($backup_file, "$cmd | gzip");
}
sub backup_db {
my $database = shift;
chitchat ("Backing up database \"$database\".");
my $backup_file = $backup_prefix . $database . '-schema-only.backup.gz';
do_dump ($backup_file, "pg_dump --schema-only --create --format=plain $shared_dump_args $database | gzip");
$backup_file = $backup_prefix . $database . '.backup';
do_dump ($backup_file, "pg_dump --format=". $opts{format} . " $shared_dump_args $database");
}
sub do_dump {
my ($backup_file, $cmd) = @_;
my $temp_file = $backup_file . '.new';
warn "Command is: $cmd > $temp_file" if ($opts{debug});
chitchat (`$cmd > $temp_file`);
if ( -f $temp_file ) {
chitchat (`mv $temp_file $backup_file`);
}
}
sub chitchat {
my @ary = @_;
return unless (@ary);
chomp @ary;
my $first = shift @ary;
my $now = strftime "%Y%m%d-%H:%M:%S", localtime;
print +(join "\n ", "$now $first", @ary), "\n";
}
sub get_options {
Getopt::Long::Configure('bundling');
my %opts = ();
GetOptions(
"a" => \$opts{all},
"all" => \$opts{all},
"p=s" => \$opts{port},
"port=s" => \$opts{port},
"U=s" => \$opts{username},
"username=s" => \$opts{username},
"h=s" => \$opts{Host},
"Host=s" => \$opts{Host},
"F=s" => \$opts{format},
"format=s" => \$opts{format},
"d=s" => \$opts{database},
"database=s" => \$opts{database},
"backup-dir=s" => \$opts{backup_dir},
"help" => \$opts{help},
"v" => \$opts{verbose},
"verbose" => \$opts{verbose},
"debug" => \$opts{debug},
);
# Does the user need help?
if ($opts{help}) {
show_help();
}
$opts{Host} ||= $ENV{PGHOSTADDR} || $ENV{PGHOST} || 'localhost';
$opts{port} ||= $ENV{PGPORT} || '5432';
$opts{Host} ||= $ENV{PGHOST} || 'localhost';
$opts{username} ||= $ENV{PGUSER} || $ENV{USER} || 'postgres';
$opts{database} ||= $ENV{PGDATABASE} || $opts{username};
$opts{backup_dir} ||= './backups';
my %formats = (
c => 'custom',
custom => 'custom',
t => 'tar',
tar => 'tar',
p => 'plain',
plain => 'plain',
);
$opts{format} = (defined $opts{format})
? $formats{$opts{format}} || 'custom'
: 'custom';
warn Dumper \%opts if ($opts{debug});
return %opts;
}
sub show_help {
print `perldoc -F $0`;
exit;
}
sub Slurp_file { local (*ARGV, $/); @ARGV = shift; <> }
__END__
更新:スクリプトがgithub here に配置されました。
使用できるようです pg_stat_database
トランザクション数を取得し、これが1つのバックアップ実行から次のバックアップ実行に変更されるかどうかを確認します。
select datname, xact_commit from pg_stat_database;
datname | xact_commit
-----------+-------------
template1 | 0
template0 | 0
postgres | 136785
pg_stat_reset
データベースが変更されたかどうかはわかりませんが、それが起こる可能性は低いと考えて、最後の読み取りと一致する正確な数のトランザクションを続けます。
-編集
これが機能しない理由については this SO question を参照してください。これがなぜ発生するのかはわかりませんが、ロギングを有効にすると多少の影響が出る可能性があります...
Postgresのドキュメントとニュースグループを掘り下げて:
txid_current()
は、新しいxid
を提供します-後でこの関数を再度呼び出した場合、xid
が1つ高い場合、 2つの呼び出し間でトランザクションがコミットされていないことがわかります。ただし、他の誰かがtxid_current()
を呼び出した場合など、誤検知が発生する可能性があります。
DBデータを含むファイルにタイムスタンプを記録し、変更されているかどうかを確認します。彼らがした場合、書き込みがありました。
WALヒントの後で編集する:これは、未処理の書き込みをフラッシュした後でのみ行う必要があります。
Postgresql 9.5では、最後に変更されたタイムスタンプを追跡して、このリンクを確認してください https://thirumal-opensource.blogspot.in/2017/03/to-track-last-modified-commit-or-get.html =