链接: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 分类目录。将固定连接加入收藏夹。