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

news2024/11/18 1:29:52

本文面向想要练习 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=# \d
        List of relations
 Schema | 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 | hello
  2 | 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   | 42
  4 | bar   | 10
(4 rows)

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

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

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

postgres=# insert into t1 values (15, 'test');
INSERT 0 1
postgres=# select * from t1;
 id |  val  
----+-------
  1 | hello
  2 | 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.coloradmin.cn/o/1386367.html

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

相关文章

快乐学Python,如何使用爬虫从网页中提取感兴趣的内容?

前面的内容,我们了解了使用urllib3和selenium来下载网页,但下载下来的是整个网页的内容,那我们又怎么从下载下来的网页中提取我们自己感兴趣的内容呢?这里就需要Python的另一个库来实现-BeautifulSoup。 BeautifulSoup 是一个 Py…

【Py/Java/C++三种语言详解】LeetCode每日一题240115【链表】LeetCode82、删除排序链表中的重复节点II

文章目录 题目链接题目描述解题思路代码pythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目链接 LeetCode82、删除排序链表中的重复节点II 题目描述 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不…

Hotspot源码解析-第十九章-ClassLoaderData、符号表、字符串表的初始化

第十九章-ClassLoaderData初始化 讲解本章先从一张图开始 众所周知,Java类的相关信息都是存储在元空间中的,但是是怎么存储的,相信很多读者是不清楚的,这里就不得不涉及到ClassLoaderDataGraph、classLoader、classLoaderData&…

容器化postgres备份策略

文章目录 1. 策略和背景1.1 背景1.2 备份策略 2. docker-compose的修改2.1 挂载备份目录2.2 备份脚本3.3 重启容器 3. 定时任务 1. 策略和背景 1.1 背景 使用docker-compose管理的postgres数据库需要备份工作目录在 /data/postgres下 1.2 备份策略 要备份的库 shu_han 库 每…

vue3中组合式api的常用方法

vue3中组合式api的常用方法 记录一下vue3中常用的组合式api&#xff0c;包括计算属性computed、监听器watch及watchEffective 一、computed 作用&#xff1a;根据已有数据计算出新数据&#xff08;和Vue2中的computed作用一致&#xff09;。 <template><div class&…

JavaScript 异步编程解决方案-中篇

天下事有难易乎&#xff1f; 为之&#xff0c;则难者亦易矣&#xff1b;不为&#xff0c; 则易者亦难矣。人之为学有难易乎&#xff1f; 学之&#xff0c;则难者亦易矣&#xff1b;不学&#xff0c;则易者亦难矣。 async 函数 和promise then的规则一样 async function fun() …

HarmonyOS4.0——ArkUI应用说明

一、ArkUI框架简介 ArkUI开发框架是方舟开发框架的简称&#xff0c;它是一套构建 HarmonyOS / OpenHarmony 应用界面的声明式UI开发框架&#xff0c;它使用极简的UI信息语法、丰富的UI组件以及实时界面语言工具&#xff0c;帮助开发者提升应用界面开发效率 30%&#xff0c;开发…

element + table 每两行对比相同值列合并

在开始之前先要明确几个概念&#xff1a; 保持不变&#xff1a;{ rowspan: 1, colspan: 1 } 删除一个单元格&#xff1a;{ rowspan: 0, colspan: 0 } 合并一个单元格&#xff1a;{ rowspan: 2, colspan: 1 } <template><div><el-table:data"tableData&quo…

二叉树的遍历 Java

二叉树的遍历 递归法前序遍历中序遍历后序遍历改进 迭代法前序、后序遍历中序遍历 Java 中 null、NULL、nullptr 区别 public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val val; }TreeNode(int val, TreeNode left, Tree…

LLM推理部署(七):FireAttention——通过无损量化比vLLM快4倍

Mixtral作为第一个在数万亿tokens上训练的OSS模型&#xff0c;最近在人工智能社区掀起了波澜&#xff0c;它支持“混合专家”&#xff08;MoE&#xff09;&#xff0c;并且训练和推理速度非常快。 Fireworks AI是第一个托管Mixtral的平台&#xff0c;在Mixtral公开发布之前就托…

小程序中使用微信同声传译插件实现语音识别、语音合成、文本翻译功能----语音识别(一)

官方文档链接&#xff1a;https://mp.weixin.qq.com/wxopen/plugindevdoc?appidwx069ba97219f66d99&token370941954&langzh_CN#- 要使用插件需要先在小程序管理后台的设置->第三方设置->插件管理中添加插件&#xff0c;目前该插件仅认证后的小程序。 语音识别…

༺༽༾ཊ—游戏-01_2D-开发—ཏ༿༼༻

首先利用安装好的Unity Hub创建一个unity 2D&#xff08;URP渲染管线&#xff09;项目 选择个人喜欢的操作格局&#xff08;这里采用2 by 3&#xff09; 在Project项目管理中将双栏改为单栏模式&#xff08;个人喜好&#xff09; 找到首选项&#xff08;Preferences&#xff09…

2024 解决matplotlib中文字体问题

第一种代码&#xff08;失败代码&#xff09; 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…

【记忆化搜索】

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;【LeetCode】winter vacation training 前言 记忆化搜索是一种优化搜索算法的方法&#xff0c;它可…

Apache StringUtils:Java字符串处理工具类

简介 在我们的代码中经常需要对字符串判空&#xff0c;截取字符串、转换大小写、分隔字符串、比较字符串、去掉多余空格、拼接字符串、使用正则表达式等等。如果只用 String 类提供的那些方法&#xff0c;我们需要手写大量的额外代码&#xff0c;不然容易出现各种异常。现在有…

GL Logger和CANFDLog-OTL-128两款记录仪都是如何实现高效的报文录制的?

GL Logger是Vector推出的记录CAN/CAN FD、LIN、FlexRay和MOST数据通信的工具。以GL2400为例带着大家一步步地实现路试过程中通过整车OBD口进行CAN/CANFD报文的录制。 Step1 设备配置 设备配置即设备录制方式、录制内容、设备休眠唤醒策略等。 ▷ 打开Vector Logger Configurat…

调试ad5245的总结

调试ad5245的总结 这个ad5245是通过IIC与FPGA进行通信的&#xff0c;首先要理解IIC协议。 经验总结&#xff1a; 1、SCL和SDA端的要有上拉电阻&#xff0c;且上拉电阻能正常工作&#xff1b; 2、要往SDA数据线上写三个字节才能调节ad5245的电阻值&#xff0c;第三个字节就是…

html中的flex是什么?——弹性布局

在HTML中&#xff0c;flex是一种布局方式&#xff0c;用于处理容器中的子元素的布局。它是CSS3的一部分&#xff0c;也被称为弹性布局。 通过使用flex布局&#xff0c;可以将容器中的子元素进行灵活的定位和扩展&#xff0c;以适应不同的屏幕尺寸和设备。它提供了一种简单而强…

flink 1.18 sql gateway /sql gateway jdbc

一 sql gateway 注意 之所以直接启动gateway 能知道yarn session 主要还是隐藏的配置文件&#xff0c;但是配置文件可以被覆盖&#xff0c;多个session 保留最新的applicationid 1 安装flink &#xff08;略&#xff09; 2 启动sql-gatway(sql-gateway 通过官网介绍只能运行…

【时光记:2023的心灵旅程】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…