暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

【技术文章】浅淡PostgreSQL流复制

云贝教育 2022-03-24
3165




点击上方蓝字关注我们








REC



点击观看视频





01

原理



1、概述

同步流复制是是从9.1版本实现的,可以是一主多从的模式,在postgresql里主节点叫master,备节点叫standby。
主备是基于日志传送的技术实现同步,主节点持续发送wal数据,备节点重放接受到的wal数据。


主要介绍以下几个方面:

  • 流复制如何启动的

  • 主备之间如何传送数据

  • 主节点如何管理多个备节点

  • 主节点如何发现失败的备节点


2、开始流复制

在流复制中,三个进程协同工作,walsender在主节点发送wal数据到备节点,然后,备节点启动一个walreceiver和一个startup进程接受和重放数据,walsender和walreceiver通过一个TCP通讯协议建立连接。这一章节,我们将探索流复制的start-up顺序,明白这些进程是如何启动,以及如何建立连接。
下图显示了流复制的启动及连接步骤:
  • 1.启动主节点和备节点

  • 2.备节点启动startup进程

  • 3.备节点启动walreceiver进程

  • 4.walreceiver发送一个连接请求到主节点,如果主节点没有运行,walreceiver会周期性的发送请求

  • 5.当主节点接受到一个连接的请求,就会启动一个walsender进程建立连接

  • 6.walreceiver发送备节点最后的LSN,这个阶段在IT领域叫握手机制

  • 7.如果备机点的LSN小于主节点的LSN,walsender发送wal数据,即从节点的LSN到主节点LSN的wal数据,wal数据由存储在主节点的pg_xlog(10版本以后叫pg_wal)目录下的wal segment提供,这个阶段就是备节点追赶主节点的阶段

  • 8.流复制开始工作

每一个walsender进程在和walreceiver建立连接工作都会有一个合适的状态,以下是可能的状态:
  • start-up:见上图5~6

  • catch-up:上图7

  • streaming:上图8

  • backup:因为备份发送整个数据库集群的文件,比如pg_basebackup工具


通过 pg_stat_replication视图可以查看:

    testdb=# SELECT application_name,state FROM pg_stat_replication;
    application_name | state
    ------------------+-----------
    standby1 | streaming
    standby2 | streaming
    pg_basebackup | backup
    (3 rows)


    在9.3以前,如果备节点请求的wal segments在主节点已经被覆盖,那么备节点无法追上主节点,对于这个问题,没有什么可靠的方法,但是把wal_keep_segments参数增大,可以减少这种事件的发生的可能性,这只是一个临时措施。在9.4以后,这个问题可是使用replication slot来预防。replication slot是一项扩展wal数据发送灵活性的特性,主要用于逻辑复制,并且提供这种问题的解决方法,即暂时回收进程,将包含pg_xlog下未发送的wal segments保存在复制槽中。

    3、流复制如何处理

    流复制有两个方面:日志传送数据同步,日志传送显然其中一个方面,因为流复制是基于日志传送的,不管何时发生写入,主节点都会发送wal到已连接的备节点,同步复制需要数据库同步,主节点和多个备节点同步数据。为了准确理解流复制的工作原理,我们应该研究一个主节点如何管理多个备节点。为了更容易理解,本节描述了一个主节点和一个备节点的情况,下一节介绍一个主节点和多个备节点的情况


    4、主节点和同步备节点如何通讯的

    假设在备节点配置了以下参数

      synchronous_standby_names = 'standby1'
      hot_standby = off
      wal_level = archive
      在自动提交模式下,假设后台进程在主节点发出了一条insert操作,后台进程开始一个事务,并且发出一条insert语句,并且立即提交事务,让我们进一步探讨如何完成此次提交:


      • 1.后台进程通过执行XLogInsert()和 XLogFlush()函数将wal数据写入并刷新到wal segment。

      • 2.walsender进行将写入wal segment的wal数据发送到walreceiver进程。

      • 3.主节点发送wal数据后,后台进程等待从备节点的ACK响应,更确切的说,后台进程执行内部函数SyncRepWaitForLSN()来获取latch锁,并等待锁释放。

      • 4.备节点的walreceiver进程使用write()函数将wal数据写入到wal segment,并且返回ACK响应给walsender。

      • 5.备节点的walreceiver进程使用fsync()函数将wal数据全部刷新到wal segment,并且返回又一个ACK响应给walsengder,通知startup进程wal已经更新。

      • 6.starup进程回放已经写入wal segment的wal数据。

      • 7.walsender进程在接受到ACK响应后释放latch锁,然后,后台进程提交或者终止的操作就完成了。latch释放的时间依赖于参数synchronous_commit,如果参数是on,则在以上第五步接受ACK响应后释放latch,如果设置是remote_write,那么在第四步接受ACK响应后就释放latch。


      备节点发送主节点每一个ACK包含以下内容:

      • 已经写入的最新WAL数据的LSN的位置

      • 已经刷新最新的WAL数据的LSN的位置

      • startup进程最新回放wal数据的LSN位置

      • 发送ACK的时间戳


      walreceiver不仅在写入和刷新的WAL数据的时候发送ACK响应,而且还定期作为备节点的心跳发送ACK响应。因此,主节点始终掌握所有备节点的状态。


      通过以下查询可以看到相关LSN的信息:

        testdb=# SELECT application_name AS host,
        write_location AS write_LSN, flush_location AS flush_LSN,
        replay_location AS replay_LSN FROM pg_stat_replication;
        host | write_lsn | flush_lsn | replay_lsn
        ----------+-----------+-----------+------------
        standby1 | 0/5000280 | 0/5000280 | 0/5000280
        standby2 | 0/5000280 | 0/5000280 | 0/5000280
        (2 rows)

        心跳发送间隔通过wal_receiver_status_interval设置,默认10秒。


        5、当失败发生时的行为反应

        描述同步流复制的备节点故障时,主节点是如何的表现,以及如何处理这种情况。
        即使同步流复制的备节点发生故障,并且不能够给主节点返回ACK响应,主节点也会继续等待ACK响应。因此,在主节点运行的事物无法提交,后续的查询也无法执行。换句话说,主节点所有的操作都是停止的。(流复制不支持由于超时而自动转到异步流复制的函数)
        有两种方法避免这种情况的发生,其中之一就是提供多台备节点来提高系统的可用性,另一个就是通过手动执行以下步骤从同步流复制转换到异步流复制:
        • 1.设置以下参数为空串

          synchronous_standby_names = ''
          • 2.执行reload命令重载配置文件

            pg_ctl -D $PGDATA reload
            以上操作对已经连接的客户端没有影响,主节点会继续处理所有连接的session的事物。


            6、管理多个备节点

            主节点提供所有备节点的sync_priority和sync_state,并依靠这些值来处理备节点。主节点即使管理一个节点也会也会提供这些值,在上一节中没有提到这一点。sync_priority表示在同步模式下备节点的优先级,是一个固定值。越小的值优先级越高,0表示异步模式,备节点的优先级按synchronous_standby_names顺序列出,例如,以下配置中standy1和standy2的优先级分别是1和2。
              synchronous_standby_names = 'standby1, standby2'
              此参数未列出的的备节点是异步模式,优先级的值为0。
              sync_state表示备节点的状态,这个值根据所有备节点的运行状态以及优先级发生变化。
              Sync是所有同步模式下备节点最高优先级。
              Potential是所有备节点中第二高的优先级级别,如果最高优先级的备节点故障,那么这个节点会代替故障节点变为最高优先级的备节点。
              async异步模式的固定值,主节点以potential一样的看待这个异步备节点,但是sync_state不会是sync和potential状态。

              可以通过以下视图查看这两个值:

                testdb=# SELECT application_name AS host,
                sync_priority, sync_state FROM pg_stat_replication;
                host | sync_priority | sync_state
                ----------+---------------+------------
                standby1 | 1 | sync
                standby2 | 2 | potential
                (2 rows)


                7、主如何管理多个备节点

                主节点会等待单个同步模式下的备节点ACK响应,换句话说,主节点只确认同步流复制的写和刷新的wal数据。因此,流复制仅仅确定同步流复制备节点与主节点处于一致和同步状态。
                下图给出了Potential备服务器的ACK响应比主备机早返回的情况。在那里,主服务器没有完成当前事务的提交操作,并继续等待主服务器的ACK响应。然后,当接收到主进程的响应时,后端进程释放锁存器并完成当前事务处理。


                standby1和standby2的sync_state分别为’sync’和’potential’。尽管从潜在的备用服务器接收到ACK响应,主进程的后端进程继续等待同步备用服务器的ACK响应。(2)主进程的后端进程释放锁存器,完成当前事务处理。
                在相反的情况下(即主服务器的ACK响应比潜在服务器早返回),主服务器立即完成当前事务的提交操作,而不确保潜在备用服务器是否写和刷新WAL数据。


                8、故障发生时的行为

                同样,我们来看下主服务器在备用服务器失败时的行为。
                当potential或异步备用服务器发生故障时,主服务器终止连接到故障备用服务器的walsender进程,并继续所有处理。换句话说,主服务器的事务处理不会受到任何一种备用服务器故障的影响。
                当同步备用服务器发生故障时,主服务器终止连接到故障备用服务器的walsender进程,并用最高优先级的潜在备用服务器替换同步备用服务器,见图。与上面描述的故障相反,从故障点到替换同步备用时,主服务器上的查询处理将暂停。(因此,备用服务器的故障检测是提高复制系统可用性的一个非常重要的功能。故障检测将在下一节中描述。)

                如果一个或多个备用服务器以同步模式运行,则主服务器始终只有一个同步备用服务器,同步备用服务器始终与主服务器处于一致同步状态。


                9、检测备用服务器的故障

                流复制使用两种常见的故障检测程序,而且不需要任何特殊的硬件。

                • 1.备用服务器进程故障检测:当检测到walsender和walreceiver之间的连接断开时,主服务器立即确定备用服务器或walreceiver进程故障。当一个low level网络函数在写或读walreceiver的套接字时返回一个错误,主函数也会立即判断出备用服务器进程故障了。

                • 2.硬件和网络的故障检测:如果walreceiver在wal_sender_timeout参数设置的时间内(默认为60秒)没有返回任何值,则主服务器判定备服务器故障。与上述故障对比,即使备节点由于故障长时间没有任何响应,也需要wal_sender_timeout这么久的时间确认备节点故障。


                根据故障类型,通常可以在故障和检测之间会有时间差,特别是,在同步流复制中发生了一种类型的故障,主节点的所有事务处理将停止,直到探测到备节点故障,即使多个potential备节点在运行。



                02

                物理复制


                1、什么是流复制

                基于流复制协议的wal日志从主节点到备节点实时复制传输与复用;
                为了实现数据库的高可用,我们需要搭建主库和备库。流复制是搭建主备库的一种有效方式;
                两套数据库之间的数据,通过wal日志,后台自动同步;
                对外部的应用程序而言,可以看作是两套数据库,需要根据业务需要,显式分别连接不同的数据库;或者配合其它中间件使用,譬如PGPOOL实现负载均衡和故障自动切换。


                2、流复制原理

                那流复制是怎么实现的呢?
                主要涉及到几个backend辅助进程:walwriter,walsender,&&&  walreceiver,startup
                当用户连接进行数据操作,产生对应的WAL日志记录后,walwriter会周期性地把产生的WAL page刷新到磁盘中,如果配置了备库,则walsender会不断将WAL page发给备库的walreceiver进程,walreceiver进程会把对应WAL page直接写到本地磁盘,同时slave上的startup辅助进程会不断地应用xlog日志,改变本地数据,实现与主库之间的数据同步。
                而且通过配置,备库是可以接受用户的只读请求。


                3、流复制不同版本的特性

                • 9.0开始支持1+n的异步流复制.

                • 9.1支持1+1+n的同步和异步流复制

                • 9.2开始支持级联流复制

                • 9.3开始支持跨平台的流复制协议

                • 9.3开始流复制协议增加了时间线文件传输的协议, 支持自动切换时间线.

                • 9.4可以使用流复制做增量数据同步,所以停机服务时间会非常短。

                 

                PostgreSQL在9.0之后引入了主备流复制机制,通过流复制,备库不断的从主库同步相应的数据,并在备库apply每个WAL record,每次传输单位是WAL日志的record。
                而PostgreSQL9.0之前提供的方法是主库写完一个WAL日志文件后,才把WAL日志文件传送到备库,这样的方式导致主备延迟特别大。
                同时PostgreSQL9.0之后提供了Hot Standby,备库在应用WAL record的同时也能够提供只读服务,大大提升了用户体验。

                4、流复制的优势

                物理层面完全一致,这是许多商业数据库的惯用手段。例如Oracle的DG。
                延迟低,事务执行过程中产生REDO record,实时的在备库apply,事务结束时,备库立马能见到数据。不论事务多大,都一样。
                物理复制的一致性、可靠性达到了金融级的需求,不必担心数据逻辑层面不一致。

                5、异步流复制

                流复制传递日志两种方式:

                • 异步流复制 

                • 同步流复制


                两者的主要区别:

                在异步流复制的情况下,事务被提交到master之后数据才可以被复制。
                slave就写操作而言,通常滞后于master一些,此延迟(delay)被称为滞后性(lag)。


                同步复制较高数据一致性规则:

                如果您决定使用同步复制,系统必须确保通过事务写入的数据至少事务同时在两台服务器上提交。
                这意味着:slave 不滞后于master,而且终端用户在两台服务器上看到的数据是一致的。


                A.考虑数据丢失

                假设我们正在以异步复制方式同步数据:
                •   1.事物发送到master。

                •   2.事物提交到master。

                •   3.在事物发送到slave之前,master宕机。

                •   4.slave永远都不会收到这个事务。

                在异步复制的情况下,有一个窗口(滞后),在滞后窗口期间数据会丢失。
                滞后窗口的大小因设置类型的不同而不同。它的大小非常短(几毫秒)或非常长(几分钟,几小时,几天)。
                一个重要的事实是:数据可能丢失。一个小的滞后只会是数据丢失的可能性较小,但任何大于零的滞后都容易导致数据丢失。
                如果想确保数据永远不丢失,必须切换到同步复制。
                一个同步事务是同步的,因为如果事物提交到了两台服务器它才是有效的。

                B.考虑性能问题

                通过网络发送不必要的消息的开销是昂贵的和费时的。
                如果一个事务采用同步的方式复制,PostgreSQL必须确保数据到达第二个节点,这样就会导致延迟问题。
                在许多方面,同步复制比异步复制要昂贵很多,因此如果这种消耗确实需要和调整,应该三思而后行。(只在需要的时候使用同步复制)


                6、异步流复制的具体实现

                  关闭防火墙
                  systemctl status firewalld.service
                  systemctl stop firewalld.service
                  systemctl disable firewalld.service
                    SELINUX
                    vi etc/selinux/config
                    将SELINUX=enforcing 改为SELINUX=disabled
                      流复制配置
                      #创建replica用户
                      create role replica login replication encrypted password 'REPLICA321';
                        创建物理复制槽
                        select * from pg_create_physical_replication_slot('node1');
                        select * from pg_create_physical_replication_slot('node2');
                        select * from pg_replication_slots;


                        7、异步流复制的具体实现

                        主库db1配置:

                          vi $PGDATA/postgresql.conf
                          ================
                          listen_addresses = '0.0.0.0' # what IP address(es) to listen on;
                          port = 5432 # (change requires restart)
                          max_connections = 1000 # (change requires restart)
                          superuser_reserved_connections = 13 # (change requires restart)
                          unix_socket_directories = '.' # comma-separated list of directories
                          unix_socket_permissions = 0700 # begin with 0 to use octal notation
                          tcp_keepalives_idle = 60 # TCP_KEEPIDLE, in seconds;
                          tcp_keepalives_interval = 10 # TCP_KEEPINTVL, in seconds;
                          tcp_keepalives_count = 10 # TCP_KEEPCNT;
                          shared_buffers = 2048MB # min 128kB
                          vacuum_cost_delay = 10 # 0-100 milliseconds
                          bgwriter_delay = 10ms # 10-10000ms between rounds
                          wal_level = replica #minimal replica 逻辑
                          wal_writer_delay = 10ms # 1-10000 milliseconds
                          max_wal_senders = 10 # max number of walsender processes
                          hot_standby = on # "on" allows queries during recovery
                          wal_receiver_status_interval = 1s # :send replies at least this often
                          hot_standby_feedback = on # send info from standby to prevent
                          log_destination = 'csvlog' # Valid values are combinations of
                          logging_collector = on # Enable capturing of stderr and csvlog
                          log_directory = 'pg_log' # directory where log files are written,
                          log_truncate_on_rotation = on # If on, an existing log file with the
                          log_rotation_age = 1d # Automatic rotation of logfiles will
                          log_rotation_size = 10MB # Automatic rotation of logfiles will
                          log_checkpoints = on
                          log_connections = on
                          log_disconnections = on
                          log_error_verbosity = verbose # terse, default, or verbose messages
                          log_timezone = 'PRC'
                          datestyle = 'iso, mdy'
                          timezone = 'PRC'
                          lc_messages = 'C' # locale for system error message
                          lc_monetary = 'C' # locale for monetary formatting
                          lc_numeric = 'C' # locale for number formatting
                          lc_time = 'C' # locale for time formatting
                          full_page_writes = on
                          synchronous_commit = on # synchronization level;
                          wal_log_hints = on
                          synchronous_standby_names = ''
                          max_replication_slots = 20
                          archive_mode = on
                          archive_command = 'cp %p opt/arch/%f && echo %f >> opt/arch/archive.list'




                          1、注解:

                            wal_level            表示启动搭建hot Standby v9.6- replica
                            max_wal_senders 设置为一个大于0的数,表示主库最多可以有多少个并发的standby
                            wal_keep_segments 设置为一个尽量大的值,以防止主库生成WAL日志太快,日志还没有来得及传送到
                            standby就被覆盖,但是需要考虑磁盘空间允许,一个WAL日志文件的大小是16M。

                            主库创建一个超级用户来专门负责让standby连接去拖WAL日志

                            ------

                            pg_hba.conf

                            host all all 0.0.0.0/0 md5
                            host replication replica 0.0.0.0/0 md5





                            2、备库db2配置:

                            从此处开始配置备库,首先通过pg_basebackup命令行工具在从库上生成基础备份

                            命令如下:

                              rm -rf pg_root
                              pg_basebackup -F p -D $PGDATA -h 本节点上级节点IP -p 本节点上级节点端口 -U replica
                              REPLICA321


                              recovery.conf pg11版本之前

                                restore_command='cp opt/arch/%f %p'
                                recovery_target_timeline='latest'
                                standby_mode=on
                                primary_conninfo='host=本节点上级节点 port=5432 user=replica password=REPLICA321 keepalives_idle=60'
                                primary_slot_name = 'node1' #node2 node3

                                注意 recovery.conf 的 primary_slot_name 在不同节点值会不同。





                                3、备库recovery.conf 文件配置

                                recovery.conf 是一个配置文件,用于主库,备库切换时的参数配置

                                可以从 $PGHOME/share 目录下复制一份 recovery.conf.sample 到备库 $PGDATA 目录,

                                也可以通过pg_basebackup制定-R参数生成。

                                 

                                关键参数注释:   

                                standby_mode = ''                                       --标记PG为STANDBY SERVER

                                primary_conninfo = ''                                  --标识主库信息

                                trigger_file = ''/temp/aaa.trigger”               --标识触发器文件 





                                4、主库创建表并插入数据验证异步流复制

                                     主库下建一张表并添加几条数据: 

                                  create table repl_t(id int);
                                  insert into repl_t select generate_series(1,10);


                                  查看从库同步效果:

                                    select * from  repl_t;


                                    验证从库是否能pg_controldata 执行删除、更新操作:

                                      delete  from repl_t ;
                                      update repl_t set id=2 where id=1;





                                      5、pg_ctl promote  主备切换

                                      执行完pg_rewind之后,一定要修改下recovery.conf文件中的内容。

                                        select pg_is_in_recovery();
                                        select * from pg_stat_replication;
                                        SELECT * FROM pg_replication_slots;
                                        select pg_reload_conf();




                                        8、同步流复制

                                        9.1版本中实现了同步流复制,同步复制要求在数据写入Standby数据库后,事务commit才返回;如果Standby库出现问题时,会导致主库被hang住。

                                        解决方法:
                                        启动两个Standby数据库,这两个standby数据库只要有一个是正常的,就不会让主库hang住;
                                        实现同步复制主要是在主库上设置postgresql.conf配置参数synchronous_standby_names;
                                        这个参数指定多个Standby的名称,各个名称通过逗号分隔,而Standby名称是在Standby连接到主库时,由连接参数"application_name"指定的。

                                        • 主库修改postgresql.conf

                                          vi postgresql.conf
                                          synchronous_standby_names = 'standby01,standby02‘


                                          • 异步流复制recovery.conf

                                            recovery_target_timeline='latest'
                                            standby_mode = 'on'
                                            primary_conninfo = 'user=repl password=111111 host=192.168.137.220 port=1921 sslmode=disable sslcompression=1'
                                            在Standby数据库的recovery.conf里的primary_conninfo一定要指定连接参数"application_name"。

                                            • 从库recovery.conf的配置:

                                              standby_mode = 'on'
                                              primary_conninfo = 'application_name=standby02 user=repl password=111111 host=192.168.137.220 port=1921'


                                              9、会话级别异步、同步复制的切换

                                              当主库已配置参数synchronous_standby_names = standby01 或 * 时
                                              会话执行:set synchronous_commit = off; 
                                              当前会话可以取消同步复制,改为异步复制。
                                               
                                              当主库注释配置参数synchronous_standby_names = 时
                                              会话执行:set synchronous_commit = on ;  不起作用


                                              10、流复制管理命令

                                              主备切换-promote
                                              流复制搭建完成后,备库是只读的,可以利用它进行读写分离均衡。当主节点数据库服务访问异常的时候,可以手动切换主从节点pg_ctl promote -D $PGDATA


                                              手动切换主备角色的步骤:

                                              • 1)关闭主库,建议使用 -m fast模式关闭

                                              • 2)备库执行pg_ctl promote -D $PGDATA,激活备库升级为主库

                                              • 3)创建或修改原主库的recovery.conf文件primary_conninfo参数指向新主库

                                              • 4)启动原主库,检查验证状态是否正常


                                              1.8.2       修复备库-pg_rewind

                                              PG 9.5 版本将 pg_rewind 加入到源码,当主备发生切换时,可以将原来主库通过同步模式恢复,避免重做备库。这样对于较大的库来说,节省了大量重做备库时间。
                                              pg_rewind 会将目标库的数据文件,配置文件复制到本地目录,由于 pg_rewind 不会读取所有未发生变化的数据块,所以速度比重做备库要快很多。
                                              pg_rewind检查源集群与目标集群的时间线历史来检测它们产生分歧的点,并希望在目标集群的pg_wal目录找到WAL回到分歧点的所有方式。


                                              使用pg_rewind 的前提条件为以下之一

                                              • postgresql.conf配置文件wal_log_hints参数设置为on;

                                              • PG 在initdb初始化数据库时开启 checksums 功能 ,示例:initdb --data-checksums

                                              • pg_rewind --target-pgdata opt/pg_root --source-server='host=192.168.200.142 port=5432 user=postgres dbname=postgres password=123456' -P


                                              full_page_writes
                                              data-checksums
                                              需要开启的,因为hint不写日志的话,这些BLOCK实际内容已经变化了,但是确是无法被修复的。

                                              11、恢复备库-pg_rewind

                                              pg_rewind是如何工作的?

                                              基本的思想是从新的集群拷贝所有的东西到老的集群,除了相同的(数据)块。
                                              • 1.从最后一个检查点开始扫描老集群的WAL日志,在该检查点之前,新集群的时间线历史从老集群被创建出来。对于每一个WAL记录,做一个数据块被触及的记录。在新的集群被创建出来以后,这产生所有在老集群中被更改的数据块的列表。

                                              • 2.从新集群复制所有这些被更改的数据块到老集群。

                                              • 3.从新集群复制所有其它像clog,conf这样的文件等等到老集群。每个文件,除了表文件。

                                              • 4.从新集群应用WAL,从故障转移创建的检查点开始。(严格的说,pg_rewind不应用WAL,它只是创建一个备份标签文件以表明PostgreSQL被启动了,它会从检查点重放并应用所有需要的WAL)



                                              03

                                              逻辑复制


                                              1、什么是逻辑复制(pglogical)

                                              逻辑复制是PostgreSQL V10重量级新特性,支持内置的逻辑复制。
                                              在10版本之前,虽然没有内置的逻辑复制,也可以通过其它方式实现,触发器、自定义脚本实现表级别同步,另外也可以通过外部工具 Londiste3 实现。
                                              从2014年发布的9.4版本开始,PostgreSQL就支持逻辑复制了,只是一直没有将其引入内核。
                                              可以针对同一个数据库实例,同时使用逻辑复制和物理复制,因为他们都是基于WAL的。备注:Londiste 是一个较新的基于队列的复制软件,它基于触发器来驱动,它是SkyTools项目集的一部分,该软件可在这个网址找到  http://pgfoundry.org/projects/skytools/


                                              2、逻辑复制应用场景

                                              可基于表级别复制,是一种粒度可细的复制,主要用在以下场景:

                                              • 1、满足业务上需求,实现某些指定表数据同步

                                              • 2、报表系统,采集报表数据

                                              • 3、可从多个上游服务器,做数据的聚集和合并

                                              • 4、PostgreSQL 跨版本数据同步

                                              • 5、PostgreSQL 大版本升级


                                              3、逻辑复制原理

                                              回放WAL日志中的逻辑条目

                                              • 发布者/订阅者模型

                                              • 使用复制槽

                                              • 订阅连接槽

                                              • 并行流

                                              • 复制SQL操作的结果,不是SQL本身


                                              4、逻辑复制相关概念-逻辑复制-发布

                                              publication - 发布

                                              可以在任何物理复制主机上定义发布;定义发布的节点称为发布者;
                                              发布是从一个表或一组表中生成的一组更改,也可能被描述为更改集或复制集;
                                              每个发布只存在于一个数据库中。
                                              发布与模式不同,不影响表格的访问方式;如果需要,每张表可以添加到多个发布;
                                              发布目前可能只包含表;对象必须显式添加, 除非为ALL TABLES创建了一个发布。
                                              发布可以选择将它们所产生的改变限制在INSERT, UPDATE和DELETE的任意组合上, 类似于触发器 默认情况下,复制所有操作类型


                                              5、逻辑复制相关概念-逻辑复制-发布语法


                                              6、逻辑复制-发布

                                                Create table t1(id int);
                                                Create table t2(id int);
                                                publication - 发布语法示例
                                                #创建一个发布,发布两个表中所有更改:
                                                CREATE PUBLICATION mypublication FOR TABLE t1, t2;
                                                #创建一个发布,发布所有表中的所有更改:
                                                CREATE PUBLICATION alltables FOR ALL TABLES;
                                                #创建一个发布,只发布一个表中的INSERT操作:
                                                CREATE PUBLICATION insert_only FOR TABLE mydata WITH (publish = 'insert');
                                                #将发布修改为只发布删除和更新:
                                                ALTER PUBLICATION noinsert SET (publish = 'update, delete');
                                                #给发布添加一些表:
                                                ALTER PUBLICATION mypublication ADD TABLE users, departments;
                                                #查看发布
                                                select * from pg_publication;

                                                7、逻辑复制-订阅

                                                subscription - 订阅

                                                订阅是逻辑复制的下游端。定义订阅的节点被称为 订阅者。
                                                订阅定义了与另一个数据库的连接以及它想要订阅的一组发布(一个或多个)。
                                                订阅者数据库可以通过定义自己的发布来用作其他数据库的发布者。
                                                如果需要,一个订阅者节点可以有多个订阅。
                                                每个订阅都将通过一个复制槽接收更改。
                                                逻辑复制订阅可以作为同步复制的备用数据库。


                                                subscription - 订阅语法


                                                subscription - 订阅语法示例

                                                创建一个到远程服务器的订阅,复制发布mypublication和 insert_only中的表,并在提交时立即开始复制:
                                                  CREATE SUBSCRIPTION mysub
                                                  CONNECTION 'host=192.168.200.142 port=5432 user=postgres dbname=postgres'
                                                  PUBLICATION mypublication;
                                                  vi postgresql.conf wal_level = logical
                                                  创建一个到远程服务器的订阅,复制insert_only发布中的表, 并且不开始复制直到稍后启用复制。
                                                    CREATE SUBSCRIPTION mysub
                                                    CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
                                                    PUBLICATION insert_only
                                                    WITH (enabled = false);

                                                    将订阅的发布更改为insert_only:

                                                      ALTER SUBSCRIPTION mysub SET PUBLICATION insert_only;

                                                      禁用(停止)订阅:

                                                        ALTER SUBSCRIPTION mysub DISABLE;

                                                        8、逻辑复制-配置

                                                        subscription - 订阅相关配置

                                                        max_logical_replication_workers (int)--指定逻辑复制工作的最大数量。这包括应用工作和表同步工作。
                                                        逻辑复制工作进程是从max_worker_processes 定义的进程池中取出的。默认值是4。

                                                         

                                                        max_sync_workers_per_subscription (integer)
                                                        每个订阅的最大同步工作者数量。此参数控制订阅初始化期间或添加新表时初始数据副本的并行数量。
                                                        目前,每个表只能有一个同步工作进程。同步工作进程是从max_logical_replication_workers 定义的进程池中取出的。默认值是2。

                                                        9、逻辑复制-复制槽

                                                        Replication Slots - 复制槽(发布端)

                                                        每个(活动)订阅都从远程(发布)端的复制槽接收更改。
                                                        通常,使用CREATE SUBSCRIPTION 创建订阅时会自动创建远程复制槽,使用DROP SUBSCRIPTION 删除订阅时会自动删除该槽。
                                                        复制槽提供了一种自动化的方法来确保主控机在所有的后备机收到 WAL段之前不会移除它们,主库随时知道从库应用 wal 的情况 , 哪怕从库掉线,主库依然保留 wal日志
                                                        这种机制的缺点是,如果从库掉线很久, 那么主库的wal日志 会一直保留以至于撑暴硬盘, 这时监控需要做到位。
                                                          #postgresql.conf关联配置
                                                          wal_level = logical #minimal replica
                                                          max_replication_slots = 10 #max_replication_slots 值最少需设置成 1,设置后重启数据库生效。
                                                          #创建复制槽
                                                          select * from pg_create_logical_replication_slot('log_slot1', 'test_decoding');
                                                          #查看复制槽信息
                                                          select * from pg_replication_slots where slot_name='log_slot1';
                                                          #查看复制槽解析记录,记录只能查询一次,之后查询为空,如果想重复查询到日志,
                                                          需使用pg_logical_slot_peek_changes() 函数
                                                          SELECT * FROM pg_logical_slot_get_changes('log_slot1', NULL, NULL);
                                                          #使用 pg_recvlogical 接收数据变化
                                                          pg_recvlogical -h 127.0.0.1 -d postgres --slot log_slot1 --start -f -

                                                          10、逻辑复制的限制

                                                          版本限制

                                                          pglogical是逻辑复制的技术组件,功能使用存在数据库版本限制:

                                                          数据源发布和订阅节点需要运行 PostgreSQL 9.4 +
                                                          复制源过滤和冲突检测需要 PostgreSQL 9.5 +
                                                          pglogical 支持跨 PostgreSQL 主要版本之间的复制
                                                          但在订阅服务器上不同版本之间进行复制时,可能会出现问题。
                                                          支持从旧版本复制到新版本因为 PostgreSQL 的向后兼容性保证的,但只有有限的向前兼容性比较安全

                                                          11、逻辑复制-其它限制

                                                          • 不支持DDL复制(ALTER TABLE/CREATE TABLE)

                                                          • 不支持TEMPRORARY表和UNLOGGED表复制

                                                          • 不支持Sequences复制( serial/bigserial/identity)

                                                          • 不支持TRUNCATE操作复制

                                                          • 不支持大对象复制(Bytea)

                                                          • 不支持视图、物化视图、外部表复制

                                                          被复制的表上最好有主键约束;
                                                          如果没有,必须执行:
                                                          ALTER TABLE reptest REPLICA IDENTITY FULL;
                                                          订阅端的复制表是可修改的,复制表一旦修改,发布者和订阅者会数据不一致,可能会打破复制。


                                                          以下两种情况逻辑复制不支持:

                                                          publisher->public.foo replicates to subscriber→private.foo

                                                          publisher->public.foo replicates to subscriber->partitioned->public.foo


                                                          分区表的逻辑复制,需要面向子表创建发布:

                                                            CREATE TABLE parttest (id bigint, test text);
                                                            – PRIMARY KEYS don’t work on partitioned tables
                                                            CREATE TABLE parttest0 PARTITION OF
                                                            parttest FOR VALUES FROM (1) TO (100);
                                                            CREATE TABLE parttest1 PARTITION OF
                                                            parttest FOR VALUES FROM (101) TO (200);
                                                            CREATE PUBLICATION parttest FOR TABLE parttest0,parttest1;

                                                            12、逻辑复制-示例1

                                                            • 1.  规划发布、订阅节点(可以是一台主机的两个实例,或者不同主机的两个实例)

                                                            • 2.  发布节点配置文件

                                                              postgresql.conf :wal_level = logical & listen_addresses = '*'
                                                              pg_hba.conf: host all repuser 订阅端ip/32 md5


                                                              • 3.  发布节点创建复制用户

                                                                create role repuser login CONNECTION LIMIT 10 replication encrypted password 'repuser';
                                                                \du

                                                                备注:用于逻辑复制的用户必须是  replication 角色 superuser 角色


                                                                • 4.  发布节点为复制用户授权

                                                                  create table test_lr1(id int4 primary key ,name text);
                                                                  insert into test_lr1 values (1,'a');
                                                                  grant connect on database postgres to repuser;
                                                                  grant usage on schema public to repuser;
                                                                  grant select on test_lr1 to repuser;
                                                                  \dp+ test_lr1;


                                                                  • 5.  发布节点为复制表创建发布

                                                                    create PUBLICATION pub1 FOR TABLE test_lr1;       #为表test_lr1创建发布
                                                                    select * from pg_publication; #查看创建的发布


                                                                    • 6.  订阅节点创建接收表及订阅

                                                                      create table test_lr1(id int4 ,name text);
                                                                      create subscription sub1 connection 'host=192.168.200.142 port=5432 dbname=postgres user=repuser password=repuser' publication pub1;
                                                                      select * from pg_subscription;


                                                                      • 7.  配置完成,发布节点分别向表中插入数据

                                                                        insert into test_lr1 values (6,'e');


                                                                        • 8.  订阅节点查看逻辑复制效果

                                                                          select * from test_lr1 ;



                                                                          13、逻辑复制-示例2

                                                                          • 1.  发布节点修改发布,测试插入1000万数据的逻辑同步

                                                                            create table test_big2(id int4 primary key, create_time timestamp without time zone default clock_timestamp(), name character varying(32));
                                                                            grant select on test_big2 to repuser;
                                                                            ALTER PUBLICATION pub1 add TABLE test_big2;


                                                                            • 2.  订阅节点创建同步表并手动执行刷新命令

                                                                              create table test_big2(id int4 primary key, create_time timestamp without time zone default clock_timestamp(), name character varying(32));
                                                                              ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION ;


                                                                              • 3.  逻辑同步结果

                                                                              上游节点插入数据

                                                                                insert into test_big2(id,name) select n,n*random()*10000 from generate_series(1,10000000) n ; 
                                                                                select * from test_big2 ;

                                                                                1000万数据同步大概用了不到30秒,速度很快。



                                                                                14、逻辑复制-小结

                                                                                publication - 发布者

                                                                                逻辑复制的前提是将数据库 wal_level 参数设置成 logical;
                                                                                源库上逻辑复制的用户必须具有 replicatoin 或 superuser 角色;
                                                                                逻辑复制目前仅支持数据库表逻辑复制,其它对象例如函数、视图不支持;
                                                                                逻辑复制支持DML(UPDATE、INSERT、DELETE)操作,TRUNCATE 和 DDL 操作不支持;
                                                                                需要发布逻辑复制的表,须配置表的 REPLICA IDENTITY 特性,或者逻辑同步的表两端含有主键约束;
                                                                                一个数据库中可以有多个publication,通过 pg_publication  查看;
                                                                                允许一次发布所有表,语法:CREATE PUBLICATION alltables FOR ALL TABLES;

                                                                                subscription - 订阅者

                                                                                订阅节点需要指定发布者的连接信息;
                                                                                一个数据库中可以有多个订阅者;
                                                                                可以使用enable/disable启用/暂停该订阅;
                                                                                发布节点和订阅节点表的模式名、表名必须一致,订阅节点允许表有额外字段;
                                                                                发布节点增加表字段,订阅节点需要执行:ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION

                                                                                 

                                                                                流复制与逻辑复制主要差异:

                                                                                流复制是物理复制,核心原理是主库将预写日志WAL日志流发送给备库,备库接收到WAL日志流后进行重做。
                                                                                流复制只能对PG实例级进行复制,而逻辑复制能够对数据库表级记性复制。
                                                                                流复制能对DDL操作进行复制,逻辑复制主库上的DDL操作不会复制到备库。
                                                                                流复制主库可读写,但从库只允许查询不允许写入,而逻辑复制的从库可读写。
                                                                                流复制要求PG大版本必须一致,逻辑复制支持跨PG大版本。



                                                                                文章转载自云贝教育,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                                                评论