<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>读写分离 on heyaohua's Blog</title><link>https://blog.heyaohua.com/tags/%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB/</link><description>Recent content in 读写分离 on heyaohua's Blog</description><image><title>heyaohua's Blog</title><url>https://blog.heyaohua.com/og-image.png</url><link>https://blog.heyaohua.com/og-image.png</link></image><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Thu, 09 Oct 2025 12:15:00 +0800</lastBuildDate><atom:link href="https://blog.heyaohua.com/tags/%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB/index.xml" rel="self" type="application/rss+xml"/><item><title>MySQL→PostgreSQL 主从架构迁移方案（读写分离版）</title><link>https://blog.heyaohua.com/posts/2025/10/mysql-to-postgresql-migration-rw/</link><pubDate>Thu, 09 Oct 2025 12:15:00 +0800</pubDate><guid>https://blog.heyaohua.com/posts/2025/10/mysql-to-postgresql-migration-rw/</guid><description>目标：用 PostgreSQL 的 WAL + Streaming Replication 实现“写走主、读走从”，并提供生产可用的高可用与连接层方案，附配置模板与运维脚本示例。适配 PostgreSQL 16/17/18。</description><content:encoded><![CDATA[<blockquote>
<p>目标：用 PostgreSQL 的 WAL + Streaming Replication 实现“写走主、读走从”，并提供生产可用的高可用与连接层方案，附配置模板与运维脚本示例。适配 PostgreSQL 16/17/18。</p>
</blockquote>
<hr>
<h2 id="1-架构总览">1. 架构总览</h2>
<h3 id="11-基础拓扑最小可用">1.1 基础拓扑（最小可用）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>App(写) ─────────►  Primary(主)
</span></span><span style="display:flex;"><span>                ╲
</span></span><span style="display:flex;"><span>                 ╲ WAL Stream
</span></span><span style="display:flex;"><span>                  ╲
</span></span><span style="display:flex;"><span>App(读) ───────────► Standby1(从)
</span></span><span style="display:flex;"><span>                   ► Standby2(从)
</span></span></code></pre></div><ul>
<li>写请求：直连主库。</li>
<li>读请求：直连从库（或通过中间层，见 §4）。</li>
<li>主从：物理复制（Streaming Replication），异步或半同步可选。</li>
</ul>
<h3 id="12-生产级拓扑推荐">1.2 生产级拓扑（推荐）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>               +-------------------+
</span></span><span style="display:flex;"><span>               |    pgbouncer      |  连接池（减少连接抖动）
</span></span><span style="display:flex;"><span>               +-------------------+
</span></span><span style="display:flex;"><span>                        │
</span></span><span style="display:flex;"><span>                 +--------------+
</span></span><span style="display:flex;"><span>                 |   Pgpool-II  |  SQL解析级读写分离/健康检查/故障转移脚本
</span></span><span style="display:flex;"><span>                 +--------------+
</span></span><span style="display:flex;"><span>                     │     │
</span></span><span style="display:flex;"><span>                (Write)   (Read)
</span></span><span style="display:flex;"><span>                     │     │
</span></span><span style="display:flex;"><span>                   Primary  ──┬── Standby1
</span></span><span style="display:flex;"><span>                              └── Standby2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        +-------------------+
</span></span><span style="display:flex;"><span>        | Patroni + etcd    |  主从编排/自动故障切换/仲裁
</span></span><span style="display:flex;"><span>        +-------------------+
</span></span></code></pre></div><ul>
<li>可替换 Pgpool-II 为 HAProxy（协议层转发）+ 应用侧读写分离（双 DSN）。</li>
<li>可替换 Patroni 为 repmgr 或手工脚本（风险更高）。</li>
</ul>
<hr>
<h2 id="2-版本与参数基线">2. 版本与参数基线</h2>
<ul>
<li>推荐版本：PostgreSQL 17（当前成熟稳定）或 18（新项目/前瞻特性）。</li>
<li>最小参数：</li>
<li><code>wal_level = replica</code>（或 <code>logical</code> 若需逻辑复制）</li>
<li><code>max_wal_senders</code> ≥ 从库数 + 维护冗余（例如 10）</li>
<li><code>wal_keep_size</code>：按网络/故障时间留足（例如 512MB~2GB）</li>
<li><code>archive_mode = on</code> + <code>archive_command</code>（若做增量备份/回放）</li>
<li>半同步：<code>synchronous_commit = on</code> + <code>synchronous_standby_names = 'FIRST 1 (standby1, standby2)'</code></li>
</ul>
<hr>
<h2 id="3-数据层主从复制配置模板物理复制">3. 数据层：主从复制配置模板（物理复制）</h2>
<h3 id="31-主库-postgresqlconf">3.1 主库 <code>postgresql.conf</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># 基础
</span></span><span style="display:flex;"><span>listen_addresses = &#39;*&#39;
</span></span><span style="display:flex;"><span>port = 5432
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 复制/WAL
</span></span><span style="display:flex;"><span>wal_level = replica
</span></span><span style="display:flex;"><span>max_wal_senders = 10
</span></span><span style="display:flex;"><span>max_wal_size = 4GB
</span></span><span style="display:flex;"><span>min_wal_size = 1GB
</span></span><span style="display:flex;"><span>wal_keep_size = 1024MB
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 半同步（可选）
</span></span><span style="display:flex;"><span># synchronous_commit = on
</span></span><span style="display:flex;"><span># synchronous_standby_names = &#39;FIRST 1 (standby1, standby2)&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 性能观测（推荐开启）
</span></span><span style="display:flex;"><span>shared_preload_libraries = &#39;pg_stat_statements&#39;
</span></span><span style="display:flex;"><span>pg_stat_statements.max = 10000
</span></span><span style="display:flex;"><span>pg_stat_statements.track = all
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 归档（如采用 pgBackRest 则由其接管）
</span></span><span style="display:flex;"><span># archive_mode = on
</span></span><span style="display:flex;"><span># archive_command = &#39;test ! -f /var/backup/%f &amp;&amp; cp %p /var/backup/%f&#39;
</span></span></code></pre></div><h3 id="32-主库-pg_hbaconf">3.2 主库 <code>pg_hba.conf</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># 允许业务访问
</span></span><span style="display:flex;"><span>host    all             appuser         10.0.0.0/16           scram-sha-256
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 允许复制连接（repl 角色）
</span></span><span style="display:flex;"><span>host    replication     repl            10.0.0.0/16           scram-sha-256
</span></span></code></pre></div><h3 id="33-创建复制用户">3.3 创建复制用户</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">CREATE</span> <span style="color:#ff79c6">ROLE</span> repl <span style="color:#ff79c6">WITH</span> REPLICATION LOGIN PASSWORD <span style="color:#f1fa8c">&#39;REPLACE_WITH_REPL_PASSWORD&#39;</span>;
</span></span></code></pre></div><h3 id="34-从库基线拉起pg_basebackup">3.4 从库基线拉起（pg_basebackup）</h3>
<blockquote>
<p>在 Standby 节点执行：</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>pg_basebackup -h 10.0.0.10 -p 5432 -U repl \
</span></span><span style="display:flex;"><span>  -D /var/lib/postgresql/17/data -X stream -C -S standby1_slot -R -P
</span></span></code></pre></div><ul>
<li><code>-R</code>：自动写入 <code>standby.signal</code> 与 <code>primary_conninfo</code>。</li>
<li><code>-C -S standby1_slot</code>：自动创建复制槽，防止 WAL 丢失。</li>
<li>多个从库请使用不同 slot 名称：<code>standby2_slot</code> 等。</li>
</ul>
<h3 id="35-启动与验证">3.5 启动与验证</h3>
<ul>
<li>启动从库后，在主库查看：</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> pid, application_name, client_addr, <span style="color:#ff79c6">state</span>, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">FROM</span> pg_stat_replication;
</span></span></code></pre></div><ul>
<li>从库延迟：</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> now() <span style="color:#ff79c6">-</span> pg_last_xact_replay_timestamp() <span style="color:#ff79c6">AS</span> standby_delay;
</span></span></code></pre></div><h3 id="36-延迟从库防误删回滚保护可选">3.6 延迟从库（防误删回滚保护，可选）</h3>
<p>从库 <code>postgresql.auto.conf</code> 增加：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>recovery_min_apply_delay = &#39;5min&#39;
</span></span></code></pre></div><hr>
<h2 id="4-连接层读写分离方案">4. 连接层：读写分离方案</h2>
<h3 id="41-pgpool-iisql-解析级自动区分读写">4.1 Pgpool-II（SQL 解析级，自动区分读/写）</h3>
<h4 id="核心优点">核心优点</h4>
<ul>
<li>会解析 SQL 并将 SELECT 分发到从库，将 写语句 路由到主库；</li>
<li>内置健康检查、自动跟随主库（<code>follow_primary_command</code>），可脚本化故障切换；</li>
<li>与 pgbouncer 叠加实现“连接池 + 读写分离”。</li>
</ul>
<h4 id="411-pgpoolconf-样例核心片段">4.1.1 <code>pgpool.conf</code> 样例（核心片段）</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># 监听
</span></span><span style="display:flex;"><span>listen_addresses = &#39;0.0.0.0&#39;
</span></span><span style="display:flex;"><span>port = 9999
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 后端节点（node_id 从 0 开始）
</span></span><span style="display:flex;"><span>backend_hostname0 = &#39;10.0.0.10&#39;   # primary
</span></span><span style="display:flex;"><span>backend_port0     = 5432
</span></span><span style="display:flex;"><span>backend_weight0   = 1
</span></span><span style="display:flex;"><span>backend_flag0     = &#39;ALWAYS_PRIMARY&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>backend_hostname1 = &#39;10.0.0.11&#39;   # standby1
</span></span><span style="display:flex;"><span>backend_port1     = 5432
</span></span><span style="display:flex;"><span>backend_weight1   = 1
</span></span><span style="display:flex;"><span>backend_flag1     = &#39;ALLOW_TO_FAILOVER&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>backend_hostname2 = &#39;10.0.0.12&#39;   # standby2
</span></span><span style="display:flex;"><span>backend_port2     = 5432
</span></span><span style="display:flex;"><span>backend_weight2   = 1
</span></span><span style="display:flex;"><span>backend_flag2     = &#39;ALLOW_TO_FAILOVER&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 读写分离与负载
</span></span><span style="display:flex;"><span>load_balance_mode = on                 # 允许 SELECT 负载到从库
</span></span><span style="display:flex;"><span>statement_level_load_balance = on      # 语句级平衡（谨慎与事务特性）
</span></span><span style="display:flex;"><span>replication_mode = off                 # 非多主
</span></span><span style="display:flex;"><span>master_slave_mode = on
</span></span><span style="display:flex;"><span>master_slave_sub_mode = &#39;stream&#39;       # Streaming Replication
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 健康检查
</span></span><span style="display:flex;"><span>health_check_period = 5
</span></span><span style="display:flex;"><span>health_check_timeout = 3
</span></span><span style="display:flex;"><span>health_check_user = &#39;pgpool&#39;           # 仅用于健康检查的低权账号
</span></span><span style="display:flex;"><span>health_check_password = &#39;***&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 主从跟随（当主切换时，更新路由）
</span></span><span style="display:flex;"><span>follow_primary_command = &#39;/usr/local/bin/pgpool-follow-primary %d %h %p %D %m %H %M %P %r %R&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 会话保持与函数黑名单（避免副作用语句被当作读）
</span></span><span style="display:flex;"><span>black_function_list = &#39;nextval,setval&#39;
</span></span><span style="display:flex;"><span>white_function_list = &#39;&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># 日志
</span></span><span style="display:flex;"><span>log_per_node_statement = off
</span></span><span style="display:flex;"><span>log_statement = off
</span></span></code></pre></div><h4 id="412-pool_hbaconf">4.1.2 <code>pool_hba.conf</code></h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>host    all     all     10.0.0.0/16     scram-sha-256
</span></span></code></pre></div><h4 id="413-pcpconf">4.1.3 <code>pcp.conf</code></h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>admin:SCRAM-SHA-256$&lt;hash&gt;
</span></span></code></pre></div><h4 id="414-典型运维脚本占位">4.1.4 典型运维脚本占位</h4>
<ul>
<li><code>pgpool-follow-primary</code>：当检测到新主库后，自动调整后端主从角色并重载。</li>
<li><code>failover_streaming</code>：在主库失效时，触发预设的 Promote（若未用 Patroni）。</li>
</ul>
<blockquote>
<p>注：Pgpool-II 与 prepared statements / 长事务 叠加时需评估；对强一致读，可将关键读强制走主（应用层或 <code>app_name</code> 路由）。</p>
</blockquote>
<h3 id="42-haproxy--pgbouncer轻量级">4.2 HAProxy + pgbouncer（轻量级）</h3>
<ul>
<li>策略：对应用提供两个 DSN：</li>
<li>写：<code>haproxy-write:5432</code>（仅指向主库或 Patroni 的主 VIP）</li>
<li>读：<code>haproxy-read:5432</code>（轮询多个从库）</li>
<li>HAProxy 核心示例：</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>frontend pg_write
</span></span><span style="display:flex;"><span>  bind *:5000
</span></span><span style="display:flex;"><span>  default_backend pg_primary
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>backend pg_primary
</span></span><span style="display:flex;"><span>  option tcp-check
</span></span><span style="display:flex;"><span>  server primary 10.0.0.10:5432 check
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>frontend pg_read
</span></span><span style="display:flex;"><span>  bind *:5001
</span></span><span style="display:flex;"><span>  default_backend pg_standbys
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>backend pg_standbys
</span></span><span style="display:flex;"><span>  balance roundrobin
</span></span><span style="display:flex;"><span>  option tcp-check
</span></span><span style="display:flex;"><span>  server s1 10.0.0.11:5432 check
</span></span><span style="display:flex;"><span>  server s2 10.0.0.12:5432 check
</span></span></code></pre></div><ul>
<li>pgbouncer：建议 transaction 池化模式，注意与 <code>server_reset_query</code> 配置，避免事务泄漏。</li>
</ul>
<hr>
<h2 id="5-高可用patroni--etcd推荐">5. 高可用：Patroni + etcd（推荐）</h2>
<h3 id="51-部署要点">5.1 部署要点</h3>
<ul>
<li>各节点运行 Patroni，使用 etcd（或 Consul）存储集群状态；</li>
<li>Patroni 负责：</li>
<li>主从编排、健康检查、自动 promote；</li>
<li>维护 <code>postgresql.conf</code> 与复制参数；</li>
<li>提供 REST API（供 HAProxy/keepalived 识别主）。</li>
</ul>
<h3 id="52-patroni-样例配置etcpatronipg01yml">5.2 Patroni 样例配置（<code>/etc/patroni/pg01.yml</code>）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>scope: pg-cluster
</span></span><span style="display:flex;"><span>name: pg01
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>restapi:
</span></span><span style="display:flex;"><span>  listen: 0.0.0.0:8008
</span></span><span style="display:flex;"><span>  connect_address: 10.0.0.10:8008
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>etcd:
</span></span><span style="display:flex;"><span>  hosts: 10.0.0.20:2379,10.0.0.21:2379,10.0.0.22:2379
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>postgresql:
</span></span><span style="display:flex;"><span>  listen: 0.0.0.0:5432
</span></span><span style="display:flex;"><span>  connect_address: 10.0.0.10:5432
</span></span><span style="display:flex;"><span>  data_dir: /var/lib/postgresql/17/data
</span></span><span style="display:flex;"><span>  bin_dir: /usr/lib/postgresql/17/bin
</span></span><span style="display:flex;"><span>  parameters:
</span></span><span style="display:flex;"><span>    wal_level: replica
</span></span><span style="display:flex;"><span>    max_wal_senders: 10
</span></span><span style="display:flex;"><span>    wal_keep_size: 1024MB
</span></span><span style="display:flex;"><span>    shared_preload_libraries: pg_stat_statements
</span></span><span style="display:flex;"><span>  authentication:
</span></span><span style="display:flex;"><span>    replication:
</span></span><span style="display:flex;"><span>      username: repl
</span></span><span style="display:flex;"><span>      password: REPLACE_WITH_REPL_PASSWORD
</span></span><span style="display:flex;"><span>    superuser:
</span></span><span style="display:flex;"><span>      username: postgres
</span></span><span style="display:flex;"><span>      password: REPLACE_WITH_SUPERUSER_PASSWORD
</span></span><span style="display:flex;"><span>  pg_hba:
</span></span><span style="display:flex;"><span>  - host all all 0.0.0.0/0 scram-sha-256
</span></span><span style="display:flex;"><span>  - host replication repl 0.0.0.0/0 scram-sha-256
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>synchronous_mode: false   # 如需半同步置 true，并配置 standby 优先级
</span></span></code></pre></div><ul>
<li>其他节点将 <code>name</code> 与 <code>connect_address</code> 对应修改即可。</li>
</ul>
<h3 id="53-与-haproxy-集成识别主">5.3 与 HAProxy 集成（识别主）</h3>
<p>HAProxy 可基于 Patroni REST 的 <code>/master</code> endpoint 进行后端切换，或以 tag 端口代理当前主。</p>
<hr>
<h2 id="6-运维脚本与常见操作">6. 运维脚本与常见操作</h2>
<h3 id="61-手工主从切换无-patroni-场景">6.1 手工主从切换（无 Patroni 场景）</h3>
<ol>
<li>在待升主的从库执行 Promote：</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>pg_ctl -D /var/lib/postgresql/17/data promote
</span></span></code></pre></div><ol>
<li>调整应用/中间件连接到新主。</li>
<li>原主修复后以从库身份重拉：</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>pg_ctl -D /var/lib/postgresql/17/data stop
</span></span><span style="display:flex;"><span>rm -rf /var/lib/postgresql/17/data/*
</span></span><span style="display:flex;"><span>pg_basebackup -h NEW_PRIMARY -U repl -D /var/lib/postgresql/17/data -X stream -R -P -C -S oldprimary_slot
</span></span><span style="display:flex;"><span>pg_ctl -D /var/lib/postgresql/17/data start
</span></span></code></pre></div><h3 id="62-创建查看复制槽">6.2 创建/查看复制槽</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#6272a4">-- 创建逻辑槽（如需逻辑复制）
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> <span style="color:#ff79c6">*</span> <span style="color:#ff79c6">FROM</span> pg_create_logical_replication_slot(<span style="color:#f1fa8c">&#39;app_slot&#39;</span>, <span style="color:#f1fa8c">&#39;pgoutput&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">-- 查看复制槽
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> slot_name, slot_type, active, restart_lsn <span style="color:#ff79c6">FROM</span> pg_replication_slots;
</span></span></code></pre></div><h3 id="63-只读事务强一致读在主库">6.3 只读事务（强一致读在主库）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">BEGIN</span> <span style="color:#ff79c6">READ</span> <span style="color:#ff79c6">ONLY</span>;  <span style="color:#6272a4">-- 强制在主库读（通过路由或角色策略）
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> ...;
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">COMMIT</span>;
</span></span></code></pre></div><hr>
<h2 id="7-监控与告警">7. 监控与告警</h2>
<h3 id="71-关键视图">7.1 关键视图</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#6272a4">-- 主库查看复制
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> <span style="color:#ff79c6">*</span> <span style="color:#ff79c6">FROM</span> pg_stat_replication;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">-- WAL 生成量
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> date_trunc(<span style="color:#f1fa8c">&#39;hour&#39;</span>, now()) <span style="color:#ff79c6">AS</span> ts,
</span></span><span style="display:flex;"><span>       pg_wal_lsn_diff(pg_current_wal_lsn(), <span style="color:#f1fa8c">&#39;0/0&#39;</span>)<span style="color:#ff79c6">/</span><span style="color:#bd93f9">1024</span><span style="color:#ff79c6">/</span><span style="color:#bd93f9">1024</span><span style="color:#ff79c6">/</span><span style="color:#bd93f9">1024</span> <span style="color:#ff79c6">AS</span> wal_gb;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">-- 从库延迟
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> now() <span style="color:#ff79c6">-</span> pg_last_xact_replay_timestamp() <span style="color:#ff79c6">AS</span> standby_delay;
</span></span></code></pre></div><h3 id="72-prometheus-exporter建议">7.2 Prometheus Exporter（建议）</h3>
<ul>
<li><code>postgres_exporter</code>：抓取一般指标</li>
<li><code>pgbouncer_exporter</code>、<code>pgpool2_exporter</code>（若使用）</li>
<li>告警建议：</li>
<li><code>standby_delay &gt; 5s/30s/60s</code> 分级；</li>
<li><code>replication_state != streaming</code></li>
<li><code>replication_slot_inactive</code> &amp; WAL 堆积</li>
</ul>
<hr>
<h2 id="8-备份与恢复">8. 备份与恢复</h2>
<h3 id="81-工具选型">8.1 工具选型</h3>
<ul>
<li>pgBackRest（推荐）：全量/增量/差异、并行、校验、保留策略、S3 对接；</li>
<li><code>pg_basebackup</code> + WAL 归档：适合轻量场景。</li>
</ul>
<h3 id="82-pgbackrest-最小配置示例片段">8.2 pgBackRest 最小配置示例（片段）</h3>
<p><code>/etc/pgbackrest/pgbackrest.conf</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[global]
</span></span><span style="display:flex;"><span>repo<span style="color:#bd93f9">1</span>-path=/var/lib/pgbackrest
</span></span><span style="display:flex;"><span>repo<span style="color:#bd93f9">1</span>-retention-full=<span style="color:#bd93f9">7</span>
</span></span><span style="display:flex;"><span>start-fast=y
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[pg]
</span></span><span style="display:flex;"><span>pg<span style="color:#bd93f9">1</span>-path=/var/lib/postgresql/<span style="color:#bd93f9">17</span>/data
</span></span><span style="display:flex;"><span>pg<span style="color:#bd93f9">1</span>-port=<span style="color:#bd93f9">5432</span>
</span></span></code></pre></div><ul>
<li>调度：<code>pgbackrest backup --type=full|diff|incr</code></li>
<li>恢复：<code>pgbackrest restore --type=time --target='2025-10-08 12:00:00'</code></li>
</ul>
<hr>
<h2 id="9-逻辑复制可选按表级跨版本">9. 逻辑复制（可选：按表级/跨版本）</h2>
<h3 id="91-主库发布publication">9.1 主库发布（publication）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">CREATE</span> PUBLICATION mypub <span style="color:#ff79c6">FOR</span> <span style="color:#ff79c6">TABLE</span> <span style="color:#ff79c6">public</span>.users, <span style="color:#ff79c6">public</span>.orders;
</span></span></code></pre></div><h3 id="92-从库订阅subscription">9.2 从库订阅（subscription）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">CREATE</span> SUBSCRIPTION mysub
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">CONNECTION</span> <span style="color:#f1fa8c">&#39;host=10.0.0.10 dbname=app user=repl password=REPLACE_WITH_REPL_PASSWORD&#39;</span>
</span></span><span style="display:flex;"><span>  PUBLICATION mypub;
</span></span></code></pre></div><blockquote>
<p>适用：选择性同步、跨版本/异构聚合、CDC 下游（Kafka/ETL）。</p>
</blockquote>
<hr>
<h2 id="10-应用侧改造要点从-mysql-迁移">10. 应用侧改造要点（从 MySQL 迁移）</h2>
<ul>
<li>自增主键：改为 <code>GENERATED BY DEFAULT AS IDENTITY</code>；</li>
<li>UPSERT：<code>INSERT ... ON CONFLICT (key) DO UPDATE</code>；</li>
<li>读写分离：强一致读上主（关键交易/下单等），弱一致读走从；</li>
<li>事务隔离：PostgreSQL 默认 <code>READ COMMITTED</code>，必要时用 <code>REPEATABLE READ</code>；</li>
<li>SQL 差异：<code>LIMIT/OFFSET</code>、<code>BOOLEAN</code>、<code>text/varchar</code>、<code>ILIKE</code> 等；</li>
<li>连接池：强烈建议使用 pgbouncer（transaction 模式）。</li>
</ul>
<hr>
<h2 id="11-交付清单可直接使用改造">11. 交付清单（可直接使用/改造）</h2>
<ul>
<li>数据层：<code>postgresql.conf</code>、<code>pg_hba.conf</code> 基线模板（§3）</li>
<li>初始化：<code>pg_basebackup</code> 命令（§3.4）</li>
<li>验证脚本：<code>pg_stat_replication</code>/延迟检查 SQL（§3.5、§7）</li>
<li>中间层：Pgpool-II 样例（§4.1）或 HAProxy 样例（§4.2）</li>
<li>高可用：Patroni 示例（§5.2）</li>
<li>故障切换：Promote/重拉流程（§6.1）</li>
<li>备份：pgBackRest 样例（§8.2）</li>
</ul>
<hr>
<h2 id="12-风险与最佳实践">12. 风险与最佳实践</h2>
<ul>
<li>WAL 保留不足：务必使用复制槽或足够的 <code>wal_keep_size</code>，防止从库追不上；</li>
<li>读写错路由：关键读务必走主；Pgpool 解析有边界，建议灰度与压测；</li>
<li>长事务：阻塞 VACUUM，导致膨胀与复制延迟；监控 <code>pg_stat_activity</code>；</li>
<li>半同步权衡：开启会增加写延迟；金融交易强一致可 <code>FIRST 1</code> 策略；</li>
<li>备份演练：每季度至少一次异地恢复演练；</li>
<li>版本升级：用 <code>pg_upgrade --check</code> 预检测，读 release notes。</li>
</ul>
<hr>
<h2 id="13-附docker-compose演示用">13. 附：Docker Compose（演示用）</h2>
<blockquote>
<p>仅供演示，生产请改为有状态存储 + 独立网络与安全策略。</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#ff79c6">version</span>: <span style="color:#f1fa8c">&#39;3.8&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">primary</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">image</span>: postgres:17
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">POSTGRES_PASSWORD</span>: REPLACE_WITH_SUPERUSER_PASSWORD
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f1fa8c">&#39;5432:5432&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">volumes</span>:
</span></span><span style="display:flex;"><span>      - primary_data:/var/lib/postgresql/data
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">standby1</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">image</span>: postgres:17
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">depends_on</span>:
</span></span><span style="display:flex;"><span>      - primary
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">POSTGRES_PASSWORD</span>: REPLACE_WITH_SUPERUSER_PASSWORD
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">command</span>: &gt;<span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">      bash -lc &#34;pg_basebackup -h primary -U postgres -D /var/lib/postgresql/data -R -P &amp;&amp; docker-entrypoint.sh postgres&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">volumes</span>:
</span></span><span style="display:flex;"><span>      - standby1_data:/var/lib/postgresql/data
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">volumes</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">primary_data</span>: {}
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">standby1_data</span>: {}
</span></span></code></pre></div><hr>
<h3 id="结语">结语</h3>
<p>本方案覆盖“写主读从”的完整链路：复制 → 连接层 → 高可用 → 备份 → 监控。可按你现网规模裁剪。如需，我可以基于你的主机清单/网段/端口策略，生成一套可直接上线的配置包（含脚本与 Ansible 角色）。</p>
]]></content:encoded></item></channel></rss>