AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 5110
Accepted
gsiems
gsiems
Asked: 2011-08-27 10:48:35 +0800 CST2011-08-27 10:48:35 +0800 CST 2011-08-27 10:48:35 +0800 CST

确定 PostgreSQL 数据库上次更改的时间

  • 772

我正在考虑更改备份的完成方式,并且想知道是否有办法确定 postgreql 集群中的哪些数据库最近没有更改?

而不是使用 pg_dumpall,我想使用 pg_dump 并且只转储自上次备份以来已更改的那些数据库(有些数据库不会经常更新)——这个想法是,如果没有任何变化,那么当前备份应该还是不错的。

有谁知道确定特定数据库上次更新/更改时间的方法?

谢谢...

更新:

我希望不必在整个地方编写触发器,因为我无法控制在一个特定集群中创建数据库(更不用说在数据库中创建 db 对象了)。

进一步挖掘,看起来 $PGDATA/global/pg_database 文件的内容(特别是第二个字段)和 $PGDATA/base 下的目录名称之间存在关联。

走出去,我猜 pg_database 文件的第二个字段是数据库 oid 并且每个数据库在 $PGDATA/base 下都有自己的子目录(子目录名称为 oid)。那是对的吗?如果是这样,使用 $PGDATA/base/* 下文件中的文件时间戳作为需要备份的触发器是否合理?

...或者,还有更好的方法?

再次感谢...

postgresql backup
  • 5 5 个回答
  • 11027 Views

5 个回答

  • Voted
  1. Best Answer
    gsiems
    2011-09-01T09:53:52+08:002011-09-01T09:53:52+08:00

    虽然select datname, xact_commit from pg_stat_database;按照@Jack Douglas 的建议使用并不能很好地工作(显然是由于 autovacuum),select datname, tup_inserted, tup_updated, tup_deleted from pg_stat_database但似乎确实有效。DML 和 DDL 更改都会更改 tup_* 列的值,而 avacuum不会(vacuum analyze另一方面...)。

    万一这可能对其他人有用,我将包括我已经放置的备份脚本。这适用于 Pg 8.4.x 但不适用于 8.2.x-- YMMV,具体取决于所使用的 Pg 版本。

    #!/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上。

    • 9
  2. Jack Douglas
    2011-08-28T01:07:39+08:002011-08-28T01:07:39+08:00

    看起来您可以pg_stat_database用来获取事务计数并检查这是否从一个备份运行更改为下一个:

    select datname, xact_commit from pg_stat_database;
    
      datname  | xact_commit 
    -----------+-------------
     template1 |           0
     template0 |           0
     postgres  |      136785
    

    如果有人打电话给pg_stat_reset您,您无法确定数据库是否已更改,但您可能认为发生这种情况的可能性不大,然后是与您上次阅读的交易数量完全一致。

    - 编辑

    看到这个 SO question为什么这可能不起作用。不知道为什么会发生这种情况,但启用日志记录可能会有所启发......

    • 2
  3. Jack Douglas
    2011-08-27T16:07:57+08:002011-08-27T16:07:57+08:00

    通过挖掘 postgres 文档和新闻组:

    txid_current()会给你一个新的xid- 如果你稍后再次调用该函数,如果你得到xid一个更高的,你知道这两个调用之间没有事务提交。你可能会得到误报 - 例如,如果其他人打电话txid_current()

    • 1
  4. Nils
    2011-08-28T12:58:53+08:002011-08-28T12:58:53+08:00

    记住包含数据库数据的文件的时间戳,并查看它们是否已更改。如果他们这样做了,就会写。

    在 WAL 提示后编辑:您应该仅在刷新未完成的写入后执行此操作。

    • 0
  5. Thirumal
    2017-04-01T03:48:49+08:002017-04-01T03:48:49+08:00

    Postgresql 9.5 让我们跟踪上次修改的时间戳查看此链接 https://thirumal-opensource.blogspot.in/2017/03/to-track-last-modified-commit-or-get.html

    • 0

相关问题

  • 运行时间偏移延迟复制的最佳实践

  • Oracle 中的数据库备份 - 导出数据库还是使用其他工具?

  • 存储过程可以防止 SQL 注入吗?

  • PostgreSQL 中 UniProt 的生物序列

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    您如何显示在 Oracle 数据库上执行的 SQL?

    • 2 个回答
  • Marko Smith

    如何选择每组的第一行?

    • 6 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    我可以查看在 SQL Server 数据库上运行的历史查询吗?

    • 6 个回答
  • Marko Smith

    如何在 PostgreSQL 中使用 currval() 来获取最后插入的 id?

    • 10 个回答
  • Marko Smith

    如何在 Mac OS X 上运行 psql?

    • 11 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Marko Smith

    将数组参数传递给存储过程

    • 12 个回答
  • Martin Hope
    Manuel Leduc PostgreSQL 多列唯一约束和 NULL 值 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler 什么时候应该将主键声明为非聚集的? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    BrunoLM Guid vs INT - 哪个更好作为主键? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k 什么时候应该使用唯一约束而不是唯一索引? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick 如何优化大型数据库的 mysqldump? 2011-01-04 13:13:48 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve