数据库服务可以采用主备的方式,在主要的服务器出错后,允许其他的服务器接管,继续对外提供服务,也允许多个服务器采用负载均衡的方式提供相同的服务。理想的服务器可以无缝的提供服务。
一些技术方案的比较:
>> 共享硬盘故障切换, 数据库共享硬盘矩阵,在主的数据库服务器失败之后,备机的服务器无缝的切换,就如同从主服务器崩溃重新启动。可以采用网络存储设备,但这种方式的局限性是矩阵硬盘出现异常之后,所有的服务都不能工作,同时在主服务在运行的过程中备机的服务不能访问共享的存储空间。
>> 文件系统主从复制,将主机文件系统的修改镜像到另一台设备的文件系统中。需要限制备机的服务必须有一个一致的文件系统拷贝,备机写入的操作必须和主机一致。在Linux环境下可用DRBD方案用于文件系统复制。
>> 基于即时恢复的暖、热备份方案,备机或者暖即持续的阅读WAL日志流。在主服务器出错后,备机包含主机的所有数据,因此可以快速作为新的主服务器对外提供服务,但这是异步操作,同时只能进行整个数据库的恢复。
>> 基于触发器的主备复制方案, 发送修改的查询到主服务器,主服务异步的发送数据变化到备机服务器,备机只能回复只读查询,备机可作为理想的数据仓库。典型的例子是Slony-I,该方案支持表级别的粒度,同时支持多个备机服务器,但由于异步的同步数据可能会在切换过程中出现数据丢失。
>>基于语句的复制中间件,该中间件接受每条SQL查询语句,并发送该语句到一个或者多个服务器,每个服务器独立的运行,涉及到读写的查询发送到所有的服务器,所有的服务器执行数据改变操作,但只读的查询可以仅仅发送到一个服务器。但如果是未涉及修改的简单广播查询,由于各个服务器是独立的,可能导致不一样的结果。发送SQL语句的方式可以在主备方案下实施,涉及到修改的操作都只发送给主机,由主机采用复制的发送发送到备机。以上的数据修改的事务必须在所有数据库上执行,通常采用二阶段提交方式。
>> 异步多主机复制,实现数据的一致性是比较有难度的,Bucardo是典型的解决方案。
>> 多主机同步复制,所有的服务器可接受写请求,在事务提交前所有修改数据将从该服务器传递到其他的服务器。大量的写活动可能会导致过度的锁,导致性能降低。实际上写性能相比但服务器更差。目前不支持该方案
>> 商业解决方案,开源进行的闭源扩展。
还有一些是上述方案没有考虑的:
>> 数据分区将表分成小的数据集合,所有的集合可以通过自身的服务器修改。
>> 多服务器并行查询执行,大部分的解决方案允许多个服务器处理多个查询,但很少有一个查询运行多个服务执行,这种方案运行多个服务器并发的完成单一的查询。
在数据库备份的过程中通常采用文件备份和日志在线归档相结合的热备份方案。关于备份和复制相关的一些属于:
>> 主服务器,被复制数据的源服务器,以及所有更新操作处理的服务器。
>> 备服务器,数据被复制到的服务器,通常在PostgreSQl中只支持读操作。
>> WAL日志,该日志是用来记录所有的事务,也被称为事务日志。PostgreSQL简单的使得日志可被备机获取,备机获取到之后就能执行日志中的事务。
>> 同步,为了保证数据零丢失,主机的事务直到所有备机更新完成后才算执行完成。
>> 异步,不等待备机执行,而是主机执行完成之后提交。这种主要在远程备份的时候使用。这种情况就有可能出现备机的数据滞后,备机可能丢失一些事务。
>> 流复制,这种不需要直接进行文件操作,使用PostgreSQL的连接协议传递WAL。这种减少了文件操作的时间。
>> 级联复制, 备机可以从相邻的备机接受日志,而不是直接从主机接受日志,这允许一个备机以主机的方式进行复制操作,但该备机仍然只能进行只读的查询。这种方式主要是减轻主机的带宽。
目前PostgreSQL各个版本的支持程度不一样,因此通常需要相同版本的PostgreSQL进行复制操作。同时由于涉及到文件操作,可能还需要安装的操作系统一致。同时目前只支持单主机的方式。
第三方的复制选择
>> Slony和Bucardo是最有名的关于PostgreSql的流复制软件。这两个开源软件方案解决了上述内置复制的问题。不需要版本一致,不需要相同的操作系统,支持多主机的场景。但是都是基于附加的触发器来初始化复制,同时通常都不支持数据库定义语言。
关于PostgreSQL将WAL日志保存在该数据集簇的pg_xlog文件夹下,这些日志保存了数据库所有的改变。主要是防止系统崩溃。通过从上次的检查点进行日志的重复,使得数据恢复。但通过第三方的策略就能实现数据库的备份。通过结合文件系统备份和备份的WAL日志文件。这种方式的好处:
>> 不需要文件系统的快照,只需要简单的归档工具即可。
>> 在线序列的WAL日志对于大型的数据库是有效的。
>> 不需要重放所有的归档日志文件
>> 在其他设备中将文件系统备份和归档文件实现一个备份。
主要对文件系统备份和在日志归档进行学习和操作:
(1) 进行文件系统的备份,需要关闭所有的节点,并对各个节点分别进行文件备份。
(2) 设置WAL归档, 在PostgreSQL中将产生大量连续的WAL记录,系统将这些WAL分成段文件,通常以16Mb分块。分块的文件名将表明该文件的位置。但关于这些文件如何处理并未给出方案,而是由数据库管理员选择,可以只是一个简单的复制操作cp。
首先需要时能WAL归档,wal_level设置为archive或者hot_standby。archive_mode设置为on,并设定archive_command的值,该值为归档的命令,即需要对归档的日志的处理方式。这些参数的配置在postgresql.conf中。
对应的命令通常为: ‘cp -i %p xxxxx/%f’其中的参数%p是指需要归档的文件的路劲名,%f是指文件的名,不包含路劲。即将归档的文件拷贝到某个文件中。
需要注意归档命令只有在完成归档才能返回0,PostgreSQL认为返回0。则认为归档成功,则将这些数据删除。返回非0,则认为归档未成功。归档命令应该设置为拒绝对已经存在的文件进行归档,这是保证完整的基础。因此归档命令中检测是否覆盖已经存在的文件是明智的。
通常采用如下的方式: archive_command='test ! -f /xxxx/%f && cp %p /xxx/%f'
归档的速度只需要满足能够跟上服务器产生日志的速度一致就可以啦。WAL日志保存了对数据库中数据相关的修订,但并不会恢复对配置文件的修订。
基线备份的过程
>> 确定WAL归档使能并能够正常的工作。
>> 连接到数据库,并执行如下的操作 SELECT pg_start_backup('label');该label可以为任意的字符串,只是用来表示备份操作。pg_start_backup将创建一个标识文件:backup_label的形式。该文件在pg_stop_backup()时被删除。
>> 执行文件系统备份,没有必要关闭服务器。
>> 停止备份,通过select pg_stop_backup()。这终止了备份模式,并将WAL切换到下一个块中。由于备份过程中已经将最后的WAL存档。该命令会保证所有的日志都归档完成。也可以使用pg_basebackup工具实现自动的使能、复制、停止的过程。
使用持续的归档备份文件恢复
>> 关闭服务器
>> 拷贝pg_xlog文件,这些文件是系统下电时没有归档的文件。
>> 移除相关的子文件夹。
>> 采用备份的文件系统进行恢复数据库。必须要有数据库的超级用户权限。
>> 删除pg_xlog中的文件
>> 将步骤2中没有归档的文件拷贝到pg_xlog中。
>> 在数据库目录下创建恢复配置文件recovery.conf。可能还需要修改pg_hba.conf,允许普通用户连接到数据库。
>> 启动数据库服务器,服务器首先进入到恢复模式中,处理已经归档的WAL文件。当备份完成之后将会将recovery.conf更名为recovery.done,然后服务器进入正常操作模式。
>> 检查数据库的内容是否正常,如果不正常重新进行恢复操作。
其中最主要的部分是recovery.conf的处理,该文件可以以share文件中的recovery.conf.sample为蓝本。修改其中的restore_command,该命令用于拷贝对应的归档文件到集群目录中。
进行如下的配置过程:
>> 首先进行基线文件备份
1、 设置待备份的数据库的配置文件postgresql.conf,其中
wal_level = archive # minimal, archive, or hot_standby
archive_mode = on
archive_command = 'test ! -f /ire_gp/data/archive_wal/%f && cp %p /ire_gp/data/archive_wal/%f'
同时创建好对应的归档文件夹目录
2、重新启动修改配置的数据库节点
[xxxx@drdb02 pg_xlog]$pg_ctl stop -Z datanode -D /ire_gp/data/datanode12
[xxxx@drdb02 pg_xlog]$ps aux | grep datanode12
xxxx 15991 0.0 0.0 103240 872 pts/3 S+ 10:46 0:00 grep datanode12
[xxxx@drdb02 pg_xlog]$pg_ctl start -Z datanode -D /ire_gp/data/datanode12 -l /ire_gp/data/datanode12/datanode12.log
3、执行对应的归档操作的开始
[xxxx@drdb02 data]$psql -p 11971 postgres
psql (xxxx 2.1.0-9.2.4, based on PG 9.2.4 (xxxx 2.1.0-9.2.4))
Type "help" for help.
xxxx[xxxx@postgres]> select pg_start_backup('hotbackup');
pg_start_backup
-----------------
0/B000020
(1 row)
4、执行文件系统的备份
[xxxx@drdb02 data]$tar -cvf /ire_gp/data/datanode12bk/backup.tar /ire_gp/data/datanode12
5、归档操作的结束
xxxx[xxxx@postgres]> select pg_stop_backup();
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
pg_stop_backup
----------------
0/B000258
(1 row)
查看对应目录下的归档日志,以下说明归档完成
[xxxx@drdb02 data]$cd archive_wal/
[xxxx@drdb02 archive_wal]$ll
total 49156
-rw------- 1 xxxx ire_gp 16777216 Jun 5 10:46 000000010000000000000009
-rw------- 1 xxxx ire_gp 16777216 Jun 5 10:49 00000001000000000000000A
-rw------- 1 xxxx ire_gp 16777216 Jun 5 10:50 00000001000000000000000B
-rw------- 1 xxxx ire_gp 293 Jun 5 10:50 00000001000000000000000B.00000020.backup
[xxxx@drdb02 archive_wal]$ls -ltrh
total 49M
-rw------- 1 xxxx ire_gp 16M Jun 5 10:46 000000010000000000000009
-rw------- 1 xxxx ire_gp 16M Jun 5 10:49 00000001000000000000000A
-rw------- 1 xxxx ire_gp 293 Jun 5 10:50 00000001000000000000000B.00000020.backup
-rw------- 1 xxxx ire_gp 16M Jun 5 10:50 00000001000000000000000B
恢复数据的过程:
(1) 关闭当前正在运行的数据库
[xxxx@drdb02 data]$pg_ctl stop -D /ire_gp/data/datanode12
(2) 解压缩对应的备份文件系统
[xxxx@drdb02 data]$cd datanode12bk/
[xxxx@drdb02 datanode12bk]$
[xxxx@drdb02 datanode12bk]$
[xxxx@drdb02 datanode12bk]$ll
total 112232
-rw-r--r-- 1 xxxx ire_gp 114923520 Jun 5 10:50 backup.tar
[xxxx@drdb02 datanode12bk]$
[xxxx@drdb02 datanode12bk]$
[xxxx@drdb02 datanode12bk]$tar
tar targetcli
[xxxx@drdb02 datanode12bk]$tar -xvf backup.tar
(3) 删除解压缩之后pg_xlog中的文件
[xxxx@drdb02 datanode12bk]$cd ire_gp/data/datanode12/pg_xlog/
[xxxx@drdb02 pg_xlog]$rm -fr *
[xxxx@drdb02 pg_xlog]$ll
total 0
(4) 添加一个标识恢复的配置文件recovery.conf,该文件可以以recovery.conf.sample为模板进行修改,主要修改restore_command参数:
restore_command = 'cp /ire_gp/data/archive_wal/%f %p',实现将归档文件中的数据拷贝到数据库当前路径的过程。
(5) 由于在服务器异常之后,通常还存在没有归档的日志,因此需要将未归档的日志拷贝到归档的目录中。
[xxxx@drdb02 archive_wal]$cp /ire_gp/data/datanode12/pg_xlog/00000001000000000000000C 00000001000000000000000C
[xxxx@drdb02 archive_wal]$cp /ire_gp/data/datanode12/pg_xlog/00000001000000000000000D 00000001000000000000000D
[xxxx@drdb02 archive_wal]$cp /ire_gp/data/datanode12/pg_xlog/00000001000000000000000E 00000001000000000000000E
[xxxx@drdb02 archive_wal]$cp /ire_gp/data/datanode12/pg_xlog/00000001000000000000000F 00000001000000000000000F
[xxxx@drdb02 archive_wal]$cp /ire_gp/data/datanode12/pg_xlog/000000010000000000000010 000000010000000000000010
(6)重新启动新的节点。
[xxxx@drdb02 archive_wal]$pg_ctl start -Z datanode -D /ire_gp/data/datanode12bk/ire_gp/data/datanode12/ -l /ire_gp/data/datanode12bk/ire_gp/data/datanode12/datanode12.log
pg_ctl: another server might be running; trying to start server anyway
[xxxx@drdb02 archive_wal]$ps aux | grep postg
xxxx 1194 0.2 0.2 740040 96380 pts/0 S 11:20 0:00 /ire_gp/xxxx/plus/bin/postgres --datanode -D /ire_gp/data/datanode12bk/ire_gp/data/datanode12
(7) 恢复完成之后,通常会将recovery.conf更名为recovery.done
(8) 检查恢复后的文件是否存在对应的数据内容。
以上的过程实现了基于文件系统的备份和归档处理,实际上只是完成了某个时间段的数据的备份,并没有实现实时的备份。而且恢复需要时间,因此若能够持续的进行备份将提高切换的效率。
以上的操作都是手动的处理,而且很容易出现错误,因此若能实现自动的控制将是不错的选择,进行持续的归档可实现一个高可用的集群。在主服务器出现异常之后,由备服务器提供服务,主备服务器共同提供这种能力,直接将一台数据库服务器的日志传动到另一台服务器称为日志传送。PostgreSQL实现了基于文件的传送,16M的块进行传递。日志传递是异步的,日志在事务提交之后才传递。因此会出现数据的丢失问题。
备机模式下的服务器持续的运用从主机端接收到的WAL日志。备机服务可以读WAL归档的文件或者直接通过TCP连接从主机端接收日志。备机服务器尝试恢复备机集群pg_xlog中的WAL日志。
开始阶段备机恢复所有的WAL。通过recovery.conf中的restore_command进行恢复。如果配置了流复制,备机将连接服务器并开始流的WAL到pg_xlog中。备机在pg_ctl_promote或者触发器文件存在将进行切换操作,切换到正常的操作模式。在切换前,WAL可里面在pg_xlog中获取。
日志传递中基于文件的传送需要将归档文件保存在备机可访问的区域,如果采用流复制的方式,首先主机必须允许备机服务器的连接,通常需要通过修改pg_hba.conf实现。同时还需要进行流拷贝的相关设置。
基本的实现过程如下所示:
>> 配置主服务器的相关参数,创建当前数据库集群的归档文件目录,然后需要配置当前的模式,通过修改postgresql.conf的配置:
archive_mode= on
archive_command = 'cp %p /ire_gp/xxxx/datanodearch/%f'
max_wal_senders = 10
wal_level = hot_standby # minimal, archive, or hot_standby
>> 修改主服务器的客户端认证配置,运行流复制, 通过pg_hba.conf进行设置:
# replication privilege.
local replication xxxx trust
host replication xxxx 127.0.0.1/32 trust
>> 关闭服务器进行文件系统的拷贝,并将pg_xlog文件夹中的相关文件删除。
[xxxx@drdb02 datanode11bk]$cp -fr ../datanode11/* .
[xxxx@drdb02 datanode11bk]$ll
total 1632
drwx------ 6 xxxx ire_gp 4096 Jun 8 14:21 base
-rw------- 1 xxxx ire_gp 1573341 Jun 8 14:21 datanode11.log
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 global
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_clog
-rw------- 1 xxxx ire_gp 4996 Jun 8 14:21 pg_hba.conf
-rw------- 1 xxxx ire_gp 1636 Jun 8 14:21 pg_ident.conf
drwx------ 4 xxxx ire_gp 4096 Jun 8 14:21 pg_multixact
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_notify
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_serial
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_snapshots
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_stat_tmp
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_subtrans
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_tblspc
drwx------ 2 xxxx ire_gp 4096 Jun 8 14:21 pg_twophase
-rw------- 1 xxxx ire_gp 4 Jun 8 14:21 PG_VERSION
drwx------ 3 xxxx ire_gp 4096 Jun 8 14:21 pg_xlog
-rw------- 1 xxxx ire_gp 21733 Jun 8 14:21 postgresql.conf
-rw------- 1 xxxx ire_gp 78 Jun 8 14:21 postmaster.opts
[xxxx@drdb02 pg_xlog]$rm -fr *
[xxxx@drdb02 pg_xlog]$
[xxxx@drdb02 pg_xlog]$ll
total 0
>> 修改备机实例目录下的配置文件,主要修改一些参数用于标识该服务器为备机,同时若在同一台服务器可能需要设置为不同的端口等相关的处理。
port = 11962 # (change requires restart)
pooler_port = 12962 # Pool Manager TCP port
hot_standby = on # "on" allows queries during recovery
>> 拷贝一个recovery.conf文件,并根据对应的需要设置对应的参数,主要是restore_command。
standby_mode = on //当前为备机模式
primary_conninfo = 'host=localhost port=11961' //连接的对端的
trigger_file = 'failover.now' //进行切换的触发文件,即存在该文件时将进行触发
restore_command = 'cp /ire_gp/xxxx/datanodearch/%f %p' //对应的归档文件拷贝到当前路径的方式
>> 启动备机服务器
[xxxx@drdb02 datanode11bk]$pg_ctl start -Z datanode -D /ire_gp/data/datanode11bk/ -l /ire_gp/data/datanode11bk/datanode11_back.log
[xxxx@drdb02 datanode11bk]$vi datanode11_back.log //查看备机的情况,启动异常,但是可以忽略,主机还未启动
cp: cannot stat `/ire_gp/data/archive_wal/000000010000000000000002': No such file or directory
FATAL: could not connect to the primary server: could not connect to server: Connection refused
Is the server running on host "localhost" (::1) and accepting
TCP/IP connections on port 11961?
could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP/IP connections on port 11961?
>> 启动主机服务器
[xxxx@drdb02 datanode11bk]$pg_ctl start -Z datanode -D /ire_gp/data/datanode11/ -l /ire_gp/data/datanode11/datanode11.log
[xxxx@drdb02 datanode11bk]$vi datanode11_back.log //查看备机端的流处理情况
FATAL: could not connect to the primary server: could not connect to server: Connection refused
Is the server running on host "localhost" (::1) and accepting
TCP/IP connections on port 11961?
could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP/IP connections on port 11961?
cp: cannot stat `/ire_gp/xxxx/datanodearch/000000010000000000000005': No such file or directory
cp: cannot stat `/ire_gp/xxxx/datanodearch/000000010000000000000005': No such file or directory
LOG: streaming replication successfully connected to primary //执行流复制成功
LOG: redo starts at 0/5000080 //redo根据日志进行事务的重新执行,进行事务的更新操作
[xxxx@drdb02 datanode11bk]$
在测试的过程中出现了如下的问题:
cp: cannot stat `/ire_gp/data/archive_wal/000000010000000000000002': No such file or directory
LOG: streaming replication successfully connected to primary
WARNING: WAL was generated with wal_level=minimal, data may be missing
HINT: This happens if you temporarily set wal_level=minimal without taking a new base backup.
FATAL: hot standby is not possible because wal_level was not set to "hot_standby" on the master server
HINT: Either set wal_level to "hot_standby" on the master, or turn off hot_standby here.
LOG: startup process (PID 12549) exited with exit code 1
LOG: aborting startup due to startup process failure
出现这个原因的问题是当前进行重新执行的WAL文件000000010000000000000002是主机在minimal模式下产生的日志。同时检查配置文件,确认相关的参数正确。通常出现这个原因的问题是在备份文件系统的过程中存在问题,最简单的方式是重新备份一次文件系统,然后备机在最新的文件系统之上进行恢复操作,即在关闭主机的服务器之后再进行备份,然后先启动备机的数据库,最后启动主机服务器。
通过上述的分析可知,主备机模式本质上是主机写WAL到归档文件夹中,备机从归档文件夹中读取日志。实际上只是通过了两个命令:在主机端的archive_command和备机端的restore_command命令。
在上述的测试过程中,restore_command命令从归档文件读取WAL的操作,只是采用简单的拷贝工作,拷贝操作实际是只有归档文件中有数据才需要拷贝,而没有数据的过程中并不需要拷贝,因此可以在restore_command命令中采用脚本的方式,通过循环的poll获取数据。但为了退出循环,添加触发条件的判断。
在数据库中为了保证写硬盘操作成功,通常都是些进行硬盘数据的写入前会先写WAL日志。
WAL日志是标准的方法来保证数据的一致性。WAL日志减少了写硬盘的操作。
在Postgres-XL中由于是多个节点的事件同步机制,当个节点的操作可能导致其他节点的问题,在多事务的情景下,各个节点处理事务的顺序存在差异。因此在多个节点的环境下进行恢复几乎是不太可能完成的。Postgres-XL提供了一种机制barriers(栅栏)来完成这种同步点。这个栅栏可以通过SQL来创建。
关于恢复相关的配置都是在recovery.conf配置文件中,该文件中主要包括归档的恢复设置,恢复目标的设置,备机服务器设置。众所周知,recovery.conf可以由share/recovery.conf.sample修改。
归档恢复设置
restore_command: 通常为shell命令来获取归档文件序列的部分片段。在归档恢复的设置中必须设置,但在流复制的过程中可不设置。%f是获取的归档文件的文件名,不包含路劲,%p代表复制的文件路劲名,通常是相对于当前工作目录。通常只有复制成功之后才会返回0。%r是指包含了最后有效的开始点的文件名。
archive_cleanup_command: 目的是为备机提供一个机制将不再需要的日志删除。因此当文件早于%r的都会被删除。通常结合pg_archivecleanup模块实现单备机的配置。在多备机的情况下需要保证该WAL在所有主机不再有效之后才能删除。
recovery_end_command: 该命令只有在恢复结束时才会执行一次。通常用于完成恢复或者流复制的清除工作。
备机服务器设置
standby_mode: 标识是否将PostgreSQL作为备机,当为on时,最后的WAL文件达到后并不会停止恢复,而是继续从归档文件中获取日志。
primary_conninfo: 指明备机服务器连接主机的连接信息,该连接信息表明主机的地址和端口号。但该参数只有在standby_mode有效时才生效。
trigger_file: 用于表示恢复结束的触发文件。但也可以通过pg_ctl_promote将备机提升为主机.