一、为什么还需要备份
HAWQ作为一个数据库管理系统,备份与恢复是其必备功能之一。HAWQ的用户数据存储在HDFS上,系统表存储在master节点主机本地。HDFS上的每个数据块缺省自带三份副本,而且一个数据块的三份副本不会存储在同一个DataNode上,因此一个DataNode节点失效不会造成数据丢失。而配置了HDFS NameNode HA与HAWQ master HA后,NameNode和master的单点故障问题也得到了解决。似乎HAWQ没有提供额外备份功能的必要。
事实上,Hadoop集群上存储和处理的数据量通常非常大,大到要想做全备份,在时间与空间消耗上都是不可接受的。这也就是HDFS的数据块自带副本容错的主要原因。那么说到HAWQ在数据库中提供了数据备份功能,个人认为有三方面原因:一是自然地从PostgreSQL继承,本身就带备份功能;二是提供了一种少量数据迁移的简便方法,比如把一个小表从生产环境迁移到到测试环境;三是处理人为误操作引起的数据问题,例如误删除一个表时,就可以使用备份进行恢复,将数据丢失最小化。
二、备份方法
HAWQ提供以下三个应用程序帮助用户备份数据:
- gpfdist
- PXF
- pg_dump
gpfdist与PXF是并行数据装载/卸载工具,能提供最佳性能。pg_dump是一个从PostgreSQL继承的非并行应用。除此之外,有些情况下还需要从ETL过程备份原始数据。用户可以根据自己的实际场景选择适当的备份/恢复方法。
1. gpfdist和PXF
用户可以在HAWQ中使用gpfdist或PXF执行并行备份,将数据卸载到外部表中。备份文件可以存储在本地文件系统或HDFS上。恢复表的过程就是简单将数据从外部表装载回数据库。
(1)备份步骤
执行以下步骤并行备份:
- 检查数据库大小,确认文件系统有足够的空间保存备份文件。
- 使用pg_dump应用程序导出源数据库的schema。
- 在目标数据库中,为每个需要备份的表创建一个可写的外部表。
- 向新创建的外部表中装载表数据。
注意:将所有表的insert语句放在一个单独的事务中,以避免因在备份期间执行任何更新操作而产生问题。
(2)恢复步骤
执行以下步骤从备份还原:
- 创建一个数据库用于恢复。
- 从schema文件(在pg_dump过程中被创建)重建schema。
- 为数据库中的每个表建立一个可读的外部表。
- 从外部表向实际的表中导入数据。
- 装载完成后,运行ANALYZE命令,保证基于最新的表统计信息生成优化的查询计划。
(3)gpfdist与PXF的区别
gpfdist与PXF的区别体现在以下方面:
- gpfdist在本地文件系统存储备份文件,PXF将文件存储在HDFS上。
- gpfdist只支持平面文本格式,PXF还支持如AVRO的二进制格式,以及用户自定义的格式。
- gpfdist不支持生成压缩文件,PXF支持压缩,用户可以在Hadoop中指定使用的压缩算法,如org.apache.hadoop.io.compress.GzipCodec。
- gpfdist和PXF都提供快速装载性能,但gpfdist要比PXF快得多。
2. pg_dump与pg_restore
HAWQ支持PostgreSQL的备份与还原应用程序,pg_dump和pg_restore。pg_dump应用在master节点所在主机上创建一个单一的dump文件,包含所有注册segment的数据。pg_restore从pg_dump创建的备份中还原一个HAWQ数据库。大多数情况下,整库备份/还原是不切实际的,因为在master节点上没有足够的磁盘空间存储整个分布式数据库的单个备份文件。HAWQ支持这些应用的主要目的是用于从PostgreSQL向HAWQ迁移数据。下面是一些pg_dump用法的简单示例。
为数据库mytest创建一个备份,导出数据文件格式为tar:
$ pg_dump -Ft -f mytest.tar mytest
使用自定义格式创建一个压缩的备份,并且压缩级别为3:
$ pg_dump -Fc -Z3 -f mytest.dump mytest
使用pg_restore从备份还原:
$ pg_restore -d new_db mytest.dump
3. 原始数据备份
大多数情况使用gpfdist或PXF并行备份能够很好地工作。但以下两种情况不能执行并行备份与还原操作:
- 周期性增量备份。
- 导出大量数据到外部表,原因是此过程花费的时间太长。
在这些情况下,用户可以使用在ETL处理期间生成原始数据的备份,并装载到HAWQ。ETL程序提供了选择在本地还是HDFS存储备份文件的灵活性。
4. 备份方法对比
表1汇总了上面讨论的四种备份方法的区别。
gpfdist | PXF | pg_dump | 原始数据备份 | |
并行执行 | Yes | Yes | No | No |
增量备份 | No | No | No | Yes |
备份文件存储位置 | 本地文件系统 | HDFS | 本地文件系统 | 本地文件系统, HDFS |
备份文件格式 | Text,CSV | Text,CSV,自定义格式 | Text,Tar,自定义格式 | 依赖原始数据的格式 |
压缩 | No | Yes | 只支持自定义格式 | 可选 |
可伸缩性 | 好 | 好 | — | 好 |
性能 | 装载快速, 卸载快速 | 装载快速, 卸载一般 | — | 快(只拷贝文件) |
表1
5. 估计空间需求
在备份数据库前,需要确认有足够的空间存储备份文件。下面说明如何获取数据库大小和估算备份文件所需空间。
(1)使用hawq_toolkit查询需要备份的数据库大小。
gpadmin=# SELECT sodddatsize FROM hawq_toolkit.hawq_size_of_database WHERE sodddatname='db1';
sodddatsize
-------------
16063436
(1 row)
如果数据库中的表是压缩的,此查询显示压缩后的数据库大小。
(2)估算备份文件总大小
- 如果数据库表和备份文件都是压缩的,可以使用sodddatsize作为估算值。
- 如果数据库表是压缩的,备份文件是非压缩的,需要用sodddatsize乘以压缩率。尽管压缩率依赖于压缩算法,但一般可以使用经验值如300%进行估算。
- 如果备份文件是压缩的,数据库表是非压缩的,需要用sodddatsize除以压缩率。
(3)得出空间需求
- 如果使用PXF与HDFS,所需空间为:备份文件大小 * 复制因子。
- 如果使用gpfdist,每个gpfdist实例的所需空间为:备份文件大小 / gpfdist实例个数。这是因为表数据将最终分布到所有gpfdist实例。
三、备份与恢复示例
1. gpfdist示例
gpfdist是HAWQ的并行文件分发程序。hawq load应用程序操作gpfdist可读外部表,将外部表文件并行分发给HAWQ处理。当gpfdist用于可写外部表时,它并行接收HAWQ segment的输出流并写出到一个文件中。
为了使用gpfdist,在要还原备份文件的主机上启动gpfdist服务器程序。可以在同一个主机或不同主机上启动多个gpfdist实例。每个gpfdist实例需要指定一个对应目录,gpfdist从该目录向可读外部表提供文件,或者创建可写外部表的输出文件。例如,如果用户有一台有两块磁盘的专门用于备份的机器,则可以启动两个gpfdist实例,每个实例使用使用一块磁盘,如图1所示:
图1
也可以在每个segment主机上运行gpfdist实例,如图2所示。在备份期间,表数据将最终分布于所有在CREATE EXTERNAL TABLE定义的LOCATION子句中指定的gpfdist实例。
图2
(1)使用gpfdist备份
使用gpfdist备份mytest数据库:
- 创建备份位置,并启动gpfdist实例。在hdp4上启动一个gpfdist实例,端口号为8081,外部数据目录是/home/gpadmin/mytest_20170223。
gpfdist -d /home/gpadmin/mytest_20170223 -p 8081 &
# 如果碰到“cannot open shared object file: No such file or directory”类似的错误,需要先安装响应的依赖包,如:
gpfdist: error while loading shared libraries: libapr-1.so.0: cannot open shared object file: No such file or directory
yum install apr
gpfdist: error while loading shared libraries: libyaml-0.so.2: cannot open shared object file: No such file or directory
yum install libyaml
- 保存mytest数据库的schema。在HAWQ master节点所在主机,使用pg_dump应用程序,将mytest数据库的schema保存到文件mytest.schema。将schema文件拷贝到备份目录,用于以后还原数据库schema。
pg_dump --schema-only -f mytest.schema mytest
scp mytest.schema hdp4:/home/gpadmin/mytest_20170223
- 为数据库中的每个表创建一个可写的外部表。在LOCATION子句中指定gpfdist实例。本例使用CSV文本格式,但也可以选择其它固定分隔符的文本格式。
psql mytest
mytest=# create writable external table wext_base_table (like base_table)
mytest=# location('gpfdist://hdp4:8081/base_table.csv') format 'csv';
mytest=# create writable external table wext_t (like t)
mytest=# location('gpfdist://hdp4:8081/t.csv') format 'csv';
- 向外部表卸载数据。所有外部表的数据插入在一个事务中进行。
mytest=# begin;
mytest=# insert into wext_base_table select * from base_table;
mytest=# insert into wext_t select * from t;
mytest=# commit;
- (可选)停止gpfdist服务,为其它进程释放端口。下面的命令查找gpfdist进程号并杀掉该进程。
ps -efww|grep gpfdist|grep -v grep|cut -c 9-15|xargs kill -9
(2)从gpfdist备份还原
- 如果gpfdist没运行则启动gpfdist实例。下面的命令在hdp4上启动一个gpfdist实例。
gpfdist -d /home/gpadmin/mytest_20170223 -p 8081 &
- 创建一个新的数据库mytest2,并将mytest的schema还原到新库中。
[gpadmin@hdp3 ~]$ createdb mytest2
[gpadmin@hdp3 ~]$ scp hdp4:/home/gpadmin/mytest_20170223/mytest.schema .
[gpadmin@hdp3 ~]$ psql -f mytest.schema -d mytest2
- 为每个表创建可读的外部表。
[gpadmin@hdp3 ~]$ psql mytest2
mytest2=# create external table rext_base_table (like base_table) location('gpfdist://hdp4:8081/base_table.csv') format 'csv';
mytest2=# create external table rext_t (like t) location('gpfdist://hdp4:8081/t.csv') format 'csv';
注意:这里的location子句与前面备份时创建的可写外部表相同。
- 从外部表倒回数据。
mytest2=# insert into base_table select * from rext_base_table;
mytest2=# insert into t select * from rext_t;
- 装载数据后运行ANALYZE命令生成表的统计信息。
mytest2=# analyze base_table;
mytest2=# analyze t;
(3)gpfdist排错
- 确认HAWQ集群节点对gpfdist可访问。gpfdist在运行时要被segment实例访问。因此,必须确保HAWQ segment主机可以通过网络访问gpfdist。由于gpfdist程序是一个web服务器,可以从HAWQ集群的每个主机(master或segment节点)运行类似下面的命令测试连接:
$ wget http://gpfdist_hostname:port/filename
- 确认CREATE EXTERNAL TABLE定义中提供了gpfdist的正确的主机名、端口号和文件名。指定的文件名和路径应该对应gpfdist提供的文件(启动gpfdist程序时使用的目录路径)。
2. PXF示例
HAWQ Extension Framework (PXF)是一个允许HAWQ查询外部系统数据的扩展框架。
(1)使用PXF备份
使用PXF备份mytest数据库:
- 在HDFS上建立一个用作备份的文件夹。
[root@hdp3 ~]# su - hdfs
[hdfs@hdp3 ~]$ hdfs dfs -mkdir -p /backup/mytest-2017-02-23
[hdfs@hdp3 ~]$ hdfs dfs -chown -R gpadmin:gpadmin /backup
- 使用pg_dump导出数据库schema,并将schema文件存储到备份文件夹中。
[gpadmin@hdp3 ~]$ pg_dump --schema-only -f mytest.schema mytest
[gpadmin@hdp3 ~]$ hdfs dfs -copyFromLocal mytest.schema /backup/mytest-2017-02-23
- 为数据库的数据库中每个表创建一个可写的外部表。
[gpadmin@hdp3 ~]$ psql mytest
mytest=# create writable external table wext_base_table (like base_table)
mytest=# location('pxf://hdp1:51200/backup/mytest-2017-02-23/base_table?Profile=HdfsTextSimple&COMPRESSION_CODEC=org.apache.hadoop.io.compress.SnappyCodec')
mytest=# format 'text';
mytest=# create writable external table wext_t (like t)
mytest=# location('pxf://hdp1:51200/backup/mytest-2017-02-23/t?Profile=HdfsTextSimple&COMPRESSION_CODEC=org.apache.hadoop.io.compress.SnappyCodec')
mytest=# format 'text';
这里,所有base_table表的备份文件存储在/backup/mytest-2017-02-23/base_table文件夹中,所有t表的备份文件存储在/backup/mytest-2017-02-23/t文件夹中。外部数据文件使用snappy压缩保存到磁盘。
mytest=# begin;
mytest=# insert into wext_base_table select * from base_table;
mytest=# insert into wext_t select * from t;
mytest=# commit;
外部表使用snappy压缩时可能遇到如下错误:
mytest=# insert into wext_t select * from t;
ERROR: remote component error (500) from '172.16.1.125:51200': type Exception report message native snappy
library not available: this version of libhadoop was built without snappy support. description The server
encountered an internal error that prevented it from fulfilling this request. exception
java.lang.RuntimeException: native snappy library not available: this version of libhadoop was built without
snappy support. (libchurl.c:897) (seg0 hdp4:40000 pid=8565) (dispatcher.c:1801)
使用下面的方法解决该问题(参考https://issues.apache.org/jira/browse/HAWQ-951):
# 创建目录和软连接
mkdir -p /usr/lib/hadoop/lib && cd /usr/lib/hadoop/lib && ln -s /usr/hdp/current/hadoop-client/lib/native native
# 添加pxf-public-classpath属性
登录ambari,在Services -> PXF -> Configs -> Advanced pxf-public-classpath中添加一行:/usr/hdp/current/hadoop-client/lib/snappy*.jar
- (可选)改变备份文件夹的HDFS文件复制因子。缺省HDFS每个数据块复制三份以提供可靠性。根据需要,可以为备份文件降低这个数,以下命令将复制因子设置为2:
su - pxf
-bash-4.1$ hdfs dfs -setrep 2 /backup/mytest-2017-02-23
注意:这只改变已经存在的文件的备份因子,新文件仍然使用缺省的备份因子。
(2)从PXF备份还原
- 创建一个新的数据库并还原schema。
[gpadmin@hdp3 ~]$ createdb mytest3
[gpadmin@hdp3 ~]$ hdfs dfs -copyToLocal /backup/mytest-2017-02-23/mytest.schema .
[gpadmin@hdp3 ~]$ psql -f mytest.schema -d mytest3
- 为每个需要还原的表创建一个可读外部表。
[gpadmin@hdp3 ~]$ psql mytest3
mytest3=# create external table rext_base_table (like base_table)
mytest3=# location('pxf://hdp1:51200/backup/mytest-2017-02-23/base_table?Profile=HdfsTextSimple')
mytest3=# format 'text';
mytest3=# create external table rext_t (like t)
mytest3=# location('pxf://hdp1:51200/backup/mytest-2017-02-23/t?Profile=HdfsTextSimple')
mytest3=# format 'text';
除了不需要指定COMPRESSION_CODEC,location子句与前面创建可写外部表的相同。PXF会自动检测压缩算法。
- 从外部表装载数据。
mytest3=# insert into base_table select * from rext_base_table;
mytest3=# insert into t select * from rext_t;
- 导入数据后运行ANALYZE。
mytest3=# analyze base_table;
mytest3=# analyze t;