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中的数据也将同步删除,
查看复制状态
主库中执行
关于异步流复制的内容到这里.
参考博文:
http://blog.csdn.net/wzyzzu/article/details/53331206