0%

JNA(Java Native Access) provides Java programs easy access to native shared libraries without writing anything but Java code - no JNI or native code is required. This functionality is comparable to Windows’ Platform/Invoke and Python’s ctypes.

debian官方提供了脚本ftpsync来搭建源镜像,而apt-mirror是一个更简单便捷的源镜像搭建工具。

安装

1
$ sudo apt-get install apt-mirror

配置
配置文件/etc/apt/mirror.list只要修改很少的地方,大部分使用默认值即可。

这里使用中科大镜像ftp.cn.debian.org作为上游镜像,只镜像debian jessie amd64架构,不需要镜像源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
############# config ##################
#
# set base_path /var/spool/apt-mirror
#
# set mirror_path $base_path/mirror
# set skel_path $base_path/skel
# set var_path $base_path/var
# set cleanscript $var_path/clean.sh
# set defaultarch <running host architecture> # 默认架构与镜像主机的架构一致,这里是amd64
# set postmirror_script $var_path/postmirror.sh
# set run_postmirror 0
set nthreads 20
set _tilde 0
#
############# end config ##############

deb http://ftp.cn.debian.org/debian jessie main contrib non-free
deb http://ftp.cn.debian.org/debian/ jessie-backports main contrib non-free
deb http://ftp.cn.debian.org/debian/ jessie-proposed-updates main contrib non-free
deb http://ftp.cn.debian.org/debian/ jessie-updates main contrib non-free
deb http://ftp.cn.debian.org/debian-security/ jessie/updates main contrib non-free
#deb-src http://ftp.us.debian.org/debian unstable main contrib non-free

# mirror additional architectures
#deb-alpha http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-amd64 http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-armel http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-hppa http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-i386 http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-ia64 http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-m68k http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-mips http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-mipsel http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-powerpc http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-s390 http://ftp.us.debian.org/debian unstable main contrib non-free
#deb-sparc http://ftp.us.debian.org/debian unstable main contrib non-free

clean http://ftp.cn.debian.org/debian

自动同步

只需root权限cron自动运行apt-mirror命令即可。

1
# crontab -e

最后输入

1
2
# m h dom mon dow command
0 0 * * * apt-mirror

即可

发布

使用nginx发布源镜像

将apt-mirror的镜像目录链接到/var/www/mirror

1
# ln -sf /var/spool/apt-mirror/mirror/ftp.cn.debian.org/ mirror

然后将nginx默认主机default(或者单独虚拟主机)的根目录设置为/var/www/mirror,并开启目录列表

1
2
3
4
root /var/www/mirror
location / {
autoindex on;
}

其他机器就可以正常使用新建的源镜像了。

References:
[1]apt-mirror

===
[erq]

keepalived是一个路由软件,主要用于linux系统上的负载均衡(load balancing)和高可用(high availability)。
keepalived基于IPVS(IP Virtual Server)提供四层负载均衡功能,Keepalived提供一组checker根据服务器的健康状况来动态维护负载均衡资源池。

另一方面,keepalived基于VRRP协议提供高可用功能。可以由多台服务器组成一个热备组,一个热备组使用一个或一组虚拟ip对外提供服务。一个组内只有一台服务器接管虚拟ip对外提供服务,成为master,其余服务器为backup服务器。当master出现问题时,会执行一次选举(election)选出一台新的master服务器来接管虚拟ip对外提供服务。master服务器负责对虚拟ip请求的相应,并定时发出VRRP通告,backup服务器则待时而动。

keepalived的配置文件中由vrrp_sync_group和vrrp_instance提供高可用配置,由virtual_server_group和virtual_server提供负载均衡配置。

负载均衡配置提供HTTP_GET,SSL_GET,TCP_CHECK,SMTP_CHECK,MISC_CHECK,ICMP_CHECK等检查器,其中ICMP_CHECK工作在三层,TCP_CHECK工作在四层,而HTTP_GET则工作在七层。

高可用配置时,keepalive可以检测网络故障和自身运行状态,还可以设定用户脚本来检测服务器状态,从而当master出现故障时,可以通过重新选举来进行主备切换。

高可用配置

此处主要讲述高可用配置,下面是由两个服务器组成一个热备组用于PostgreSQL数据库高可用服务的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# Configuration File for keepalived

global_defs {
notification_email {
admin@domain.tld # 接收通知的email
}
notification_email_from admin@domain.tld # 发送通知的email地址
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id vmin # 标识机器,可以使用机器名,主备不能相同
}

vrrp_script chk_pgsql {
# script "/path/script.sh"
script "sudo -u postgres psql -c 'select 1'"
interval 2 # 2秒检查一次
weight -2 # 检查失败(返回值非0)时,优先级加-2,其他情况优先级保持不变.默认值为2
}

vrrp_instance VI_1 {
state BACKUP # 初始状态,主备可都设置为BACKUP,启动后会自动选举优先级高者为master
  # 如果高优先级的服务器设置为MASTER,就算设置了nopreempt,当重新启动keepalived实例时,仍然会抢占vip
interface br0 # vrrp绑定接口
#use_vmac # 使用虚拟MAC地址,地址格式为00-00-5E-00-01-<virtual_router_id>,主备设置相同
#vmac_xmit_base # 不使用虚拟接口收发VRRP报文
virtual_router_id 50 # 虚拟路由id,主备必须使用相同的配置
track_interface {
br0 # 监测的网络接口,当此接口不可用时会引起主备切换,可有多个被监测的接口
}
nopreempt # 非抢占模式,master设置为nopreempt,backup不要设置,
# 当高优先级的master重新恢复上线时,不会抢占当前低优先级的backup
priority 100 # 初始优先级,取值范围为0-255, master可设置为100, backup设置为99
advert_int 1 # VRRP 通告时间间隔
virtual_ipaddress {
192.168.0.200 # 对外提供服务器的虚拟地址,主备设置相同
}
authentication {
auth_type PASS # 认证类型,master与backup必须一致
auth_pass 1234 # 认证密码,只使用前8个字符,master与backup必须一致
}

# 如果使用参数调用的脚本,将脚本及参数用引号包围
notify_master "/usr/local/bin/notify.sh master" # 状态转移为master时执行的脚本
notify_backup "/usr/local/bin/notify.sh backup" # 状态转移为backup时执行的脚本
notify_fault "/usr/local/bin/nofity.sh fault" # 状态转移为故障时执行的脚本

# 当发生任何的状态变化时,在nofity_*脚本之后被调用,调用时会提供三个参数:
# $1 = "GROUP""INSTANCE"
# $2 = 组或实例的名字
# $3 = 变化的目标状态
# ("MASTER""BACKUP""FAULT")
nofity "/usr/local/bin/notify.sh"

# 发送邮件通知,使用global_defs中的定义
# smtp_alert

track_script {
chk_pgsql # 使用检查脚本
}
}

MASTER和BACKUP节点的优先级如何调整?

首先,每个节点有一个初始优先级,由配置文件中的priority配置项指定,MASTER节点的priority应比BAKCUP高。运行过程中keepalived根据vrrp_script的weight设定,增加或减小节点优先级。

规则如下:

  1. 当weight > 0时,vrrp_script script脚本执行返回0(成功)时优先级为priority + weight, 否则为priority。当BACKUP发现自己的优先级大于MASTER通告的优先级时,进行主从切换。
  2. 当weight < 0时,vrrp_script script脚本执行返回非0(失败)时优先级为priority + weight, 否则为priority。当BACKUP发现自己的优先级大于MASTER通告的优先级时,进行主从切换。
  3. 当两个节点的优先级相同时,以节点发送VRRP通告的IP作为比较对象,IP较大者为MASTER。
  4.  优先级并不会不断的提高或降低,只会根据脚本返回结果计算一次。

什么时候会发生主从切换?

当监测的网络接口发生故障、keepalived实例关闭或者主备优先级发生变化时,会重新选择新的master服务器来接管服务。

防止脑裂(brain split)

将主备服务器都设置为BACKUP状态,并且将master服务器(初始优先级高的服务器)配置为nopreempt,当master因为各种可能原因下线,然后重新恢复上线时,虽然恢复上线的master优先级高于当前master的优先级,但不会去抢夺控制权。

这样会造成一个问题,除非当前的master网络故障或keepalived实例停止,其优先级就算降低后低于原来的master服务器,因为设置了nopreempt,也不会切换到原来的master。所以原master恢复上线之前,应该降低其优先级,并且要低于当前master的优先级,然后去掉nopreempt,而当前master添加nopreempt。

如果没有设置nopreempt,当master因为网络原因短暂下线后,backup服务器接管vip,并且PostgreSQL备库升级为主库。当原来的master网络恢复,重新上线时,会重新成为master,而此时就有了两个主库,发生了分裂。

通知脚本
可以在通知脚本中处理PostgeSQL备库提升,主库停止,发送通知等各种事务。

查看VRRP通告
可以使用tcpdump命令监测VRRP通告,可以看到当前的master服务器为192.168.0.3

1
2
3
4
5
6
7
$ sudo tcpdump -vvv -n -i eth0 host 224.0.0.18
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:06:49.974761 IP (tos 0xc0, ttl 255, id 373, offset 0, flags \[none\], proto VRRP (112), length 40)
192.168.0.3 > 224.0.0.18: vrrp 192.168.0.3 > 224.0.0.18: VRRPv2, Advertisement, vrid 50, prio 98, authtype simple, intvl 1s, length 20, addrs: 192.168.0.200 auth "1234^@^@^@^@"
23:06:50.975113 IP (tos 0xc0, ttl 255, id 374, offset 0, flags \[none\], proto VRRP (112), length 40)
192.168.0.3 > 224.0.0.18: vrrp 192.168.0.3 > 224.0.0.18: VRRPv2, Advertisement, vrid 50, prio 98, authtype simple, intvl 1s, length 20, addrs: 192.168.0.200 auth "1234^@^@^@^@"
...

如果热备组内的服务器跨越子网,则交换路由设备必须开启VRRP多播报文转发。

References:
[1]man keepalived.conf
[2]Keepalived在PostgreSQL高可用中的运用
[3]Linux集群实现–Keepalived-1.2.7
[4]PostgreSQL+pgpooll+Keepalived双机HA方案
[5]keepalived vip漂移基本原理及选举算法
[6]Note on using VRRP with Virtual MAC address
[7]Keepalived 集群软件高级使用(工作原理和状态通知)
[8]keepalived
===
[erq]

IE9及以下版本并不能正确处理application/json返回类型,会提示文件下载,下载之后的文件内容就是返回的json数据串。
出现此种症状,只需针对特定版本IE浏览器,将返回类型设置为text/html或者text/plain即可解决此问题。

据说IE10也有此问题,木有测试,也有人说木有问题,可能是早期版本与后期更新版本有不同的表现吧。

References:
[1]Jquery + JSON: IE8/IE9 treats response as downloadable file

===
[erq]

cassandra的native_transport使用与rpc_address同样的绑定地址,默认情况下只绑定到localhost,所以从外部是无法访问到cassandra的9042端口的,使用native协议的驱动也就无法访问节点了。因此,可以在不启用rpc的情况下,修改rpc_address地址为外部可访问接口,从而可以从外部访问native transport。

当standby备库启动时,首先会调用restore_command来恢复所有可用的归档日志。

如果没有配置restore_command或者恢复了所有的归档日志restore_command失败之后,stanby备库会尝试pg_xlog目录下所有可用的WAL日志。

pg_xlog目录下没有WAL日志或者已有的WAL日志全部恢复完毕后,如果配置了流复制,standby会尝试连接到主库,开始进行流式复制。

如果失败,或者没有配置流式复制,或者连接中断,standby会重新开始一个新的恢复循环。

从WAL归档日志,到pg_xlog,再到流式复制的恢复循环会一直持续到服务器停止或者退出standby模式。

可以使用pg_ctl promote命令或者找到触发文件时,备库退出standby模式并切换到正常操作模式,可以接受正常的读写请求。
在failover完成之前,所有restore_command可以访问的以及pg_xlog目录下的WAL日志会被恢复,但是不会连接到主库进行流式恢复。

References:
[1]25.2.2. Standby Server Operation

===
[erq]

前面讲了基于日志文件传输的warm/hot standby配置,但日志文件传输的最大问题在于延迟。

单个日志文件要空间满了或者到了归档超时时间才会传输然后应用到备库,每个日志文件有16MB大小。这会造成较大的延迟,如果主库当机,则数据损失会较大。如果缩小归档超时时间,又会造成大量的空间浪费。

基于流复制则解决了这些问题。备库会连接到主库,不用等待WAL日志文件填满就可以立即传输完成的WAL记录到备库。
流复制默认是异步的,这样主库和备库之间会有微小的延迟,极端情况下可能会有极少的数据丢失。

流复制不依赖于归档模式archive_mode和归档日志。但是部署流复制的同时,开启归档也是有必要的。不能把鸡蛋放到一个篮子里。基于日志文件传输或流复制的高可用warm/hot standby配置仍然很难防范主库误删除数据的问题。

基于流复制同样可以实现warm或者hot standby配置。

流复制hot standby配置步骤

  1. 主库复制用户认证配置
    使用超级用户postgres或者新建一个超级用户作为备库连接到主库进行复制的用户,修改pg_hba.conf文件:
    1
    host replication postgres 192.168.0.0/24 md5
  2. 主库postgresql.conf配置
    1
    2
    3
    wal_level = hot_standby
    max_wal_senders = 5
    wal_keep_segments = 30
    max_wal_senders参数要比standby备库的数量再多一些,防止某些备库连接中断但尚未完全释放连接,如果参数设置过小,重新连接时可能会失败。
    流复制模式下,pg_xlog目录下的WAL日志文件会循环利用,如果备库应用日志跟不上主库产生日志的速度,或者备库故障导致无法应用主库的日志,此时,主库的日志可能会被覆盖,从而导致备库需要重新建立。
    当然如果同时做了归档备份,并且备库能访问到WAL日志归档目录,则备库会从归档备份目录来获取所需要的归档日志。

wal_keep_segments这个参数只能根据实际情况来估算,并不会很精确。参数设置小了,有可能需要的日志会被覆盖,设置大了会占用主库大量的存储空间。
流复制槽可以解决WAL日志循环覆盖的问题,只要备库没有应用主库的WAL日志,则这些日志会一直保存,直到备库不再需要这些日志。设置流复制槽时,如果备库一直下线,则需要注意主库的存储空间是否充裕。
3. 使用基础备份搭建备库
详见前文所述。
4. 备库端配置
postgresql.conf文件:

1
hot_standby = on

recovery.conf文件:

1
2
3
standby_mode = 'on'
primary_conninfo = 'host=192.168.0.80 port=5432 user=postgres password=pass'
trigger_file = '/var/lib/postgresql/trigger'
  1. 启动备库
    日志中有类似如下文本:
    1
    LOG: started streaming WAL from primary at 1/B0000000 on timeline 1
  2. 复制信息查看
    主库端:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    postgres=# select pg_current_xlog_location();
    pg_current_xlog_location
    --------------------------
    1/B0142910
    (1 row)
    postgres=# \\x
    Expanded display is on.
    postgres=# select * from pg_stat_replication;
    -\[ RECORD 1 \]----+-----------------------------
    pid 11471
    usesysid 19670
    usename reis
    application_name walreceiver
    client_addr 192.168.0.5
    client_hostname
    client_port 44247
    backend_start 2015-12-10 22:04:45.47403+08
    backend_xmin
    state streaming
    sent_location 1/B01572F0
    write_location 1/B01572F0
    flush_location 1/B01572F0
    replay_location 1/B0157268
    sync_priority 0
    sync_state async

备库端:

1
2
3
4
5
postgres=# select pg_last_xlog_receive_location();
pg_last_xlog_receive_location
-------------------------------
1/B0170B90
(1 row)

配置复制槽

  1. 主库端
    创建流复制槽:
    1
    2
    postgres=# select pg_create_physical_replication_slot('slot_1');
    ERROR: replication slots can only be used if max_replication_slots > 0

所以要修改postgresql.conf文件:

1
max_replication_slots = 5 

重启后,重新创建复制槽即可。

1
2
3
4
5
postgres=# select pg_create_physical_replication_slot('slot_1');
pg_create_physical_replication_slot
-------------------------------------
(slot_1,)
(1 row)
  1. 备库端
    recovery.conf文件:
    1
    primary_slot_name = 'slot_1'
    重新启动备库
  2. 主库端检查复制槽状态
    1
    2
    3
    4
    5
    6
    postgres=# select * from pg_replication_slots;
    slot_name plugin slot_type datoid database active xmin catalog_xmin restart_lsn
    -----------+--------+-----------+--------+----------+--------+------+--------------+-------------
    slot_1 physical t 1/B1021F18
    (1 row)

    复制槽slot_1已经启用。启用复制槽后参数wal_keep_segments就没用了。

References:
[1]25.2.5. Streaming Replication
[2]25.2.2. Standby Server Operation
[3]PostgreSQL Hot Standby
[4]postgresql高可用性之备库(二)
[5]25.3. Failover

===
[erq]

传统的备份恢复问题在于恢复时间过长,恢复时间有时候是无法接受的。因此有了高可用(High Availability)的概念。

高可用的实现方式多种多样,PostgreSQL内置支持基于WAL日志文件传输或流复制的方式来实现warm/hot standby备库从而实现数据库的高可用性。

这一篇文章只讲基于日志文件传输方式的warm/hot standby配置。
hot standby与warm standby的区别在于,hot standby在日志恢复的同时还可以提供只读查询,而warm standby只能进行日志恢复。

先决条件

主备库硬件可以不同,但硬件架构必须相同,字长也必须相同。PostgreSQL的主版本号必须相同,小版本号可以不同。
但是推荐主备库使用完全相同的版本号。

主备库升级时,要先升级备库,因为新版本的程序会兼容旧版本传输过来的wal日志,但反过来却不一定。

搭建warm standby备库

首先参考”PostgreSQL使用连续归档备份恢复数据库“在主库上进行一次基础备份,然后将其在备机上恢复。

然后recovery.conf文件中除了restore_comman参数之外,再打开standby_mode参数:

1
2
3
restore_command = 'cp /var/backups/postgresql/archive/%f %p'
standby_mode = 'on'
#archive_cleanup_command = ’pg_archivecleanup /path/to/archive %r’

如果需要清理standby不再需要的归档日志,可以配置archive_cleanup_command。不过一般来讲,为了备份的目的,归档日志应该dump到永久存储介质之后再行删除。

可以看到,此处与前文恢复数据库的主要区别既在于在recovery.conf中打开了standby_mode参数。这样以来,备库就会一直处于WAL归档日志恢复循环之中,直到主库失败,备库通过failover升级为主库,或者通过switchover升级为主库。

还可以在recovery.conf文件通过trigger_file参数指定一个触发文件,当standby服务器检测到这个文件时,就会结束恢复模式进入正常操作模式。

1
trigger_file = '/var/lib/postgresql/trigger'

此参数在standby_mode为off时无效。即使设置了此参数,仍然可以使用pg_ctl promote命令来结束恢复模式,从而可以升级为master数据库。

提升为hot standby备库

warm standby是不可查询的:

1
2
$ sudo -u postgres psql
psql: FATAL: the database system is starting up

在warm standby备库的基础上,主备库只需做少许参数配置即可升级到hot standby模式。

主库端postgresql.conf文件中:

1
wal_level = hot_standby

然后重新启动主库,并切换归档日志,从而使下一个归档日志具有hot standby信息:

1
2
$ sudo service postgresql restart
$ sudo -u postgres psql -c "select pg_switch_xlog()"

备库端postgresql.conf文件中:

1
hot_standby = on

然后重新启动备库即可。

如果启动备库时有类似如下错误:

1
2
FATAL: hot standby is not possible because max_connections = 100 is a lower setting than on the master server (its value was 500)
FATAL: hot standby is not possible because max_prepared_transactions = 0 is a lower setting than on the master server (its value was 50)

需要将standby上的max_connections,max_prepared_transactions参数设置为大于或等于master上对应参数的值,然后再重新启动。

启动成功后日志中会有类似的输出:

1
2
3
4
5
6
2015-12-09 21:53:19 CST \[23100-3\] LOG: entering standby mode
2015-12-09 21:53:19 CST \[23100-4\] LOG: restored log file "0000000100000001000000A9" from archive
2015-12-09 21:53:19 CST \[23100-5\] LOG: redo starts at 1/A9000090
2015-12-09 21:53:19 CST \[23100-6\] LOG: restored log file "0000000100000001000000AA" from archive
2015-12-09 21:53:19 CST \[23100-7\] LOG: consistent recovery state reached at 1/AB000000
2015-12-09 21:53:19 CST \[23099-1\] LOG: database system is ready to accept read only connections

可用使用以下命令进一步确认备库处于日志恢复状态:

1
2
3
4
5
sudo -u postgres psql -c "select pg_is_in_recovery()"
pg_is_in_recovery
-------------------
t
(1 row)

此命令在主库上执行会返回false。

可以在主库上修改数据库并切换归档,然后查询备库,检查数据是否有一样的变化。因为备库是基于日志文件传输的,所以如果不强制切换归档,备库要等到主库日志切换之后才能看到修改。

流复制有更高的实时性,从而数据丢失的风险更低。

References:
[1]Chapter 26. Recovery Configuration

===
[erq]

当数据库因为各种原因损坏时,连续归档备份就派上用场了,不过这种恢复在对停机时间很苛刻的环境下并不是很合适。
如果数据库很大的话,恢复时间可能是不可接受的,这时候就应该配置高可用实时复制系统,比如配置warm/hot standby备用机。

此文中的$PG_VERISON代表postgresql的主次版本号,比如当前为9.4。

数据恢复

  1. 停止服务器
    如果服务器还在运行中,应该先将其停止。如果硬件故障已无法开机,则可以在异机恢复。这里以异机恢复为例讲解。
    1
    $ sudo service postgresql stop
  2. 删除现有集群数据
    将当前postgresql集群数据目录下的所有文件删除,删除debian默认安装postgresql集群数据:
    1
    $ sudo rm -rf /var/lib/postgresql/$PG_VERSION/main/*
  3. 恢复基础备份
    将最近的基础备份恢复到集群的数据目录:
    1
    $ sudo -u postgres tar jxvf 20151207.tbz2 -C /var/lib/postgresql/$PG_VERSION/main/
    一定要注意恢复的数据文件的属主是运行PostgreSQL服务的系统用户,debian系统上为postgres,还应该保持原来的权限。
  4. pg_xlog目录
    如果进行基础备份时,pg_xlog目录下有未归档日志,恢复后应将目录下的所有文件删除,因为这些文件已经过时。如果没有pg_xlog目录则建立此目录,注意目录的属主和权限。
    主服务器崩溃后如果尚未归档的WAL日志还能访问,则应将其拷贝到pg_xlog目录,以尽最大可能恢复数据。
  5. 配置recovery.conf文件
    如果使用pg_basebackup命令进行备份时使用了-R(–write-recovery-conf)参数,则恢复后的数据目录中已经有了一个recovery.conf文件。如果没有,则可以拷贝一个模板到数据目录:
    1
    $ sudo -u postgres cp /usr/share/postgresql/$PG_VERSION/recovery.conf.sample /var/lib/postgresql/$PG_VERSION/main/

然后修改文件中的restore_command为适当的shell脚本以在恢复时可以读取到归档的WAL日志:

1
restore_command = 'cp /var/backups/postgresql/archive/%f %p'

因为此案例没有使用流复制,因此应该注释掉primary_conninfo参数。

恢复期间还应该修改pg_hba.conf文件或其他途径以阻止客户端连接。
6. 启动服务器,开始恢复

1
$ sudo service postgresql start

当所有的归档WAL恢复完毕,无法读取到其他更新的归档日志后,恢复就会自动结束,并且recovery.conf会被更名为recovery.done,防止意外重新进入restore过程。
恢复完毕后,可以允许客户端连接到服务器。
注意,恢复的最后阶段,日志中会出现No such file or directory字样的提示,这是正常的,因为恢复过程已经无法读取到其他的归档日志文件或时间线history文件。

1
2
3
4
5
6
7
8
9
10
11
12
2015-12-07 09:24:42 CST \[4906-5\] LOG: consistent recovery state reached at 1/86008798
cp: cannot stat ‘/var/backups/postgresql/archive/000000010000000100000087’: No such file or directory
2015-12-07 09:24:42 CST \[4906-6\] LOG: redo done at 1/86008798
2015-12-07 09:24:42 CST \[4906-7\] LOG: last completed transaction was at log time 2015-12-07 08:57:51.075265+08
2015-12-07 09:24:42 CST \[4906-8\] LOG: restored log file "000000010000000100000086" from archive
cp: cannot stat ‘/var/backups/postgresql/archive/00000002.history’: No such file or directory
2015-12-07 09:24:42 CST \[4906-9\] LOG: selected new timeline ID: 2
cp: cannot stat ‘/var/backups/postgresql/archive/00000001.history’: No such file or directory
2015-12-07 09:24:42 CST \[4906-10\] LOG: archive recovery complete
2015-12-07 09:24:42 CST \[4906-11\] LOG: MultiXact member wraparound protections are now enabled
2015-12-07 09:24:42 CST \[4924-1\] LOG: autovacuum launcher started
2015-12-07 09:24:42 CST \[4905-1\] LOG: database system is ready to accept connections

时间点恢复Point-in-Time Recovery (PITR)

默认情况下,恢复过程会一直持续到最后一个可用的WAL归档日志。
但是也可以在recovery.conf中设置参数来控制恢复到的目标点,这四个参数recovery_target,recovery_target_name,recovery_target_time和recovery_target_xid可以用来指定恢复的目标点,但同时只能有一个生效,如果指定多个,则以最后一个为准。

这几个参数的含义如下:

  • recovery_target
    该参数目前只有一个取值’immediate’,指示恢复应该在达到一直状态后尽快的结束。对于连续归档备份来说,基础备份结束时就处于一致状态。
  • recovery_target_name
    指定使用pg_create_restore_point()函数设定的恢复点名称。pg_create_restore_point()可以创建一个命名的恢复日志记录作为恢复目标,此函数只有超级用户可以访问。
  • recovery_target_time
    指定恢复所要到达的时间戳。注意,recovery_target_time 设置的时间格式,使用pg的now函数输出的格式。比如:
    1
    recovery_target_time = '2015-02-13 20:04:49.63197+08' 
  • recovery_target_xid
    指定恢复过程要到达的事务id。要注意,事务id在事务开始时顺序赋值,但每个事务的完成不一定会遵循先后顺序,因此指定这个参数,只有那些在此事务id之前启动的事务会被恢复。

还有几个参数会影响恢复目标的设定以及到达恢复目标时的动作:

  • recovery_target_inclusive
    此参数只影响recovery_target_time和recovery_target_xid参数。当设定为true时,会包含恢复目标,而设定为false时,会恢复到恢复目标之前而不包含恢复目标。此参数默认为true。
  • recovery_target_timeline
    指定在一个特定的时间线上恢复。默认在与制作基础备份相同的时间线上恢复。设置为latest会在归档日志中最新的时间线上恢复。
  • pause_at_recovery_target
    指定当到达恢复目标是是否应该暂停,默认为true。此参数的意图是允许查询数据库来检查当前的恢复目标是否是想要的恢复点。
    如果当前恢复目标并不是想要的,可以停止服务器,修改恢复目标设置,重新开始恢复数据库。
    可以使用pg_xlog_replay_resume()函数来结束暂停继续恢复数据库,此时会一直恢复到最后的一致状态。

没有指定恢复目标,或者没有处于hot_standby状态时,这个参数并不生效。

恢复时间线timeline

在做数据恢复时,如果能像时间旅行或者并行宇宙中那样来来回回随意穿梭就好了。比如,恢复一次之后,发现不满意,可以从头再来,直到找到满意的恢复点为止。

幸好,PostgreSQL支持时间线timeline,正好支持了这种“超能力”。如果没有时间线,每次恢复之后新产生的WAL日志极有可能会将部分之前的WAL日志覆盖,从而再也无法恢复到那些状态。

时间线是这样的,无论何时,一个恢复完成后,会创建一个新的时间线来标识此次恢复之后产生的WAL日志。时间线的id号是WAL日志文件名字的一部分,因此不会覆盖其他时间线上的WAL日志文件。

每次创建一个新的时间线时,PostgreSQL会创建一个新的时间线历史文件,后缀为.history。历史文件会标识此时间线是什么时候从那个时间线分支而来的。有了时间线历史文件,PostgreSQL就可以在含有多个时间线的归档文件中找到正确的WAL归档日志。

虽然时间线看起来的确很高能,但是无论如何也不可能恢复到制作基础备份之前的时间。

References:
[1]24.3. Continuous Archiving and Point-in-Time Recovery (PITR)
[2]26.2. Recovery Target Settings
[3]postgresql在线备份与恢复(三)

===
[erq]