Postgres -- 备份恢复的三种方式

Feb 13, 2017


本篇的主要内容 :

    1. 方案一 : 基于 转储 的备份与恢复
    2. 方案二 : 基于 归档 的备份和恢复
    3. 方案三 : 基于 pg_rman 的全备与增备 ( 推荐 )

方案一. 基于转储的备份和恢复

    A. script for backup ( sql_backup.sh )
    
    // 该脚本备份某个库
    //   结果放入以当天日期为名的目录中
    //   并把结果文件按256M拆分,接着每个文件单独压缩(因打包压缩会超出最大文件限制)
    // 用法 : ./sql_backup.sh dbname
    
        #!/bin/sh
        
        # accept parameters
        dbname=$1
        if [ ${#dbname} -eq 0 ]; then
        	echo "You must choose a database to backup"
        	echo "Eg : ./sql_backup.sh dbname"
        	exit
        fi
        
        BackupDir=/mydata/pgbackup
        
        # environment variables
        export PGUSER=postgres
        export PGPASSWORD='xxxxxx'
        export PGHOST=127.0.0.1
        export PGPORT=5432
        
        # dump
        CurDate=$(date +%Y%m%d)
        mkdir $BackupDir/$CurDate
        pg_dump -Fc $dbname | split -b 256M - $BackupDir/$CurDate/
        
        # compress each file separately
        cd $BackupDir/$CurDate
        ls -lh | while read line
        do
        	file=$(echo $line | awk '{word=$9}END{print word}')
        	if [ ${#file} -gt 0 ]; then
        		zip $file.zip $file
        		rm $file
        	fi
        done
        
        echo "successfully backup to $BackupDir/$CurDate"
    
    
    B. script for restore ( sql_restore.sh )
    
    // 该脚本恢复某个库
    // 用法 : ./sql_restore.sh 某次备份的结果目录 新库名
    // 例如 : ./sql_restore.sh /mydata/pgbackup/20170215
    
        #!/bin/sh
        
        # accept parameters
        restore_dir=$1
        new_dbname=$2
        if [ ${#restore_dir} -eq 0 ] || [ ${#new_dbname} -eq 0 ]; then
        	echo "!-- lack of parameters"
        	echo "Eg : ./sql_restore.sh /mydata/pgbackup/20170215 newdb"
        	exit
        fi
        
        # environment variables
        export PGUSER=postgres
        export PGPASSWORD='xxxxxx'
        export PGHOST=127.0.0.1
        export PGPORT=5432
        
        # uncompress each file separately
        cd $restore_dir
        ls -lh | while read line
        do
        	file=$(echo $line | awk '{word=$9}END{print word}')
        	if [ ${#file} -gt 0 ]; then
        		unzip $file
        		rm $file
        	fi
        done
        
        # restore
        createdb $new_dbname
        cat * | pg_restore -d $new_dbname
        psql -d $new_dbname -c 'analyze'
        
        echo "successfully restore from $restore_dir to $new_dbname"

方案二. 基于 归档 的备份和恢复

    1. 修改 postgresql.conf
    
        wal_level       = archive
        archive_mode    = on
        archive_command = 'test ! -f /usr/local/pgsql/archivedir/%f && cp %p /usr/local/pgsql/archivedir/%f'
        archive_timeout = 300
        max_wal_senders = 4
        // 如果你的事务不是很频繁,可以设置 archive_timeout,限制超过多少秒就把wal段归档
    
    
    2. 修改 pg_hba.conf,添加一行 :
    
        host    replication     postgres        127.0.0.1/32            password
        // 比如你要使用超级用户postgres来进行复制
        // 改完这两个配置文件后重启服务
    
    
    3. 先进行一次全备份( 基础备份 )
    
        pg_basebackup -h 127.0.0.1 -p 5432 -U postgres -D /mydata/pgbackup -Ft -z -P
        // -D     指定备份目录
        // -Ft -z 指定按照tar.gz来压缩
        // -P     指定输出一些进度信息
    
    
    4. 更改一些数据,模拟读写请求,然后等一会,直至/usr/local/pgsql/archivedir目录下出现了新的wal归档
        // 如果等不及,可以用 select pg_switch_xlog(); 来强制更新wal段
    
    
    5. 发生故障,如何恢复 ?
    
        第一步 : 关闭服务
        
        第二步 : 清空数据目录 ( 清空前建议打包压缩先放到其他地方,以免恢复失败时不至于服务都启动不了 )
        
        第三步 : 把之前做的基础备份放到数据目录下并还原 :
        
                mv /mydata/pgbackup/base.tar.gz /usr/local/pgsql/data/
                cd /usr/local/pgsql/data
                tar -xvf base.tar.gz
                rm base.tar.gz
                
                rm -rf pg_xlog/*
                mkdir pg_xlog/archive_status
                chmod 700 pg_xlog/archive_status

        第四步 : 创建 /usr/local/pgsql/data/recovery.conf,内容是 :
                restore_command = 'cp /usr/local/pgsql/archivedir/%f %p'
        
        第五步 : 启动服务
        // 过一会数据恢复好以后,可以看到数据目录下的 recovery.conf 后缀名变成了 done
        // 最后不要忘了恢复后执行一下 vacuum 或 analyze

方案三. 基于 pg_rman 的全备和增备( 推荐 )

    1. postgresql.conf 的要求
    
        wal_level         = archive
        
        archive_mode      = on
        archive_command   = 'test ! -f /usr/local/pgsql/archivedir/%f && cp %p /usr/local/pgsql/archivedir/%f'
        
        log_destination   = 'csvlog'
        logging_collector = on
        log_directory     = '/usr/local/pgsql/logs'
    
    
    2. 初始化备份目录
    
        pg_rman init -B /mydata/pgbackup -D /usr/local/pgsql/data
        // -B 指定一个空的备份目录
        // -D 指定原数据目录
    
    
    3. 先来做一次全量备份
    
        pg_rman backup \
            -B /mydata/pgbackup \
            -D /usr/local/pgsql/data \
            -b full \
            -s -Z -C \
            --keep-data-days=10 \
            --keep-arclog-files=20 \
            --keep-arclog-days=10 \
            --keep-srvlog-files=20 \
            --keep-srvlog-days=10 \
            -h 127.0.0.1 -p 5432 -U postgres -d deepnote
        // -b full              表示是全备份
        // -s                   表示服务日志也要备份
        // -Z                   表示备份要压缩
        // -C                   表示要检查检查点
        // --keep-data-days=10  表示这份全备要保持10天
        
        pg_rman show -B /mydata/pgbackup
        // 结果 :
            ============================================================================
            Start                Time   Total    Data     WAL     Log  Backup   Status  
            ============================================================================
            2017-02-20 21:02:54    0m    34MB    ----    33MB   1023B  4046kB   DONE
        // 根据这个时间(正是这次全备),状态是DONE,说明全备成功
        
        pg_rman validate -B /mydata/pgbackup
        // 每次做完一次备份( 不管是全备还是增备 ),都必须做校验
        
        pg_rman show -B /mydata/pgbackup
        // 结果 :
            ============================================================================
            Start                Time   Total    Data     WAL     Log  Backup   Status  
            ============================================================================
            2017-02-20 21:02:54    0m    34MB    ----    33MB   1023B  4046kB   OK
        // 根据这个时间(正是刚才的全备),状态从原来的DONE变成了OK,说明校验成功
    
    
    4. 再来做一次增量备份
        
        pg_rman backup \
            -B /mydata/pgbackup \
            -D /usr/local/pgsql/data \
            -b incremental \
            -s -Z -C \
            --keep-data-days=10 \
            --keep-arclog-files=20 \
            --keep-arclog-days=10 \
            --keep-srvlog-files=20 \
            --keep-srvlog-days=10 \
            -h 127.0.0.1 -p 5432 -U postgres -d deepnote
        
        pg_rman show -B /mydata/pgbackup
        // 结果 :
            ============================================================================
            Start                Time   Total    Data     WAL     Log  Backup   Status  
            ============================================================================
            2017-02-20 21:05:49    0m    ----   492kB    33MB      0B   102kB   DONE
            2017-02-20 21:02:54    0m    34MB    ----    33MB   1023B  4046kB   OK
        // 从 Total 字段,可以看出哪份是全备,哪份是增备
        
        pg_rman validate -B /mydata/pgbackup
        // 每次做完一次备份( 不管是全备还是增备 ),都必须做校验
        
        pg_rman show -B /mydata/pgbackup
        // 结果 :
            ============================================================================
            Start                Time   Total    Data     WAL     Log  Backup   Status  
            ============================================================================
            2017-02-20 21:05:49    0m    ----   492kB    33MB      0B   102kB   OK
            2017-02-20 21:02:54    0m    34MB    ----    33MB   1023B  4046kB   OK
        // 看到刚才的增备,状态从DONE变成了OK,说明这份增备也校验成功

四. 基于 pg_rman,如何删除不想要的备份

    在进行全备和增备的时候,参数 --keep-data-days 表示该全备(or增备)的有效期
    然而可能等不及过期就想删掉某些备份,我们知道强迫症患者有时候是很可怕的 > <
    
    
    // 假设此时 pg_rman show -B /mydata/pgbackup 的结果是这样的 :
        ============================================================================
        Start                Time   Total    Data     WAL     Log  Backup   Status  
        ============================================================================
        2017-02-20 21:13:51    0m    ----   336kB    33MB      0B    87kB   OK
        2017-02-20 21:12:05    0m    ----   287kB    33MB      0B    84kB   OK
        2017-02-20 21:09:25    0m    34MB    ----    33MB      0B  4047kB   OK
        2017-02-20 21:05:49    0m    ----   492kB    33MB      0B   102kB   OK
        2017-02-20 21:02:54    0m    34MB    ----    33MB   1023B  4046kB   OK
    // 此时有两条备份线 :
    // 备份线1 : 2017-02-20 21:02:54(全)  -->  2017-02-20 21:05:49(增)
    // 备份线2 : 2017-02-20 21:09:25(全)  -->  2017-02-20 21:12:05(增)  -->  2017-02-20 21:13:51(增)
    
    
    pg_rman delete 2017-02-20 21:13:51 -B /mydata/pgbackup
    // 执行完再用show命令看看会发生什么 :
        ============================================================================
        Start                Time   Total    Data     WAL     Log  Backup   Status  
        ============================================================================
        2017-02-20 21:13:51    0m    ----   336kB    33MB      0B    87kB   OK
        2017-02-20 21:12:05    0m    ----   287kB    33MB      0B    84kB   OK
        2017-02-20 21:09:25    0m    34MB    ----    33MB      0B  4047kB   OK
    // 特别注意一下 delete 参数的含义 : 要恢复到这个时刻,哪些备份是不必要的
    // 所以,我要恢复到 2017-02-20 21:13:51,只要前三份备份就够了
    // 即, 21:02:54那个全备 和 21:05:49那个增备,没有也不妨碍
    
    
    pg_rman show timeline -B /mydata/pgbackup
    // 结果 :
        ============================================================
        Start                Mode  Current TLI  Parent TLI  Status  
        ============================================================
        2017-02-20 21:13:51  INCR            3           0  OK
        2017-02-20 21:12:05  INCR            3           0  OK
        2017-02-20 21:09:25  FULL            3           0  OK
    // show timeline 能更方便观察时间线和全备增备

五. 基于 pg_rman,如何恢复到指定时间

    // 假设此时的 pg_rman show -B /mydata/pgbackup 的结果是这样的 :
        ============================================================================
        Start                Time   Total    Data     WAL     Log  Backup   Status  
        ============================================================================
        2017-02-21 08:56:51    0m    ----   509kB    67MB   3118B   156kB   OK
        2017-02-20 21:13:51    0m    ----   336kB    33MB      0B    87kB   OK
        2017-02-20 21:12:05    0m    ----   287kB    33MB      0B    84kB   OK
        2017-02-20 21:09:25    0m    34MB    ----    33MB      0B  4047kB   OK
    
    
    // 比如我要恢复到 2017-02-20 21:13:00 这个时刻,怎么办 ?
    // 2017-02-20 21:13:00 属于 2017-02-20 21:12:05 和 2017-02-20 21:13:51 之间
    // 即需要使用如下三份备份 :
    //   2017-02-20 21:09:25 这个全量备份
    //   2017-02-20 21:12:05 这个增量备份
    //   2017-02-20 21:13:51 这个增量备份
    
    
    // 注意 :
    // 我的表空间,x_log都在数据目录下,即 /usr/local/pgsql/data
    // 我的wal段文件存储目录             /usr/local/pgsql/archivedir
    
    
    pg_ctl stop -m fast -D /usr/local/pgsql/data
    // 注意,在恢复数据前,最好先关闭服务,且使用 -m fast模式为佳
    // 而且最好把原来的数据目录先备份一下
    
    
    pg_rman restore \
        --recovery-target-time "2017-02-20 21:13:00" \
        -B /mydata/pgbackup \
        -D /usr/local/pgsql/data
    // 恢复到目标时间
    
    
    vi /usr/local/pgsql/data/recovery.conf
    // 结果 :
        # recovery.conf generated by pg_rman 1.2.11
        restore_command = 'cp /usr/local/pgsql/archivedir/%f %p'
        recovery_target_time = '2017-02-20 21:13:00'
        recovery_target_timeline = '4'
    // 没错
    
    
    最后再次启动服务,会发现数据不是最新的(确实是"2017-02-20 21:13:51"这份备份截止时的)
    // 最后不要忘了恢复后执行一下 vacuum 或 analyze