• 售前

  • 售后

热门帖子
入门百科

postgresql流复制原理以及流复制和逻辑复制的区别说明

[复制链接]
我要去2018 显示全部楼层 发表于 2021-10-26 12:56:13 |阅读模式 打印 上一主题 下一主题
流复制的原理:

物理复制也叫流复制,流复制的原理是主库把WAL发送给备库,备库吸收WAL后,举行重放。
逻辑复制的原理:

逻辑复制也是基于WAL文件,在逻辑复制中把主库称为源端库,备库称为目的端数据库,源端数据库根据预先指定好的逻辑剖析规则对WAL文件举行剖析,把DML利用剖析成肯定的逻辑变革信息(标准SQL语句),源端数据库把标准SQL语句发给目的端数据库,目的端数据库吸收到之后举行应用,从而实现数据同步。
流复制和逻辑复制的区别:

流复制主库上的事件提交不必要等候备库吸收到WAL文件后的确认,逻辑复制相反。
流复制要求主备库的大版本划一,逻辑复制可以跨大版本的数据同步,也可以实现异构数据库的数据同步。
流复制的主库可读写,从库只允许读,逻辑复制的目的端数据库要求可读写
流复制是对实例级别的复制(整个postgresql数据库),逻辑复制是选择性的复制一些表,以是是对表级别的复制。
流复制有主库的DDL、DML利用,逻辑复制只有DML利用。
增补:PostgreSQL 同步流复制原理和代码浅析
配景

数据库ACID中的长期化怎样实现
数据库ACID内里的D,长期化。 指的是对于用户来说提交的事件,数据是可靠的,纵然数据库crash了,在硬件齐备的情况下,也能规复返来。
PostgreSQL是怎么做到的呢,看一幅图,画得比较丑,拼集看吧。
假设一个事件,对数据库做了一些利用,并且产生了一些脏数据,首先这些脏数据会在数据库的shared buffer中。
同时,产生这些脏数据的同时也会产生对应的redo信息,产生的REDO会有对应的LSN号(你可以明白为REDO 的虚拟地点空间的一个唯一的OFFSET,每一笔REDO都有),这个LSN号也会记录到shared buffer中对应的脏页中。
walwriter是负责将wal buffer flush到长期化装备的进程,同时它会更新一个全局变量,记录已经flush的最大的LSN号。
bgwriter是负责将shared buffer的脏页长期化到长期化装备的进程,它在flush时,除了要遵循LRU算法之外,还要通过LSN全局变量的比对,来包管脏页对应的REDO记录已经flush到长期化装备了,假如发现还对应的REDO没有长期化,会触发WAL writer去flush wal buffer。 (即确保日志比脏数据先落盘)
当用户提交事件时,也会产生一笔提交事件的REDO,这笔REDO也携带了LSN号。backend process 同样必要等候对应LSN flush到磁盘后才会返回给用户提交乐成的信号。(包管日志先落盘,然后返回给用户)
数据库同步复制原理浅析

同步流复制,即包管standby节点和本地节点的日志双双落盘。
PostgreSQL利用另一组全局变量,记录同步流复制节点已经吸收到的XLOG LSN,以及已经长期化的XLOG LSN。
用户在发起提交哀求后,backend process除了要判断本地wal有没有长期化,同时还必要判断同步流复制节点的XLOG有没有吸收到或长期化(通过synchronous_commit参数控制)。
假如同步流复制节点的XLOG还没有吸收或长期化,backend process会进入等候状态。
数据库同步复制代码浅析

对应的代码和表明如下:
  1. CommitTransaction @ src/backend/access/transam/xact.c
  2. RecordTransactionCommit @ src/backend/access/transam/xact.c
复制代码
  1.   /*
  2.    * If we didn't create XLOG entries, we're done here; otherwise we
  3.    * should trigger flushing those entries the same as a commit record
  4.    * would. This will primarily happen for HOT pruning and the like; we
  5.    * want these to be flushed to disk in due time.
  6.    */
  7.   if (!wrote_xlog) // 没有产生redo的事务,直接返回
  8.    goto cleanup;
  9. if (wrote_xlog && markXidCommitted) // 如果产生了redo, 等待同步流复制
  10.   SyncRepWaitForLSN(XactLastRecEnd);
复制代码
  1. SyncRepWaitForLSN @ src/backend/replication/syncrep.c
复制代码
  1. /*
  2. * Wait for synchronous replication, if requested by user.
  3. *
  4. * Initially backends start in state SYNC_REP_NOT_WAITING and then
  5. * change that state to SYNC_REP_WAITING before adding ourselves
  6. * to the wait queue. During SyncRepWakeQueue() a WALSender changes
  7. * the state to SYNC_REP_WAIT_COMPLETE once replication is confirmed.
  8. * This backend then resets its state to SYNC_REP_NOT_WAITING.
  9. */
  10. void
  11. SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
  12. {
  13. ...
  14. /*
  15.   * Fast exit if user has not requested sync replication, or there are no
  16.   * sync replication standby names defined. Note that those standbys don't
  17.   * need to be connected.
  18.   */
  19. if (!SyncRepRequested() || !SyncStandbysDefined()) // 如果不是同步事务或者没有定义同步流复制节点,直接返回
  20.   return;
  21. ...
  22. /*
  23.   * We don't wait for sync rep if WalSndCtl->sync_standbys_defined is not
  24.   * set. See SyncRepUpdateSyncStandbysDefined.
  25.   *
  26.   * Also check that the standby hasn't already replied. Unlikely race
  27.   * condition but we'll be fetching that cache line anyway so it's likely
  28.   * to be a low cost check.
  29.   */
  30. if (!WalSndCtl->sync_standbys_defined ||  
  31.   XactCommitLSN <= WalSndCtl->lsn[mode]) // 如果没有定义同步流复制节点,或者判断到commit lsn小于已同步的LSN,说明XLOG已经flush了,直接返回。
  32. {
  33.   LWLockRelease(SyncRepLock);
  34.   return;
  35. }
  36. ...
  37. // 进入循环等待状态,说明本地的xlog已经flush了,只是等待同步流复制节点的REDO同步状态。
  38. /*
  39.   * Wait for specified LSN to be confirmed.
  40.   *
  41.   * Each proc has its own wait latch, so we perform a normal latch
  42.   * check/wait loop here.
  43.   */
  44. for (;;) // 进入等待状态,检查latch是否满足释放等待的条件(wal sender会根据REDO的同步情况,实时更新对应的latch)
  45. {
  46.   int   syncRepState;
  47.   /* Must reset the latch before testing state. */
  48.   ResetLatch(&MyProc->procLatch);
  49.   syncRepState = MyProc->syncRepState;
  50.   if (syncRepState == SYNC_REP_WAITING)
  51.   {
  52.    LWLockAcquire(SyncRepLock, LW_SHARED);
  53.    syncRepState = MyProc->syncRepState;
  54.    LWLockRelease(SyncRepLock);
  55.   }
  56.   if (syncRepState == SYNC_REP_WAIT_COMPLETE) // 说明XLOG同步完成,退出等待
  57.    break;
  58. // 如果本地进程挂了,输出的消息内容是,本地事务信息已持久化,但是远程也许还没有持久化
  59.   if (ProcDiePending)
  60.   {
  61.    ereport(WARNING,
  62.      (errcode(ERRCODE_ADMIN_SHUTDOWN),
  63.       errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
  64.       errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
  65.    whereToSendOutput = DestNone;
  66.    SyncRepCancelWait();
  67.    break;
  68.   }
  69. // 如果用户主动cancel query,输出的消息内容是,本地事务信息已持久化,但是远程也许还没有持久化
  70.   if (QueryCancelPending)
  71.   {
  72.    QueryCancelPending = false;
  73.    ereport(WARNING,
  74.      (errmsg("canceling wait for synchronous replication due to user request"),
  75.       errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
  76.    SyncRepCancelWait();
  77.    break;
  78.   }
  79. // 如果postgres主进程挂了,进入退出流程。
  80.   if (!PostmasterIsAlive())
  81.   {
  82.    ProcDiePending = true;
  83.    whereToSendOutput = DestNone;
  84.    SyncRepCancelWait();
  85.    break;
  86.   }
  87. // 等待wal sender来修改对应的latch
  88.   /*
  89.    * Wait on latch. Any condition that should wake us up will set the
  90.    * latch, so no need for timeout.
  91.    */
  92.   WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
复制代码
注意用户进入等候状态后,只有主动cancel , 或者kill(terminate) , 或者主进程die才华退出无限的等候状态。背面会讲到怎样将同步级别降级为异步。
前面提到了,用户端必要等候LATCH的释放信号。
那么谁来给它这个信号了,是wal sender进程,源码和表明如下 :
  1. src/backend/replication/walsender.c
复制代码
  1. StartReplication
  2. WalSndLoop
  3. ProcessRepliesIfAny
  4. ProcessStandbyMessage
  5. ProcessStandbyReplyMessage
  6. if (!am_cascading_walsender) // 非级联流复制节点,那么它将调用SyncRepReleaseWaiters修改backend process等待队列中它们对应的 latch。  
  7.   SyncRepReleaseWaiters();
  8. SyncRepReleaseWaiters @ src/backend/replication/syncrep.c
  9. /*
  10. * Update the LSNs on each queue based upon our latest state. This
  11. * implements a simple policy of first-valid-standby-releases-waiter.
  12. *
  13. * Other policies are possible, which would change what we do here and what
  14. * perhaps also which information we store as well.
  15. */
  16. void
  17. SyncRepReleaseWaiters(void)
  18. {
  19. ...
  20.   // 释放满足条件的等待队列
  21. /*
  22.   * Set the lsn first so that when we wake backends they will release up to
  23.   * this location.
  24.   */
  25. if (walsndctl->lsn[SYNC_REP_WAIT_WRITE] < MyWalSnd->write)
  26. {
  27.   walsndctl->lsn[SYNC_REP_WAIT_WRITE] = MyWalSnd->write;
  28.   numwrite = SyncRepWakeQueue(false, SYNC_REP_WAIT_WRITE);
  29. }
  30. if (walsndctl->lsn[SYNC_REP_WAIT_FLUSH] < MyWalSnd->flush)
  31. {
  32.   walsndctl->lsn[SYNC_REP_WAIT_FLUSH] = MyWalSnd->flush;
  33.   numflush = SyncRepWakeQueue(false, SYNC_REP_WAIT_FLUSH);
  34. }
  35. ...
复制代码
  1. SyncRepWakeQueue @ src/backend/replication/syncrep.c
复制代码
  1. /*
  2. * Walk the specified queue from head. Set the state of any backends that
  3. * need to be woken, remove them from the queue, and then wake them.
  4. * Pass all = true to wake whole queue; otherwise, just wake up to
  5. * the walsender's LSN.
  6. *
  7. * Must hold SyncRepLock.
  8. */
  9. static int
  10. SyncRepWakeQueue(bool all, int mode)
  11. {
  12. ...
  13. while (proc) // 修改对应的backend process 的latch
  14. {
  15.   /*
  16.    * Assume the queue is ordered by LSN
  17.    */
  18.   if (!all && walsndctl->lsn[mode] < proc->waitLSN)
  19.    return numprocs;
  20.   /*
  21.    * Move to next proc, so we can delete thisproc from the queue.
  22.    * thisproc is valid, proc may be NULL after this.
  23.    */
  24.   thisproc = proc;
  25.   proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[mode]),
  26.           &(proc->syncRepLinks),
  27.           offsetof(PGPROC, syncRepLinks));
  28.   /*
  29.    * Set state to complete; see SyncRepWaitForLSN() for discussion of
  30.    * the various states.
  31.    */
  32.   thisproc->syncRepState = SYNC_REP_WAIT_COMPLETE; // 满足条件时,改成SYNC_REP_WAIT_COMPLETE
  33. ....
复制代码
怎样设置事件可靠性级别

PostgreSQL 支持在会话中设置事件的可靠性级别。
off 表现commit 时不必要等候wal 长期化。
local 表现commit 是只必要等候本地数据库的wal 长期化。
remote_write 表现commit 必要等候本地数据库的wal 长期化,同时必要等候sync standby节点wal write buffer完成(不必要长期化)。
on 表现commit 必要等候本地数据库的wal 长期化,同时必要等候sync standby节点wal长期化。
提示一点, synchronous_commit 的任何一种设置,都不影响wal日志长期化必须先于shared buffer脏数据长期化。 以是不管你怎么设置,都欠好影响数据的划一性。
  1. synchronous_commit = off # synchronization level;
  2.        # off, local, remote_write, or on
复制代码
怎样实现同步复制降级

从前面的代码剖析可以得知,假如 backend process 进入了等候循环,只担当几种信号降级。 并且降级后会告警,表现本地wal已长期化,但是sync standby节点不确定wal有没有长期化。
假如你只配置了1个standby,并且将它配置为同步流复制节点。一旦出现网络抖动,或者sync standby节点故障,将导致同步事件进入等候状态。
怎么降级呢?
方法1.
修改配置文件并重置
  1. $ vi postgresql.conf
  2. synchronous_commit = local
  3. $ pg_ctl reload
复制代码
然后cancel 全部query .
  1. postgres=# select pg_cancel_backend(pid) from pg_stat_activity where pid<>pg_backend_pid();
复制代码
收到如许的信号,表现事件乐成提交,同时表现WAL不知道有没有同步到sync standby。
  1. WARNING: canceling wait for synchronous replication due to user request
  2. DETAIL: The transaction has already committed locally, but might not have been replicated to the standby.
  3. COMMIT
  4. postgres=# show synchronous_commit ;
  5. synchronous_commit
  6. --------------------
  7. off
  8. (1 row)
复制代码
同时它会读到全局变量synchronous_commit 已经是 local了。
如许就完成了降级的动作。
方法2.
方法1的降级必要对已有的正在等候wal sync的pid利用cancel举行处理,有点不人性化。
可以通过修改代码的方式,做到更人性化。
SyncRepWaitForLSN for循环中,加一个判断,假如发现全局变量sync commit酿成local, off了,则告警并退出。如许就不必要人为的去cancel query了.
  1. WARNING: canceling wait for synchronous replication due to user request
  2. DETAIL: The transaction has already committed locally, but might not have been replicated to the standby.
复制代码
以上为个人经验,渴望能给大家一个参考,也渴望大家多多支持草根技术分享。如有错误或未考虑完全的地方,望不吝见教。

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作