一、概述

此文档描述了Postgresql数据库备份方案, 当数据库发生崩溃时,如何利用备份恢复到崩溃前,保证数据库最少数据量丢失甚至不丢失;

二、备份恢复原理

运用在线备份以及即时恢复(PITR)原理,利用Postgresql 数据库的WAL(Write Ahead Logging )预写日志和基础备份( $PGDATA目录文件tar包 ),恢复到数据库崩溃前时间点,保证数据量最少丢失或者不丢失,如果数据库崩溃,我们就可以通过热备产生的备份文件data_bak.tar包 ($PGDATA目录文件tar包)和archive_command产生的WAL及我们自己备份的WAL(pg_xlog)来进行数据库的 recovery 。

三、在线热备

1. 创建表,插入100W测试数据:
create table foo(id integer);
insert into foo values(generate_series(1,1000000));
2. 可以查看现在数据库的大小:
select oid from pg_dtabase where datname='postgres'; 返回 16384 ;
在base目录下找到该文件夹,进入16384文件夹,执行 du -sh查看大小。
或者可以执行
select pg_size_pretty(pg_database_size('postgres'));来查看大小

3.1 配置归档模式
配置归档需要编辑postgresql.conf文件,默认与 /usr/local/pgsql/data/ 目录下
vi $PGDATA/postgresql.conf ,修改以下

archive_mode = on
archive_command = 'cp -i %p /home/postgres/archive/%f </dev/null'

注意:这里将WAL日志备份到 /home/postgres/archive 目录下,
pg_xlog/下的目录下可能还有未备份的数据,需要另外备份, 详见3.7。

3.2 重启动数据库

pg_ctl –D $PGDATA stop
pg_ctl –D $PGDATA sart

3.3 创建基础备份

postgres=# select pg_start_backup('bakup_test');

3.4 备份整个data目录 (tar方式)

tar cvf /database/pgdata/tbs1/pg_root/base_data.tar /database/pgdata/tbs1/pg_root/data

3.5 停止备份

postgres=# select pg_stop_backup();

3.6 切换日志

postgres=# pg_switch_xlog();

3.7 定期备份WAL新生成日志
如果单独通过archive_command来备份WAL的话, 能根本就做不到PITR, 因为pg_xlog/下面可能还有数据没有备份到archive_command指定的目录里;所以需要另外写脚本把/data/pg_xlog/下的WAL日志文件备份到预设的归档目录下,保证产生的WAL日志都已归档, 这里写了个脚本,每五分钟执行一次

每5分钟备份 (通过Crontab执行)

vi  cp_pg_xlog.sh
cp -f /database/pgdata/tbs1/pg_root/data/pg_xlog/[0-9]*  /home/postgres/archive/archive_bak2/

crontab – e
*/5  *   *   *   *   /home/postgres/script/cp_pg_xlog.sh > /dev/null 2>&1

3.8 编写备份脚本hot_bak_post.sh (供参考)
附件:以上操作可以写个脚本,定时执行,热备脚本为 hot_bak_post.sh ,定时执行写在CRONTABL里,每周六零晨做一次BASE 备份。脚本内容如下,参考

#!/bin/bash

CUR_DATE=`date +%Y%m%d`

export PGPORT=1921
export PGHOME=/database/pgdata/tbs1/pg_root
export PATH=/database/pgdata/tbs1/pg_root/bin:$PATH
export DATA_BAK_DIR=/home/postgres/archive/data_bak
export LOG_PATH=/home/postgres/archive/log
ARCHIVE_LOG="${LOG_PATH}/bak_pg_${CUR_DATE}.log"

echo "*************** Begin backup ,please wait *************************" |tee -a ${ARCHIVE_LOG}

psql -c "select pg_start_backup('backup_test');"

cd $PGHOME
tar cvf data_${CUR_DATE}.tar data
mv *.tar ${DATA_BAK_DIR}
psql -c "select pg_stop_backup();"
psql -c "select pg_switch_xlog();"
echo "Backup completed!"

说明:脚本已经测试,可以实现功能,目前还在完善中。

四、恢复

如果数据库崩溃,我们就可以使用热备产生的data_bak.tar包(即$PGDATA目录文件tar包)和archive_command产生的WAL及我们自己备份的WAL(pg_xlog)来进行数据库的 recovery,以下实验模拟数据库崩溃时的恢复过程

4.1 创建一张测试表

psql -dmydb -Uskytf
mydb=> \d
          List of relations
 Schema |    Name     | Type  | Owner
--------+-------------+-------+-------
 skytf  | test2       | table | skytf
 skytf  | test_1      | table | skytf
 skytf  | test_3      | table | skytf

mydb=> create table test_backup (id int,remark varchar(32));
CREATE TABLE

mydb=> insert into test_backup values (1 ,'a');
INSERT 0 1
mydb=> insert into test_backup values (2 ,'b');
INSERT 0 1
mydb=> insert into test_backup values (3 ,'c');

mydb=> select * from test_backup;
 id | remark
----+--------
  1 | a
  2 | b
  3 | c
(3 rows)

打开psql,往表中再插入100W条数据,使得总数据为200W:
insert into foo values(generate_series(1,1000000));
假定此时由于某种原因我们新的插入100万条记录的数据库出问题了,我们的疑问是可否利用前面的“基础备份库”+“新插入100万条记录所产生的WAL备份日志”恢复呢?

4.2 停数据库

pg_ctl –D $PGDATA stop

4.3 删除data目录

cd $PGDATA
rm -rf data

4.4 恢复备份

tar xvf –f database/pgdata/tbs1/pg_root/data_bak.tar

4.5 修改 pg_hba.conf,避免普通用户服务连接

4.6 清空/data/pg_xlog/目录下所有文件

rm –r $PGDATA/pg_xlog/

4.7 创建/pg_xlog/及其下面的archive_status目录

mkdir -p /database/pgdata/tbs1/pg_root/pg_xlog/archive_status

4.8 在/data/目录下创建recovery.conf

vi database/pgdata/tbs1/pg_root/data/recovery.conf 写入以下行

restore_command =  ' cp /home/postgres/archive/%f "%p"'

4.9 启动数据库

pg_ctl -D $PGDATA start

注意:切正常的话数据库就会自动应用WAL日志进行恢复
启动过程如有异常可以查看CSV日志,参数log_directory指定了日志目录(前提是你设置了日志记录功能);

4.10 查看数据库是否恢复

mydb=> \d
          List of relations
 Schema |    Name     | Type  | Owner
--------+-------------+-------+-------
 skytf  | test2       | table | skytf
 skytf  | test_1      | table | skytf
 skytf  | test_3      | table | skytf
 skytf  | test_backup | table | skytf
(4 rows)

mydb=> select * from test_backup;
 id | remark
----+--------
  1 | a
  2 | b
  3 | c
(3 rows)

4.11 库分析

vacuumdb -z -d mydb

至此,数据已成功恢复!

五、后续工作

  1. 清理脚本
    由于备份的tar包会越来越大,后续可以写些清理脚本,例如只保留最近几个tar包,其它的都删除掉等;
  2. 由于WAL日志文件比较大,可以制定删除策略,定期删除备份目录WAL日志。