链接:http://blog.itpub.net/28602568/viewspace-2107799/
标题:PostgreSQL 主备流复制机制介绍/搭建步骤
作者:lōττéry©版权所有[文章允许转载,但必须以链接方式注明源地址,否则追究法律责任.]
PostgreSQL主备流复制机制介绍
PostgreSQL在9.0之后引入了主备流复制机制,通过流复制,备库不断的从主库同步相应的数据,并在备库apply每个WAL record,这里的流复制每次传输单位是WAL日志的record。
而PostgreSQL9.0之前提供的方法是主库写完一个WAL日志文件后,才把WAL日志文件传送到备库,这样的方式导致主备延迟特别大。
同时PostgreSQL9.0之后提供了Hot Standby,备库在应用WAL record的同时也能够提供只读服务,大大提升了用户体验。
PG主备流复制的核心部分由walsender,walreceiver和startup三个进程组成。
walsender进程是用来发送WAL日志记录的
walreceiver进程是用来接收WAL日志记录的
startup进程是用来apply日志的
下图是PG主备总体框架图:
walsender和walreceiver进程流复制过程
walsender和walreceiver交互主要分为以下几个步骤:
- walreceiver启动后通过recovery.conf文件中的primary_conninfo参数信息连向主库,主库通过连接参数replication=true启动walsender进程;
- walreceiver执行identify_system命令,获取主库systemid/timeline/xlogpos等信息,执行TIMELINE_HISTORY命令拉取history文件;
- 执行wal_startstreaming开始启动流复制,通过walrcv_receive获取WAL日志,期间也会回应主库发过来的心跳信息(接收位点、flush位点、apply位点),向主库发送feedback信息(最老的事务id),避免vacuum删掉备库正在使用的记录;
- 执行walrcv_endstreaming结束流复制,等待startup进程更新receiveStart和receiveStartTLI,一旦更新,进入步骤2。
图2. PG流复制过程
walreceiver和startup进程
startup进程进入standby模式和apply日志主要过程:
- 读取pg_control文件,找到redo位点;读取recovery.conf,如果配置standby_mode=on则进入standby模式。
- 如果是Hot Standby需要初始化clog、subtrans、事务环境等。初始化redo资源管理器,比如Heap、Heap2、Database、XLOG等。
- 读取WAL record,如果record不存在需要调用XLogPageRead->WaitForWALToBecomeAvailable->RequestXLogStreaming唤醒walreceiver从walsender获取WAL record。
-
对读取的WAL record进行redo,通过record->xl_rmid信息,调用相应的redo资源管理器进行redo操作。比如heap_redo的XLOG_HEAP_INSERT操作,就是通过record的信息在buffer page中增加一个record:
MemSet((char *) htup, 0, sizeof(HeapTupleHeaderData)); /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */ memcpy((char *) htup + offsetof(HeapTupleHeaderData, t_bits), (char *) xlrec + SizeOfHeapInsert + SizeOfHeapHeader, newlen); newlen += offsetof(HeapTupleHeaderData, t_bits); htup->t_infomask2 = xlhdr.t_infomask2; htup->t_infomask = xlhdr.t_infomask; htup->t_hoff = xlhdr.t_hoff; HeapTupleHeaderSetXmin(htup, record->xl_xid); HeapTupleHeaderSetCmin(htup, FirstCommandId); htup->t_ctid = xlrec->target.tid; offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true); if (offnum == InvalidOffsetNumber) elog(PANIC, "heap_insert_redo: failed to add tuple"); freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */ PageSetLSN(page, lsn); if (xlrec->flags&XLOG_HEAP_ALL_VISIBLE_CLEARED) PageClearAllVisible(page); MarkBufferDirty(buffer);
还有部分redo操作(vacuum产生的record)需要检查在Hot Standby模式下的查询冲突,比如某些tuples需要remove,而存在正在执行的query可能读到这些tuples,这样就会破坏事务隔离级别。通过函数ResolveRecoveryConflictWithSnapshot检测冲突,如果发生冲突,那么就把这个query所在的进程kill掉。
- 检查一致性,如果一致了,Hot Standby模式可以接受用户只读查询;更新共享内存中XLogCtlData的apply位点和时间线;如果恢复到时间点,时间线或者事务id需要检查是否恢复到当前目标;
- 回到步骤3,读取next WAL record。
图3. PG standby模式和apply日志过程
如上postgresql主备流复制机制介绍 信息来源于网站 http://mysql.taobao.org/monthly/2015/10/04/
搭建步骤
环境:
Wonhigh-Test15 10.240.1.101 -->主
Wonhigh-Test16 10.240.1.102 -->备
前提:主备机器都安装DB..【搭建步骤可参考 http://blog.itpub.net/28602568/viewspace-1841163/ 】
primary 主
参数文件配置
[postgres@Wonhigh-Test15 data]$ grep -v "^#" postgresql.conf 默认配置:
max_connections = 100 shared_buffers = 128MB dynamic_shared_memory_type = posix log_timezone = 'PRC' datestyle = 'iso, ymd' timezone = 'PRC' lc_messages = 'zh_CN.UTF-8' # locale for system error message lc_monetary = 'zh_CN.UTF-8' # locale for monetary formatting lc_numeric = 'zh_CN.UTF-8' # locale for number formatting lc_time = 'zh_CN.UTF-8' # locale for time formatting default_text_search_config = 'pg_catalog.simple' #主备配置更改部分
wal_level = hot_standby # 这个是设置主为wal的主机
max_wal_senders = 32 # 这个设置了可以最多有几个流复制连接,差不多有几个备,就设置几个
wal_keep_segments = 256 #设置流复制保留的最多的xlog数目
wal_sender_timeout = 60s #设置流复制主机发送数据的超时时间
max_connections = 100 # 这个设置要注意下,备库的max_connections必须要大于主库的
listen_addresses = '*' [postgres@Wonhigh-Test15 data]$ 访问权限配置 [postgres@Wonhigh-Test15 data]$ grep -v "^#" pg_hba.conf |grep replication
host replication replica 10.240.1.102/32 md5
[postgres@Wonhigh-Test15 data]$ DB同步账号配置
[postgres@Wonhigh-Test15 data]$ psql -c"CREATE ROLE replica login replication encrypted password 'replica';"
standby 备
复制主库到备库...前提/data/pgsql/data/需要为空
[postgres@Wonhigh-Test16 ~]$ pg_basebackup -D /data/pgsql/data/ -Fp -Xs -v -P -h 10.240.1.101 -U replica -p5432 --password
Password:
transaction log start point: 0/2000028 on timeline 1
pg_basebackup: starting background WAL receiver
41369/41369 kB (100%), 1/1 tablespace
transaction log end point: 0/20000F0
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed
[postgres@Wonhigh-Test16 ~]$
配置参数文件postgresql.conf
hot_standby = on # 说明这台机器不仅仅是用于数据归档,也用于数据查询 <若是off的话 启动DB链接时会报错psql: FATAL: the database system is starting up>
max_standby_streaming_delay = 30s # 数据流备份的最大延迟时间
wal_receiver_status_interval = 1s # 多久向主报告一次备的状态,当然备每次数据复制都会向主报告状态,这里只是设置最长的间隔时间
hot_standby_feedback = on # 如果有错误的数据复制,是否向主进行反馈
配置recover.conf文件
$ cp /usr/local/pgsql/share/recovery.conf.sample /data/pgsql/data/recovery.conf
$ grep -v '^#' /data/pgsql/data/recovery.conf
standby_mode = 'on' # 这个说明这台机器为备库 trigger_file = '/data/pgsql/data/postgresql.trigger.1949' primary_conninfo = 'host=10.240.1.101 port=5432 user=replica password=replica keepalives_idle=60' # 这个说明这台机器对应主库的信息 recovery_target_timeline = 'latest' # 这个说明这个流复制同步到最新的数据
$
启动备库
$ pg_ctl restart -D /data/pgsql/data/ -l /data/pgsql/log.log
查看复制状态
postgres=# /x -- Expanded display is on. postgres=# select * from pg_stat_replication; -[ RECORD 1 ]----+------------------------------
pid | 8964 # sender的进程
usesysid | 16384 # 复制的用户id
usename | replica # 复制的用户用户名
application_name | walreceiver
client_addr | 10.240.1.102 # 复制的客户端地址
client_hostname |
client_port | 50511 # 复制的客户端端口
backend_start | 2016-05-25 17:29:53.874416+08 # 这个主备搭建的时间
backend_xmin |
state | streaming # 同步状态 startup: 连接中、catchup: 同步中、streaming: 同步
sent_location | 0/3012290 # Master传送WAL的位置
write_location | 0/3012290 # Slave接收WAL的位置
flush_location | 0/3012290 # Slave同步到磁盘的WAL位置
replay_location | 0/3012290 # Slave同步到数据库的WAL位置
sync_priority | 0 #同步Replication的优先度 0: 异步、1~?: 同步(数字越小优先度越高)
sync_state | async # 有三个值,async: 异步、sync: 同步、potential: 虽然现在是异步模式,但是有可能升级到同步模式 postgres=#
查看主从进程
-->查看wal sender进程 -->walsender进程是用来发送WAL日志记录的
《主》$ ps -ef |grep sender|grep -v grep
postgres 8964 4365 0 17:29 ? 00:00:00 postgres: wal sender process replica 10.240.1.102(50511) streaming 0/3012368
-->查看receiver/start进程 --> walreceiver进程是用来接收WAL日志记录的,startup进程是用来apply日志的
《备》$ ps -ef |grep receiver|grep -v grep
postgres 21103 21098 0 17:29 ? 00:00:00 postgres: wal receiver process streaming 0/3012368 $ ps -ef |grep startup|grep -v grep
postgres 21099 21098 0 17:29 ? 00:00:00 postgres: startup process recovering 000000010000000000000003
验证是否开始同步
主库执行DDL/DML ,在备库验证是否同步
主:
test=# create table t_1 (name varchar(10));
CREATE TABLE
test=# insert into t_1 values('1');
INSERT 0 1
test=#
备:
postgres=# /c test
You are now connected to database "test" as user "postgres".
test=# select * from t_1;
name
------
1
(1 row)
test=#
【源于本人笔记】 若有书写错误,表达错误,请指正...
此条目发表在 PostgreSQL 分类目录。将固定连接加入收藏夹。