postgresql16 物理复制与逻辑复制的实现和对比

本文面向想要练习 PostgreSQL 中数据库复制基础知识但可能无法访问远程服务器的初学者。我认为学习新技术时,在自己的机器上运行示例以巩固概念是至关重要的。对于副本来说,这可能很困难,因为许多可用的资源假设用户具有一定的 PostgreSQL 经验并且可以访问另一台运行副本的服务器。我们不会在这里做任何假设,唯一的先决条件是您已经安装了 Postgres 并且可以登录到 shell。

准备工作

对于这些示例,我们使用 Ubuntu 22.10 来运行 PostgreSQL 16(开发版本)。

$HOME/pg/data

$HOME/pg/data 作为我们的数据目录。第一个示例将介绍使用流式物理复制,然后是使用 日志传输 (shipping)进行物理复制的简短示例,然后是逻辑复制的示例,最后总结不同的复制类型及其功能。

流式物理复制

首先,我们以超级用户身份登录主服务器,通常这是默认用户“postgres”。

psql -U postgres

进入 shell 后,为复制创建一个新角色,在主服务器中创建一个表,向该表中插入一些数据,然后退出。

postgres=# CREATE ROLE rep_user WITH REPLICATION LOGIN PASSWORD 'rep_pass';
postgres=# create table t1(a int, b int);
postgres=# insert into t1 values (1,2);
postgres=# \q

打开 $HOME/pg/data/postgres.conf 并确保

listen_addresses = 'localhost'

然后,将以下内容附加到 $HOME/pg/data/pg_hba.conf 的末尾

host  replication   rep_user  localhost   md5

运行以下命令创建主服务器的备份,然后将其用作副本服务器的数据目录(位于 $HOME/pg/rep)。

$ pg_basebackup -h localhost -U rep_user -X stream -C -S replica_1 -v -R -W -D $HOME/pg/rep

创建此目录后,打开 $HOME/pg/rep/postgres.conf 并将端口号设置为 PostgreSQL 默认 5432 以外的其他值。

port = 5433

然后在 postgres.conf 中编辑以primary_conninfo开始的 行,使其如下所示:

primary_conninfo = 'dbname=postgres user=postgres host=localgost port=5432 sslmode=disable'

在启动服务器之前我们必须做的最后一件事是在副本服务器的数据文件夹中创建一个 空的standby.signal文件。在命令行中输入以下内容:

$ touch $HOME/pg/rep/standby.signal

打开第二个终端,因为我们将在主机上启动两个独立的 PostgreSQL 实例,每个实例位于不同的端口上。

在第一个终端(主服务器)中,输入:

pg_ctl -D $HOME/pg/data start

在第二个终端(副本服务器)中,输入:

pg_ctl -D $HOME/pg/rep start

每个都应该给出一个输出,表明服务器已启动。我们可以通过输入以下内容来确认这一点:

$ ps -aux | grep postgres
tristen    29088  0.0  0.1 175596 18580 ?        Ss   15:42   0:00 /home/tristen/disk/pgapp/bin/postgres -D /home/tristen/pg/data
tristen    29089  0.0  0.0 175728  2388 ?        Ss   15:42   0:00 postgres: checkpointer 
tristen    29090  0.0  0.0 175752  2364 ?        Ss   15:42   0:00 postgres: background writer 
tristen    29092  0.0  0.0 175596  7424 ?        Ss   15:42   0:00 postgres: walwriter 
tristen    29093  0.0  0.0 177196  4892 ?        Ss   15:42   0:00 postgres: autovacuum launcher 
tristen    29094  0.0  0.0 177176  4984 ?        Ss   15:42   0:00 postgres: logical replication launcher 
tristen    29112  0.0  0.1 175596 18456 ?        Ss   15:42   0:00 /home/tristen/disk/pgapp/bin/postgres -D /home/tristen/pg/rep
tristen    29113  0.0  0.0 175728  2564 ?        Ss   15:42   0:00 postgres: checkpointer 
tristen    29114  0.0  0.0 175596  2564 ?        Ss   15:42   0:00 postgres: background writer 
tristen    29115  0.0  0.0 176348  4488 ?        Ss   15:42   0:00 postgres: startup recovering 000000010000000000000014
tristen    29116  0.0  0.0 176136  3664 ?        Ss   15:42   0:00 postgres: walreceiver streaming 0/14000110
tristen    29117  0.0  0.0 177336  7060 ?        Ss   15:42   0:00 postgres: walsender rep_user 127.0.0.1(39650) streaming 0/14000110
tristen    29126  0.0  0.0  17580  2252 pts/2    S+   15:42   0:00 grep --color=auto postgres

可以看到有两个进程 $HOME/disk/pgapp/bin/postgres 在不同的两个目录中运行。

在这里插入图片描述

现在,我们将连接到两个实例中的每一个。首先是第一个终端上的主服务器:

psql -U postgres -p 5432

然后是第二个终端上的副本服务器:

psql -U postgres -p 5433

-p 标志用于指定端口。在主服务器上使用它是多余的,因为它使用默认端口。然而,这很好地表明我们确实连接到两个不同的端口,从而连接到两个不同的 PostgreSQL 实例。

如果我们在 Postgres shell 中输入 \d,可以获得数据库中的表的列表。在副本服务器相对应的第二个终端中,可以看到表 t1 确实被复制了。

postgres=# \dList of relationsSchema | Name | Type  |  Owner   
--------+------+-------+----------public | t1   | table | postgres
(1 row)

现在让在主服务器上创建一个新表,以表明它将自动复制到副本服务器。

在第一个终端上输入:

postgres=# create table t2(c int, d text);
CREATE TABLE
postgres=# insert into t2 values (3, 'hello');
INSERT 0 1

在第二个终端中输入:

postgres=# select * from t2;c |   d   
---+-------3 | hello
(1 row)

可以看到,不仅新表是从主服务器复制过来的,而且刚刚插入的数据也是如此。

值得注意的是,这种复制关系只是单向的。也就是说,副本服务器只能从主服务器的复制。如果尝试在副本服务器中创建表,则会收到一条错误消息:

ERROR:  cannot execute CREATE TABLE in a read-only transaction

从逻辑上讲,这是因为我们希望副本是只读的,以使来自 主服务器的数据更容易可用。

日志传输物理复制

日志传输(Shipping)物理复制 与 流式物理复制 有类似的设置,主要只是更改一些配置文件。在主服务器上编辑 postgresql.conf

wal_level = replica
archive_mode = on
archive_command = 'cp %p /path/to/archive/%f'

如果服务器正在运行,请重新启动服务器并创建主服务器的备份:

$ pg_basebackup -h localhost -U rep_user -X fetch -v -R -W -D $HOME/pg/rep

打开 $HOME/pg/rep/postgresql.conf 并编辑两行。注释掉archive_command 开头的行,并取消注释Restore_command 开头的行,添加在 = 之后进行恢复时要使用的命令。这些行应如下所示:

#archive_command = 'cp %p /path/to/archive/%f'
restore_command = 'cp /path/to/archive/%f %p'

像 流式物理复制 示例中一样启动两台服务器,应该可以看到副本正在复制主服务器。请注意,日志传输更改(shipping changes)可能不会像在流式物理复制中那样实时。日志传输复制具有较高的延迟,并且仅在 WAL 文件填充到副本时才发送更改。

逻辑复制

逻辑复制与物理复制具有与服务器相同的初始设置。它涉及一些不同的配置,还涉及一些手动编辑以使表正常工作。

在主服务器上编辑 postgresql.conf

wal_level = logical

确保主服务器上的 pga_hba.conf 包含复制用户的正确连接权限。

host     all     rep_user     localhost     md5

一旦所有配置准备就绪,要启动两台服务器并登录 shell。回想一下,在我们的示例中,每个服务器都使用不同的端口号,因此请确保您没有两次使用同一服务器!在每个服务器各自的 shell 中,我们需要在每个服务器中创建一个具有 相同名称和结构 的表。如果表完全不同,副本将无法找到它,将收到错误。也就是说,在两个终端中输入以下内容:

postgres=# create table t1(id int, val text);

现在我们在两个数据库中都有相同的空表,继续以下命令复制该表。在主服务器上输入以下命令为表 t1 创建发布:

postgres=# CREATE PUBLICATION pub_t1 FOR TABLE t1;

这将为表 t1 创建一个发布,这意味着表 t1 将被复制到副本服务器。可以复制任意数量的表作为发布的一部分。

创建发布后,将副本数据库中的表 t1 订阅到主数据库中的表 t1。为此,必须在副本数据库中创建订阅,如下所示:

postgres=# CREATE SUBSCRIPTION sub_t1 CONNECTION 'dbname=postgres host=localhost port=5432 user=postgres' PUBLICATION pub_t1;

注意,在本示例中,我们同时使用了默认用户 postgres 和默认数据库 postgres,但这不是必需的。

现在已经创建了订阅,将看到主服务器表 t1 中的所有更新都出现在副本服务器的表 t1 中。主服务器上没有做发布的任何其他表都不会复制到副本。现在做一个测试,在主数据库终端中向表 t1 中插入一些数据:

postgres=# insert into t1 values (1, 'hello');
INSERT 0 1
postgres=# insert into t1 values (2, 'world');
INSERT 0 1

现在让我们看看该表是否实际上已复制到副本服务器中:

postgres=# select * from t1;id |  val  
----+-------1 | hello2 | world
(2 rows)

现在,数据已成功复制到副本服务器。但我们怎么区分这是不是物理复制呢?好吧,让我们向主数据库添加一个新表并插入一些数据:

postgres=# create table t2(c int, d int);
CREATE TABLE
postgres=# insert into t2 values(3,4);
INSERT 0 1
postgres=# insert into t2 values(5,6);
INSERT 0 1

再检查一下副本:

postgres=# select * from t2;
ERROR:  relation "t2" does not exist
LINE 1: select * from t2;

可以看到表 t2 没有被复制,因为它没有订阅,因此不存在于副本中。这允许将某些表从中央数据库公开给远程数据库,同时保持其他表的私有性。

但是对 表结构 的 修改 在副本数据库中 结果会如何?好吧,让我们测试一下。在主数据库终端中,更改表结构并添加一些数据:

postgres=# alter table t1 add column x int;
ALTER TABLE
postgres=# insert into t1 values (3, 'foo', 42);
INSERT 0 1
postgres=# insert into t1 values (4, 'bar', 10);
INSERT 0 1
postgres=# select * from t1;id |  val  | x  
----+-------+----1 | hello |   2 | world |   3 | foo   | 424 | bar   | 10
(4 rows)

现在检查一下副本服务器:

postgres=# select * from t1;id |  val  
----+-------1 | hello2 | world
(2 rows)

可以看到副本服务器不再复制主服务器。即使将同一架构中的新数据添加到主数据库中,它也不会显示在副本中:

postgres=# insert into t1 values (15, 'test');
INSERT 0 1
postgres=# select * from t1;id |  val  
----+-------1 | hello2 | world
(2 rows)

由于我们更改了 t1 的 架构,副本不再从中复制数据,因为它现在实际上是一个不同的表。如果您更改了表的架构并将副本运行在其他地方,记住这一点很重要。

如果不在副本中创建与主服务器中的表 相匹配的表,会发生什么情况?

postgres=# CREATE PUBLICATION pub_t2 FOR TABLE t2;
CREATE PUBLICATION
postgres=# CREATE SUBSCRIPTION sub_t2 CONNECTION 'dbname=postgres host=localhost port=5432 user=postgres' PUBLICATION pub_t2;
ERROR:  relation "public.t2" does not exist

这样收到一条错误消息,指出我们想要订阅的 关系 不存在。这可能会非常令人困惑,因为我们给了它所有连接信息,并且我们知道表 t2 存在于该位置。但是,此错误是指它在我们的副本上找不到表 t2,因此无法创建订阅。因此,请务必记住,我们需要两个数据库包含具有相同架构的相同表,以便逻辑复制正常工作。

对比

以下是我们从高层次角度讨论的所有主题的摘要。如果您仍在尝试决定要使用哪种复制方法,那么此摘要可能有助于做出决定。

逻辑复制与物理复制
Logical 逻辑复制Physical 物理复制
行级别工作,将对各个数据库行的更改从主服务器复制到副本服务器。通过逻辑复制,我们可以选择要复制哪些表、模式甚至列,从而提供更多粒度。磁盘块级别工作,将数据从主服务器复制到副本。这使得它在时间和空间上都更加高效。
因为逻辑在行级别工作,所以我们可以在不同的 PostgreSQL 版本之间甚至在不同操作系统上运行的 PostgreSQL 实例之间进行备份。由于复制了整个数据库集群,因此我们无法获得逻辑复制的粒度。
逻辑复制支持双向或多主复制设置。我们还需要在同一操作系统上运行相同的 PostgreSQL 版本来制作副本。
逻辑复制的效率低于物理复制物理复制有两种方式,传输 和 流复制

逻辑复制与物理复制的比较

日志传输 与 流式复制
Streaming 流式复制Shipping 日志传输
当发生更改时,将更改从主服务器发送到副本。这会降低延迟并确保副本与主服务器保持同步接近实时,不会在发生更改时发送更改,而是在 WAL 文件填满或达到可配置的超时后发送它们。相对来说延迟较高
持续将 WAL 记录发送到副本,副本会重放这些记录以保持同步文件级别工作,WAL 文件在主服务器上存档并复制到副本服务器上。这是通过手动传输或脚本完成的,然后重播 WAL 文件以与主服务器保持同步。
更高效、更少资源占用,不需要复制或归档 WAL 文件效率较低且资源密集
需要持久连接不需要持久连接,适合连接不稳定或高延迟的环境
副本是只读的。对于负载平衡查询、高可用性和备份很有用,但不适合写入。副本可以配置为只读或读写

流式物理复制与日志传输物理复制的比较

异步流与同步流 复制
Asynchronous 异步Synchronous 同步
如果主服务器比较繁忙,则副本可以落后于主服务器。如果主服务器崩溃,我们就会丢失未复制的数据。性能较高主服务器在收到副本已收到事务的确认之前不会提交。如果主数据库崩溃,我们永远不会丢失数据,但是,如果副本出现问题,此方法可能会减慢主数据库的速度,甚至停止它。此外,由于网络延迟也会对性能产生影响。

异步流与同步流的比较

总结

关于使用 PostgreSQL 设置服务器复制以实现各种复制方法的文章到此结束。首先,我们回顾了设置和运行每种方法的实际步骤,然后查看了每种方法功能的高级摘要。

References 参考

27.2. log-shipping standby servers. PostgreSQL Documentation. (2023, February 9). Retrieved March 29, 2023, from https://www.postgresql.org/docs/current/warm-standby.html

31.11. quick setup. PostgreSQL Documentation. (2023, February 9). Retrieved March 29, 2023, from https://www.postgresql.org/docs/current/logical-replication-quick-setup.html

B, A. (2022, April 8). [web log]. Retrieved March 29, 2023, from https://scalegrid.io/blog/comparing-logical-streaming-replication-postgresql/.

Levinas, M. (2022, October 10). [web log]. Retrieved March 29, 2023, from https://www.cherryservers.com/blog/how-to-set-up-postgresql-database-replication.

Ravoof, S. (2023, February 17). [web log]. Retrieved March 29, 2023, from https://kinsta.com/blog/postgresql-replication/.


原文地址Setting Up a PostgreSQL Replica Server Locally - Highgo Software Inc.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/624537.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网页屏幕适配通透了

一,如果设计尺寸固定 那就按照固定尺寸开发 一般都是1920*1080 二,需要适配多种像素屏幕(大屏可视化) 可使用媒体查询设置多套css样式或者使用自适应单位,%,vw,vh 最好解决方案rem&#xff…

IPv6路由综合运用

一、基础配置: SWA: sw1(config)#host swA swA(config)#ipv6 ena swA(config)# vlan 100 swA(config-vlan100)#int vlan 100 swA(config-if-vlan100)#ipv6 ena swA(config-vlan100)#ip add 172.16.1.1 255.255.255.252 swA(config-if-vlan100)#int e1/0/24 swA(conf…

教育科学杂志教育科学杂志社教育科学编辑部2023年第12期部分目录

“思政教育联合思维导图”教学模式在肝病感染控制护理临床教学中的应用 黄雪霞;陈海涵;蒋雅文 中职语文教学中厚植爱国主义情怀的要点分析 苏琴 职业素养视角下中职计算机专业课程教学改革实践 李石 产教融合背景下高职院校产业学院建设模式的实践探索 郭洋 《工程制图与CAD》课…

数据结构学习 jz31 栈的压入、弹出序列

关键词:模拟 栈 不太熟,调了好一阵子。 题目:https://leetcode.cn/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof/ 思路: 主要是利用一个辅助栈,来模拟这个过程,如果过程不行就返回失败。 int sig 0;如…

Java研学-分页查询

一 分页概述 1 介绍 将大量数据分段显示,避免一次性加载造成的内存溢出风险 2 真假分页 ① 真分页   一次性查询出所有数据存到内存,翻页从内存中获取数据,性能高但易造成内存溢出 ② 假分页   每次翻页从数据库中查询数据&#xff0c…

解决计算机中vcruntime140.dll错误!六种方法教你修复

什么是vcruntime140.dll文件呢?为什么会出现丢失的情况?如何解决这个问题呢?本文将为您详细介绍vcruntime140.dll文件的作用、丢失原因以及6个快速解决方法。 一、vcruntime140.dll是什么文件? vcruntime140.dll是Visual C Redi…

聚合收益协议 InsFi :打开铭文赛道全新叙事的旋转门

​“InsFi 协议构建了一套以铭文资产为基础的聚合收益体系,该体系正在为铭文资产捕获流动性、释放价值提供基础,该生态也正在成为铭文赛道掘金的新热土。” 在 2023 年年初,Ordinals 协议在比特币链上被推出后,为比特币链上带来了…

STC51+TLC2543+ADXL335+proteus

51单片机解析adxl335振动检测蜂鸣器报警课设 通过按键调整振动检测阈值 传感器介绍 TLC2543:12 位精密模数转换器,原理图与引脚功能描述如下所示: 引脚功能1~9、11、12模拟量输入通道10GND电源地13REF-为负基准电压端14REF为正基准电压端…

基于ssm的课程在线教学平台设计与实现论文

摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统Java课程教学信息管理难度大,容错率低&#x…

数据资产入表背后:中国To B数字化驶入“数据时代”

数据资产“入表”,更像是一剂通过颠覆旧的生产关系,从根上医治数字化转型的“良方”。 那么,数据资产到底是什么?以及在愈发被规范的数据市场大背景下,对中国的To B企业和To B服务商而言,正在或者即将发生…

【记忆化搜索】

欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:【LeetCode】winter vacation training 前言 记忆化搜索是一种优化搜索算法的方法,它可…

基于JavaWeb+BS架构+SpringBoot+Vue基于hive旅游数据的分析与应用系统的设计和实现

基于JavaWebBS架构SpringBootVue基于hive旅游数据的分析与应用系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 1 概 述 5 1.1 研究背景 5 1.2 研究意义 5 1.3 研究内容…

Java实现二维码、条形码生成器

文章目录 前言 在数字化时代,二维码已经成为了信息交流的一种常见方式。它们被广泛用于各种应用,从产品标签到活动传单,以及电子支付。本文将向您展示如何在Spring Boot应用程序中整合ZXing库,以创建和解析QR码。无论您是想为您的…

常用文件文档能做二维码吗?多种文件在线做二维码的方法

二维码是现在工作和生活中随处可见,可以用来展现很多不同的内容。现在很多下发通知的文件、教程或者其他文件内容,也会制作成二维码图片后,让其他人通过扫码来获取文件内容。最简单的制作方法就是通过二维码生成器来制作,支持多种…

【KMP】【二分查找】【C++算法】100207. 找出数组中的美丽下标 II

作者推荐 【矩阵快速幂】封装类及测试用例及样例 本文涉及的基础知识点 二分查找算法合集 LeetCode100207. 找出数组中的美丽下标 II 给你一个下标从 0 开始的字符串 s 、字符串 a 、字符串 b 和一个整数 k 。 如果下标 i 满足以下条件,则认为它是一个 美丽下标…

usb个人总结

一、usb工具分析 1、不同的usb抓包工具抓包分析 2、USB抓包分析方式 外接usb分析仪分析 (1)力科usb分析仪 (2)HD-USB12 协议分析仪 (3)沁恒CH552 usb分析仪,软件工具USB2.0 Monitor (4)等等…

RICHARD MILLE 理查米尔顶级制表技艺典范之作:RM 35-03腕表

瑞士高端制表品牌RICHARD MILLE(理查米尔)在腕表界拥有着卓越的地位,其材质、技术、设计、品质以及服务独树一帜,皆被认为是顶级水平的代表,经典作品数不胜数。而即使在众多经典之作中,RM 35-03 Rafael Nadal 自动上链腕表仍以独特蝶形摆陀和强大性能备受用户推崇,被誉为“RICH…

2024三掌柜赠书活动第三期:Rust系统编程

目录 前言 Rust语言概念 关于《Rust系统编程》 Rust系统编程的核心点 Rust系统编程的关键技术和工具 编辑推荐 内容简介 作者简介 图书目录 书中前言/序言 《Rust系统编程》全书速览 结束语 前言 在技术圈,最近的编程语言新秀当属Rust莫属,R…

2024 解决matplotlib中文字体问题

第一种代码(失败代码) import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.font_manager import FontPropertiesfont_path /Users/huangbaixi/Desktop/SimHei.ttfdef plot_demo():#print(mpl.get_cachedir())# 绘制折线图font…