使用 Pglogical 的 PostGres 数据库复制

发布: (2026年2月25日 GMT+8 04:52)
4 分钟阅读
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the actual text of the post (the paragraphs, headings, etc.) in order to do so. Could you please paste the content you’d like translated? The source line will stay unchanged, and I’ll keep all formatting, code blocks, URLs, and technical terms intact while translating the surrounding text into Simplified Chinese.

前置条件

  • Docker Engine

    sudo systemctl start docker
    sudo systemctl enable docker   # optional – start at boot
  • 在 Debian 上的 Docker Desktop(通过 .deb 包安装)

    systemctl --user start docker-desktop

项目结构

//docker/

├── Dockerfile.pglogical
└── postgres/
    └── docker-compose.yml

docker-compose.yml

/docker/postgres/ 目录下创建文件 docker-compose.yml

services:
  pg1:
    build:
      context: ..
      dockerfile: Dockerfile.pglogical
    container_name: pg1
    command:
      - postgres
      - -c
      - shared_preload_libraries=pglogical
      - -c
      - wal_level=logical
      - -c
      - max_replication_slots=10
      - -c
      - max_wal_senders=10
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: testdb
      POSTGRES_HOST_AUTH_METHOD: trust
    ports:
      - "5433:5432"
    volumes:
      - pg1_data:/var/lib/postgresql/data
    networks:
      - pgnet

  pg2:
    build:
      context: ..
      dockerfile: DockerFile.pglogical
    container_name: pg2
    command:
      - postgres
      - -c
      - shared_preload_libraries=pglogical
      - -c
      - wal_level=logical
      - -c
      - max_replication_slots=10
      - -c
      - max_wal_senders=10
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: testdb
      POSTGRES_HOST_AUTH_METHOD: trust
    ports:
      - "5434:5432"
    volumes:
      - pg2_data:/var/lib/postgresql/data
    networks:
      - pgnet

networks:
  pgnet:
    driver: bridge

volumes:
  pg1_data:
  pg2_data:

Dockerfile.pglogical

因为基础 PostgreSQL 镜像不包含 pglogical 扩展,需要创建自定义 Dockerfile:

FROM postgres:16

RUN apt-get update \
 && apt-get install -y postgresql-18-pglogical \
 && rm -rf /var/lib/apt/lists/*

构建并启动容器

cd /docker/postgres
docker compose up -d

验证容器是否正在运行:

docker ps

您应该会看到类似如下的输出:

CONTAINER ID   IMAGE               COMMAND                  CREATED        STATUS        PORTS                                            NAMES
c61ef5d393cc   postgres-pg1       "docker-entrypoint.s…"   3 days ago     Up 4 minutes  0.0.0.0:5433->5432/tcp, [::]:5433->5432/tcp      pg1
b055528784d5   postgres-pg2       "docker-entrypoint.s…"   3 days ago     Up 4 minutes  0.0.0.0:5434->5432/tcp, [::]:5434->5432/tcp      pg2

在每个容器中安装并启用 pglogical

docker exec -it pg1 psql -U postgres -d testdb

psql 中:

CREATE EXTENSION pglogical;
\dx   -- 验证扩展已安装

你应该会看到类似以下的条目:

 List of installed extensions
  Name    | Version |   Schema   | Description
----------+---------+------------+--------------------------------
 pglogical| 2.4.6   | pglogical  | PostgreSQL Logical Replication
 plpgsql  | 1.0     | pg_catalog | PL/pgSQL procedural language

pg2 容器重复相同的步骤。

将数据库注册为 pglogical 节点

pg1 中运行以下语句:

SELECT pglogical.create_node(
    node_name := 'pg1',
    dsn := 'host=pg1 port=5432 dbname=testdb user=postgres password=postgres'
);

pg2 中运行以下语句:

SELECT pglogical.create_node(
    node_name := 'pg2',
    dsn := 'host=pg2 port=5432 dbname=testdb user=postgres password=postgres'
);

在 pg1 上创建复制集

SELECT pglogical.create_replication_set(
    'demo_set',
    replicate_insert := true,
    replicate_update := true,
    replicate_delete := true,
    replicate_truncate := true
);

创建表并插入基线行 (pg1)

CREATE TABLE demo_events (
    id SERIAL PRIMARY KEY,
    message TEXT,
    created_at TIMESTAMP DEFAULT now()
);

INSERT INTO demo_events (message)
SELECT 'baseline row ' || g
FROM generate_series(1,5) g;

将表添加到复制集 (pg1)

SELECT pglogical.replication_set_add_table(
    'demo_set',
    'demo_events'
);

在 pg2 上创建订阅

SELECT pglogical.create_subscription(
    subscription_name := 'sub_pg1',
    provider_dsn := 'host=pg1 port=5432 dbname=testdb user=postgres password=postgres',
    replication_sets := ARRAY['demo_set'],
    synchronize_structure := true,
    synchronize_data := true
);

稍等几秒,然后验证数据是否已复制:

SELECT * FROM demo_events;

您应该会看到在 pg1 上插入的五条基线记录。

测试写入方向 (pg1 → pg2)

Insert a new row on the primary (pg1):

INSERT INTO demo_events (message)
VALUES ('inserted new row');

该行会自动出现在 pg2 上。直接写入 pg2 将会触发错误,确认 pg2 是只读副本。

结论

您现在拥有两个使用 pglogical 扩展进行数据复制的 PostgreSQL 容器。对主库 (pg1) 所做的更改会自动传播到副本 (pg2),实现逻辑的、近实时的复制,无需物理流式传输。祝您使用同步的数据库愉快! 🎉

0 浏览
Back to Blog

相关文章

阅读更多 »

没人想负责的 Systemd Bug

TL;DR:存在一个命名空间 bug,影响 Ubuntu 20.04、22.04 和 24.04 服务器,导致随机服务故障。自 2021 年起已在系统中报告……