postgresql主从复制实现方式之一:
基于Standby的异步流复制,这是PostgreSQL9.x版本(2010.9)之后提供的一个很nice的功能,类似的功能在Oracle中是11g之后才提供的active dataguard和SQL Server 2012版本之后才提供的日志传送,此处再次为pg鼓掌,确实是一个很棒的开源数据库。废话不多说,本篇blog就详细记录一下在pg9.5中实现Hot Standby异步流复制的完整配置过程和注意事项。

Standby数据库原理:
首先我们做主从同步的目的就是实现db服务的高可用性,通常是一台主数据库提供读写,然后把数据同步到另一台从库,然后从库不断apply从主库接收到的数据,从库不提供写服务,只提供读服务。在postgresql中提供读写全功能的服务器称为primary database或master database,在接收主库同步数据的同时又能提供读服务的从库服务器称为hot standby server。

PostgreSQL在数据目录下的pg_xlog子目录中维护了一个WAL日志文件,该文件用于记录数据库文件的每次改变,这种日志文件机制提供了一种数据库热备份的方案,即:在把数据库使用文件系统的方式备份出来的同时也把相应的WAL日志进行备份,即使备份出来的数据块不一致,也可以重放WAL日志把备份的内容推到一致状态。这也就是基于时间点的备份(Point-in-Time Recovery),简称PITR。而把WAL日志传送到另一台服务器有两种方式,分别是:

    WAL日志归档(base-file)
    流复制(streaming replication)

第一种是写完一个WAL日志后,才把WAL日志文件拷贝到standby数据库中,简言之就是通过cp命令实现远程备份,这样通常备库会落后主库一个WAL日志文件。而第二种流复制是postgresql9.x之后才提供的新的传递WAL日志的方法,它的好处是只要master库一产生日志,就会马上传递到standby库,同第一种相比有更低的同步延迟,所以我们肯定也会选择流复制的方法。
在实际操作之前还有一点需要说明就是standby的搭建中最关键的一步——在standby中生成master的基础备份。postgresql9.1之后提供了一个很方便的工具—— pg_basebackup,关于它的详细介绍和参数说明可以在官网中查看(pg_basebackup tool).下面在搭建过程中再做相关具体说明,关于一些基础概念和原理先介绍到这里。
pg_basebackup tool官网介绍:
https://www.postgresql.org/docs/current/static/p-pgbasebackup.html


详细配置环境:
下面开始实战,首先准备两台服务器,我这里开了2个虚机做测试,分别是:
主库(master)CentOS release 6.5 (Final) 10.0.0.100 postgresql 9.5.9
从库(standby)CentOS release 6.7 (Final) 10.0.0.110 postgresql 9.5.9

从主库配置开始。
首先要提前在master机器10.0.0.100安装好postgresql,采用的是二进制安装包,具体参考本博文的postgresql二进制安装过程。

主库配置:
注意此处的操作都是在主库(10.0.0.100)上进行的,首先打开数据目录下的postgresql.conf文件然后做以下修改:

1.listen_address = ‘*’(默认localhost)
2.port = 10280       (默认是5432)
3.wal_level = hot_standby(默认是minimal)
4.max_wal_senders=2(默认是0)
5.wal_keep_segments=64(默认是0)
下面对上述参数稍作说明
第一个是监听任何主机,wal_level表示启动搭建Hot Standby,max_wal_senders则需要设置为一个大于0的数,它表示主库最多可以有多少个并发的standby数据库,而最后一个wal_keep_segments也应当设置为一个尽量大的值,以防止主库生成WAL日志太快,日志还没有来得及传送到standby就被覆盖,但是需要考虑磁盘空间允许,一个WAL日志文件的大小是16M:
[postgres@localhost data]$ cd /data/pgsql100/data/pg_xlog/
[postgres@localhost pg_xlog]$ ls
000000010000000000000001  000000010000000000000002  000000010000000000000003  000000010000000000000004  000000010000000000000005  archive_status
[postgres@localhost pg_xlog]$ du -sh *
16M    000000010000000000000001
16M    000000010000000000000002
16M    000000010000000000000003
16M    000000010000000000000004
16M    000000010000000000000005
4.0K    archive_status
如上,一个WAL日志文件是16M,如果wal_keep_segments设置为64,也就是说将为standby库保留64个WAL日志文件,那么就会占用16*64=1GB的磁盘空间,所以需要综合考虑,在磁盘空间允许的情况下设置大一些,就会减少standby重新搭建的风险。接下来还需要在主库创建一个超级用户来专门负责让standby连接去拖WAL日志:
CREATE ROLE replica login replication encrypted password 'replica';
接下来打开数据目录下的pg_hba.conf文件然后做以下修改:
[postgres@localhost pg_xlog]$ tail -2 /data/pgsql100/data/pg_hba.conf
#host    replication     postgres        ::1/128                 trust
host    replication     replica     10.0.0.110/32                md5

如上,这行配置的意思是允许用户replica从10.0.0.110/32网络上发起到本数据库的流复制连接,简言之即允许从库服务器连接主库去拖WAL日志数据。主库配置很简单,到此就算结束了,启动主库并继续配置从库
pg_ctl -D /data/pgsql100/data -l /data/pgsql100/log/postgres.log stop
pg_ctl -D /data/pgsql100/data -l /data/pgsql100/log/postgres.log start

从库配置:
首先要说明的是从库上一开始也是需要安装postgresql数据库服务的,应为需要pg_basebackup命令工具才能在从库上生成的master主库的基础备份。但是还要强调一点的是:从库上初始化数据库时指定的数据目录/data/psql110/data需要清空,才可以在从库上使用pg_basebackup命令工具来生成master主库的基础备份数据。

从此处开始配置从库(10.0.0.110),首先要通过pg_basebackup命令行工具在从库上生成基础备份:
[postgres@localhost data]$ pg_basebackup -h 10.0.0.100 -U replica -p 10280 -F p -x -P -R -D /data/psql110/data/ -l replbackup
Password: 密码(replica)
46256/46256 kB (100%), 1/1 tablespace
[postgres@localhost data]$
简单做一下参数说明(可以通过pg_basebackup --help进行查看),
-h指定连接的数据库的主机名或IP地址,这里就是主库的ip。
-U指定连接的用户名,此处是我们刚才创建的专门负责流复制的repl用户。
-F指定了输出的格式,支持p(原样输出)或者t(tar格式输出)。
-x表示备份开始后,启动另一个流复制连接从主库接收WAL日志。
-P表示允许在备份过程中实时的打印备份的进度。
-R表示会在备份结束后自动生成recovery.conf文件,这样也就避免了手动创建。

-D指定把备份写到哪个目录,这里尤其要注意一点就是做基础备份之前从库的数据目录(/data/psql110/data/)目录需要手动清空。
-l表示指定一个备份的标识。

[postgres@localhost data]$ cat /data/psql110/data/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=replica password=replica host=10.0.0.100 port=10280 sslmode=prefer sslcompression=1 krbsrvname=postgres'


运行命令后看到如下进度提示就说明生成基础备份成功:
[postgres@localhost data]$ pg_basebackup -h 10.0.0.100 -U replica -p 10280 -F p -x -P -R -D /data/psql110/data/ -l replbackup
Password: 密码(replica)
46256/46256 kB (100%), 1/1 tablespace
[postgres@localhost data]$
如上由于我们在pg_hba.conf中指定的md5认证方式,所以需要输入密码。最后还需要修改一下从库数据目录下的postgresql.conf文件,将hot_standby改为启用状态,即hot_standby=on。到此为止就算配置结束了,我们现在可以启动从库,
[postgres@localhost data]$ egrep -v '^#|^$' /data/psql110/data/postgresql.conf|grep "hot_standby"
wal_level = hot_standby            # minimal, archive, hot_standby, or logical
hot_standby = on            # "on" allows queries during recovery

[postgres@localhost data]$ pg_ctl -D /data/psql110/data -l /data/psql110/log/postgres.log start
server starting

从库上查看到流复制进程:

[postgres@localhost data]$ ss -lntup|grep postgres
tcp    LISTEN     0      128                   :::10280                :::*      users:(("postgres",23161,4))
tcp    LISTEN     0      128                    *:10280                 *:*      users:(("postgres",23161,3))
[postgres@localhost data]$ ps -ef|grep postgres
root       5663   4716  0 18:12 pts/0    00:00:00 su - postgres
postgres   5664   5663  0 18:12 pts/0    00:00:00 -bash
postgres   5855   5664  0 18:13 pts/0    00:00:00 /bin/bash /usr/local/pgsql/bin/psql
postgres   5857   5855  0 18:13 pts/0    00:00:00 /usr/local/pgsql/bin/psql.bin
root      12406   7244  0 18:34 pts/1    00:00:00 su - postgres
postgres  12407  12406  0 18:34 pts/1    00:00:00 -bash
root      13861  13810  0 18:47 pts/3    00:00:00 su - postgres
postgres  13862  13861  0 18:47 pts/3    00:00:00 -bash
root      21768  21736  0 19:54 pts/2    00:00:00 su - postgres
postgres  21769  21768  0 19:54 pts/2    00:00:00 -bash
postgres  23161      1  0 20:05 pts/2    00:00:00 /usr/local/pgsql/bin/postgres -D /data/psql110/data
postgres  23164  23161  0 20:05 ?        00:00:00 postgres: startup process   recovering 000000010000000000000007
postgres  23165  23161  0 20:05 ?        00:00:00 postgres: checkpointer process                     
postgres  23166  23161  0 20:05 ?        00:00:00 postgres: writer process                           
postgres  23167  23161  0 20:05 ?        00:00:00 postgres: stats collector process                  
postgres  23168  23161  0 20:05 ?        00:00:00 postgres: wal receiver process   streaming 0/7000140
postgres  23240  21769  0 20:06 pts/2    00:00:00 ps -ef
postgres  23241  21769  0 20:06 pts/2    00:00:00 grep postgres

主库上查看到流复制进程:

[postgres@localhost pg_xlog]$ ps -ef|grep postgres
root       2904   2642  0 00:40 pts/0    00:00:00 su - postgres
postgres   2905   2904  0 00:40 pts/0    00:00:00 -bash
postgres   2939      1  0 00:42 pts/0    00:00:00 /usr/local/pgsql/bin/postgres -D /data/pgsql100/data
postgres   2941   2939  0 00:42 ?        00:00:00 postgres: checkpointer process                      
postgres   2942   2939  0 00:42 ?        00:00:00 postgres: writer process                            
postgres   2943   2939  0 00:42 ?        00:00:00 postgres: wal writer process                        
postgres   2944   2939  0 00:42 ?        00:00:00 postgres: autovacuum launcher process               
postgres   2945   2939  0 00:42 ?        00:00:00 postgres: stats collector process                   
root       3109   3064  0 00:58 pts/2    00:00:00 su - postgres
postgres   3110   3109  0 00:58 pts/2    00:00:00 -bash
postgres   3151   3110  0 00:59 pts/2    00:00:00 /bin/bash /usr/local/pgsql/bin/psql -p10280
postgres   3153   3151  0 00:59 pts/2    00:00:00 /usr/local/pgsql/bin/psql.bin -p10280
root       3189   3087  0 01:07 pts/3    00:00:00 su - postgres
postgres   3190   3189  0 01:07 pts/3    00:00:00 -bash
postgres   3272   2939  0 01:25 ?        00:00:00 postgres: postgres testdb01 [local] idle            
postgres   3415   2939  0 02:16 ?        00:00:00 postgres: wal sender process replica 10.0.0.110(34021) streaming 0/7000140
postgres   3422   3190  0 02:17 pts/3    00:00:00 ps -ef
postgres   3423   3190  0 02:17 pts/3    00:00:00 grep postgres

此时从库上可以看到流复制的进程,同样的主库也能看到该进程。表明主从流复制配置成功。


同步测试演示:
创建库和建表做测试,在master服务器(10.0.0.100)中的创建testdb02库并且建一张表并添加几条数据:


master上操作:
postgres=# create database testdb02;
CREATE DATABASE
检查:
[postgres@localhost pg_xlog]$ psql -p10280 -c '\list'|grep testdb02
 testdb02  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |

testdb01=# \c testdb02
You are now connected to database "testdb02" as user "postgres".
testdb02=# \d
No relations found.
创建表:
CREATE TABLE weather ( city varchar(80), temp_lo int, temp_hi int, prcp real,date date);
testdb02=# \d
          List of relations
 Schema |  Name   | Type  |  Owner   
--------+---------+-------+----------
 public | weather | table | postgres
(1 row)

testdb02=# \d weather
           Table "public.weather"
 Column  |         Type          | Modifiers
---------+-----------------------+-----------
 city    | character varying(80) |
 temp_lo | integer               |
 temp_hi | integer               |
 prcp    | real                  |
 date    | date                  |

testdb02=#
testdb02=# INSERT INTO weather (city, temp_lo, temp_hi, prcp, date) VALUES ('China05', '47', '59', '1.0', '1994-12-15');
INSERT 0 1
testdb02=# INSERT INTO weather (city, temp_lo, temp_hi, prcp, date) VALUES ('China04', '46', '58', '2.0', '1994-12-14');\
INSERT 0 1
testdb02=# select * from weather;
  city   | temp_lo | temp_hi | prcp |    date    
---------+---------+---------+------+------------
 China05 |      47 |      59 |    1 | 1994-12-15
 China04 |      46 |      58 |    2 | 1994-12-14
(2 rows)
testdb02=#

从库上检查:
[postgres@localhost data]$  psql -p10280 -c '\list'|grep testdb02
 testdb02  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |

postgres=# \c testdb02;
You are now connected to database "testdb02" as user "postgres".
testdb02=# \d
          List of relations
 Schema |  Name   | Type  |  Owner   
--------+---------+-------+----------
 public | weather | table | postgres
(1 row)

testdb02=# \d weather;
           Table "public.weather"
 Column  |         Type          | Modifiers
---------+-----------------------+-----------
 city    | character varying(80) |
 temp_lo | integer               |
 temp_hi | integer               |
 prcp    | real                  |
 date    | date                  |

testdb02=# select * from weather;
  city   | temp_lo | temp_hi | prcp |    date    
---------+---------+---------+------+------------
 China05 |      47 |      59 |    1 | 1994-12-15
 China04 |      46 |      58 |    2 | 1994-12-14
(2 rows)

testdb02=#
可以看到完美同步,那么从库是否能删除呢?测试一下:
从库上测试删除数据库testdb02;
postgres=# drop database testdb02;
ERROR:  cannot execute DROP DATABASE in a read-only transaction
postgres=# drop database testdb01;
ERROR:  cannot execute DROP DATABASE in a read-only transaction

standby的数据无法删除,正如我们之前说的,standby只提供只读服务,而只有master才能进行读写操作,所以master才有权限删除数据。master删除的同时standby中的数据也将同步删除,

查看复制状态

主库中执行

postgresql主从实现之异步流复制_第1张图片

关于异步流复制的内容到这里.

参考博文:

http://blog.csdn.net/wzyzzu/article/details/53331206