haproxy支持postgresql伪双写


难度 中等

要达到双写,其实就是把备库收到的请求都转发或者路由到主库,这又分为两种方式:

1.在备库的TCP五元组上将数据转发到主库;(这种方式客户端连接方式无需更改,但需要侵入修改数据库内核)
2.在备库或者其他节点加一层代理,使用不同的TCP五元组,并将数据转发到主库;(这种方式需要修改客户端连接方式,增加客户端连接串)

由于不能侵入修改数据库内核,因而采用增加代理的方式,而代理的主要作用就是将收到的TCP数据原样转发给主库。

这就意味着代理需要能访问所有节点,并判断谁是主节点。

  • haproxy本身就是一个代理组件,支持代理。

  • 要实现主库判断,可以通过健康检查来实现。

健康检查又分为多种方式:

  • tcp检查(发送tcp请求检查,可以构造postgresql协议的数据,可读性差,对postgresql协议需要有一定的理解)
  • http检查(发送http请求,需要有http检查服务)
  • pgsql-check(haproxy提供的数据库检查,但只能发送startup消息,需要做二次开发)
  • 外部脚本(在外部写一个脚本用于判断,耦合性最小,实现最简单,但是延迟可能较高)

代理

haproxy本身就用于代理和故障切换或者负载均衡。只需要使用其基本功能就行。示例配置文件如下:

global
    maxconn 5000
    log stdout format raw local0

defaults
    mode tcp
    timeout connect 5s
    timeout client  1m
    timeout server  1m
    log global

frontend pg_front
    bind *:5000
    default_backend pg_primary

backend pg_primary
    mode tcp
    balance first
    option tcp-check

    server pg1 127.0.0.1:5433 check
    server pg2 127.0.0.1:5432 check backup

上面的haproxy配置对应如下内容:


1️⃣ 大模块结构

HAProxy 配置通常分为三个大块:

  1. global → 全局设置,影响整个 HAProxy 进程
  2. defaults → 默认行为设置,可以被 frontend/backend 继承
  3. frontend/backend → 实际网络流量处理和负载分发逻辑

你的配置大概是:

global
defaults
frontend pg_front
backend pg_primary
  • global:控制 HAProxy 本身运行的资源、日志、最大连接等
  • defaults:设置 frontend/backend 的默认 TCP 模式、超时和日志
  • frontend pg_front:监听客户端请求端口 5000,把请求送到 pg_primary 后端
  • backend pg_primary:定义 PostgreSQL 节点,健康检查、负载均衡策略、主备关系

2️⃣ global 模块

global
    maxconn 5000
    log stdout format raw local0
配置项 作用
maxconn 5000 HAProxy 最大同时处理的连接数是 5000,超过会排队
log stdout format raw local0 日志输出到 stdout(常用于容器),日志格式是 raw,local0 是 syslog facility

说明

  • 这里没有启用 daemonpidfile,通常在容器里会直接在前台运行
  • maxconn 控制全局并发能力,不影响单个 backend/server 配置

3️⃣ defaults 模块

defaults
    mode tcp
    timeout connect 5s
    timeout client  1m
    timeout server  1m
    log global
配置项 作用
mode tcp 默认使用 TCP 层转发(第 4 层),而不是 HTTP 层
timeout connect 5s 客户端连接 HAProxy 时,如果连接到后端超时 5 秒就断开
timeout client 1m 客户端空闲时间超过 1 分钟就断开
timeout server 1m HAProxy 与后端 server 之间空闲超过 1 分钟就断开
log global 继承 global 定义的日志设置

说明

  • mode tcp 是关键,因为 PostgreSQL 是 TCP 协议,不是 HTTP
  • 超时设置保证 HAProxy 不会无限等待,避免资源占用

4️⃣ frontend pg_front

frontend pg_front
    bind *:5000
    default_backend pg_primary
配置项 作用
frontend pg_front 定义一个入口(前端)处理客户端连接
bind *:5000 HAProxy 在本地 5000 端口监听所有网卡的 TCP 连接
default_backend pg_primary 所有流量默认发送到 pg_primary 后端

说明

  • 这里没有定义高级路由或 ACL,所以所有连接都走 pg_primary
  • 前端只是 TCP 隧道,把流量直接送到 backend

5️⃣ backend pg_primary

backend pg_primary
    mode tcp
    balance first
    option tcp-check

    server pg1 127.0.0.1:5433 check
    server pg2 127.0.0.1:5432 check backup

5.1 backend 基础配置

配置项 作用
mode tcp backend 使用 TCP 转发
balance first 负载均衡策略:总是选择第一个健康节点,只有它 DOWN 才用第二个节点
option tcp-check 启用 TCP 层健康检查(简单建立 TCP 连接,不发任何数据)

说明

  • balance first 是关键,因为 HAProxy 会永远把流量发送到 pg1(127.0.0.1:5433),除非它 DOWN
  • option tcp-check 让 HAProxy 自动检测 pg1/pg2 是否可以 TCP 连接

balance除了有first模式外,还有如下模式:

模式 适用 特点 场景
roundrobin TCP/HTTP 轮询分发 节点均衡、简单场景
leastconn TCP/HTTP 最少连接 长连接负载均衡
source TCP/HTTP 根据客户端 IP 哈希 会话固定分配
first TCP/HTTP 总是选择第一个可用 主备切换、数据库主备
static-rr TCP/HTTP 静态轮询 少用
random TCP/HTTP 随机 高并发、均匀分布
uri/hdr/cookie HTTP HTTP 内容哈希 HTTP 会话保持

5.2 server 定义

server 配置 解释
server pg1 127.0.0.1:5433 check 主库节点,启用健康检查 (check)
server pg2 127.0.0.1:5432 check backup 备库节点,健康检查启用,但 backup 表示只有主库 DOWN 才会被使用

重点:backup 行为

  • HAProxy backup server 机制:

    1. pg1 UP → 流量全部走 pg1
    2. pg1 DOWN → 流量切换到 pg2
  • 结合 balance first → 整体逻辑就是“一主一备”模式


6️⃣ 总结流量行为

假设客户端连接到 5000

  1. HAProxy pg_front 接受连接

  2. 默认送到 pg_primary backend

  3. backend 检查健康状态:

    • pg1 UP → 走 pg1:5433
    • pg1 DOWN → pg2 UP → 走 pg2:5432
    • pg2 DOWN → 连接失败
  4. TCP-check 确保节点可以建立连接

结果

  • 平时 → 所有流量走主库
  • 主库挂掉 → 自动切到备库
  • 备库正常,主库恢复 → 自动切回主库

7️⃣ 图解(文字版)

Client
  |
  | TCP:5000
  v
HAProxy Frontend pg_front
  |
  |--> Backend pg_primary
        |
        |--> pg1 127.0.0.1:5433 (主库, first)
        |
        |--> pg2 127.0.0.1:5432 (备库, backup)
  • balance first + backup → HAProxy 优先 pg1
  • TCP-check → 确保节点可以建立连接

总结:

  • global → HAProxy 全局资源和日志
  • defaults → 默认 TCP 模式、超时
  • frontend → 客户端入口,监听端口,流量指向 backend
  • backend → 定义节点、健康检查、负载策略
  • server + check + backup → 一主一备自动故障切换

健康检查


1️⃣ HAProxy 健康检查概念

HAProxy 的健康检查(Health Check / check)用于 判断后端 server 是否可用,主要作用:

  1. 保证流量只走健康节点
  2. 自动剔除宕机节点,避免请求失败
  3. 支持主备节点切换(backup)、负载均衡策略选择

健康检查核心点

  • 类型:TCP 层或 HTTP 层
  • 频率:检查间隔 (inter)
  • 阈值:标记 UP/DOWN 所需的连续成功/失败次数 (rise, fall)
  • 动作:标记节点 DOWN/UP,影响负载均衡

HAProxy 在 TCP/HTTP 或 external 脚本层面都有实现机制。


2️⃣ TCP 健康检查

原理

  • HAProxy 只在 TCP 层建立连接
  • 检查 server 端口是否可达
  • 不发送任何数据包,不关心服务内容

工作流程

  1. HAProxy 尝试 TCP 连接到 server 的 IP:port
  2. 连接成功 → 计数器增加
  3. 连接失败 → 计数器增加失败
  4. 达到 rise → server 标记 UP
  5. 达到 fall → server 标记 DOWN

配置示例

backend pg_primary
    mode tcp
    option tcp-check
    default-server inter 3s rise 2 fall 3
    server pg1 127.0.0.1:5433 check
    server pg2 127.0.0.1:5432 check backup
参数 说明
option tcp-check 启用 TCP 健康检查
inter 3s 每 3 秒执行一次检查
rise 2 连续 2 次成功才标记 UP
fall 3 连续 3 次失败才标记 DOWN
backup 备份节点,只在主节点 DOWN 时使用

特点

  • 简单,性能开销小
  • 只能判断端口可达性,无法判断业务逻辑

3️⃣ HTTP 健康检查

原理

  • HTTP 层发起请求(GET/HEAD/POST 等)

  • 根据返回状态码判断 server 健康:

    • 2xx / 3xx → 健康
    • 4xx / 5xx → 不健康
  • 支持检查特定 URI 或自定义头

工作流程

  1. HAProxy 发起 HTTP 请求到后端节点
  2. 根据返回码判断成功/失败
  3. 持续计数,根据 rise/fall 标记 UP/DOWN

配置示例

backend web_app
    mode http
    option httpchk GET /health
    default-server inter 5s rise 2 fall 3
    server s1 10.0.0.1:80 check
    server s2 10.0.0.2:80 check
参数 说明
option httpchk GET /health /health 发起 GET 请求作为健康检查
inter 5s 每 5 秒检查一次
rise/fall 连续成功/失败次数判定节点状态
check 表示开启健康检查

特点

  • 可以检查服务业务逻辑
  • 可配置返回码、HTTP 方法、请求头
  • 对微服务、HTTP API 很适用

4️⃣ External-check 健康检查

原理

  • external-check 允许 HAProxy 调用外部脚本判断节点健康

  • 脚本返回 exit code 控制节点状态:

    • 0 → UP
    • 0 → DOWN
  • 可以实现 复杂逻辑,如数据库主备检测、应用健康检查

工作流程

  1. HAProxy 调用 external-check command /path/to/script
  2. 脚本执行任意逻辑(TCP/HTTP/SQL/etc)
  3. 脚本通过 exit code 返回结果
  4. HAProxy 根据 rise/fall/inter 更新节点健康状态

配置示例

backend pg_primary
    mode tcp
    option external-check
    external-check command /etc/haproxy/check_pg_master.sh
    default-server inter 3s rise 2 fall 3
    server pg1 127.0.0.1:5433 check
    server pg2 127.0.0.1:5432 check backup
  • check_pg_master.sh 脚本示例:
#!/bin/bash
HOST="${HAPROXY_SERVER_ADDR}"
PORT="${HAPROXY_SERVER_PORT}"
USER="monitor"
PASS="password"

STATUS=$(PGPASSWORD="$PASS" psql -qtAX -h "$HOST" -p "$PORT" -U "$USER" -d postgres \
        -c "SELECT pg_is_in_recovery();" 2>/dev/null)

if [[ "$STATUS" == "f" ]]; then
    exit 0   # 主库 UP
else
    exit 1   # 备库 DOWN
fi

特点

  • 灵活,可以检测数据库、应用、业务逻辑
  • 支持复杂条件判断(如一主多备)
  • 可通过环境变量获取 HAProxy server IP/端口

5️⃣ 健康检查参数汇总

参数 作用
check 启用健康检查
inter 检查间隔
rise 连续成功次数判定 UP
fall 连续失败次数判定 DOWN
option tcp-check TCP 健康检查
option httpchk HTTP 健康检查
option external-check 外部脚本健康检查
backup 备份节点,主节点 DOWN 才使用
port 覆盖 server 健康检查端口(可选)

6️⃣ 健康检查流程总结

TCP/HTTP 流程

HAProxy ---> TCP/HTTP connect/request ---> server
       <--- TCP ACK / HTTP 2xx
         |
     判断成功/失败
         |
      rise/fall计数
         |
      更新server状态 UP/DOWN

External-check 流程

HAProxy ---> execute script ---> 脚本逻辑
       <--- exit code (0=UP, !=0=DOWN)
         |
     rise/fall计数
         |
      更新server状态 UP/DOWN

7️⃣ 使用场景对比

健康检查类型 场景 优缺点
TCP 数据库、Redis、简单服务 轻量、只检测端口连通性
HTTP Web 服务、API 可检测业务逻辑,支持返回码、请求头
external-check 数据库主备、复杂逻辑 最灵活,可执行 SQL、命令或任意逻辑

总结

  • HAProxy 健康检查核心目标是 保证流量只走健康节点
  • TCP → 端口可达性
  • HTTP → 业务逻辑健康
  • external-check → 自定义脚本逻辑(最灵活)
  • rise/fall/inter + backup → 控制切换、主备流量分发

对于我们需要数据库双写来说,其实就是所有走到非主数据库的流量都要转到主库。
在健康检查里就是,只有主库才是up,备库都是down。

使用extern-check的配置示例如下:

global
    log stdout format raw local0
    maxconn 5000
    external-check  # 启用 external-check

defaults
    mode tcp
    timeout connect 5s
    timeout client  30s
    timeout server  30s

listen pg_write
    bind *:5000
    mode tcp
    option external-check
    external-check command /etc/haproxy/check_pg_master.sh

    # 定义默认 server 健康检查策略
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions

    # PostgreSQL 节点
    server pg01 127.0.0.1:5432 check
    server pg02 127.0.0.1:5433 check

check_pg_master.sh的内容如下:

#!/bin/bash
# 检查当前节点是否为 PostgreSQL 主库
# 返回:
# 0 -> 主库 (UP)
# 1 -> 备库 (DOWN)

# 支持两种方式获取 server 地址:
# HAProxy >=2.4 可用参数传递 %s %p
HOST="${HAPROXY_SERVER_ADDR:-$1}"
PORT="${HAPROXY_SERVER_PORT:-$2}"
USER="repl"
PASS=""
export LD_LIBRARY_PATH=/var/postgres/lib

# 查询 PostgreSQL 节点角色
STATUS=$(PGPASSWORD="$PASS" /var/postgres/bin/psql -qtAX -h "$HOST" -p "$PORT" -U "$USER" -dpostgres \
        -c "SELECT pg_is_in_recovery();" 2>/tmp/connect.log)

echo "result is $?"
if [[ "$STATUS" == "f" ]]; then
    echo " - $HOST:$PORT is PRIMARY"
    exit 0   # 主库 -> HAProxy UP
else
    echo " - $HOST:$PORT is REPLICA"
    exit 1   # 备库 -> HAProxy DOWN
fi

文章作者: growdu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 growdu !
  目录
分类导航
随笔2 AI27 算法1 计算机基础13 博客搭建7 ChatGPT2 集群63 计算机通信1 数据库34 数据库深入80 DPDK26 Docker11 Elasticsearch4 编辑工具4 FAQ1 Go Web1 hometown2 编程语言16 网络9 OPC1 Linux38 openGauss4 页面12 PostgreSQL54 程序员自我修养1 协议11 成长之路1 stock1 存储5 工具20 VPP18 视频作品1 Vue13 Web1 代码示例11 数据库15 BenchmarkSQL1 PostgreSQL 源码修炼之路14
最热文章
1
13 逻辑复制深入
数据库深入🔥 1570
2
0 Postgresql存储、索引及系统优化、主备切换
PostgreSQL🔥 1495
3
一文读懂openguass dcf网络模块
集群🔥 1420
4
逻辑复制源码分析
数据库深入🔥 1327
5
PostgreSQL 分区表:从一行 `PARTITION BY` 到路由热路径的全链路拆解
数据库🔥 1094
6
applyparallelworker.c 之 LA 端源码深度解析:Leader Apply Worker 的指挥中枢
数据库深入🔥 1082
7
PostgreSQL Background Worker 全解:从 `RegisterBackgroundWorker` 到逻辑复制 4 类 worker 的全生命周期
数据库🔥 1078
8
PostgreSQL的后台进程walsender分析 - 关系型数据库 - 亿速云
PostgreSQL🔥 1033
9
PostgreSQL 逻辑复制的监控:六张视图 + 一组可执行 SQL,把 publisher/subscriber 的速率与健康度彻底看透
数据库🔥 1032
10
PostgreSQL 逻辑复制支持 DDL 之后:DDL 与 DML 的时序难题(重点:分区表)
数据库🔥 999
11
reorderbuffer.c 源码深度解析:PostgreSQL 逻辑复制的"事务重组引擎
数据库深入🔥 953
12
PostgreSQL 内核开发:读取一张表的 9 步标准流程与缓存全景
数据库🔥 938
13
从 `postgres` 二进制到生产级守护 —— PostgreSQL 最外层模块与启动全流程拆解
数据库🔥 936
14
支持逻辑复制同步 DDL 适配 SQL Server 方案
数据库深入🔥 934
15
PostgreSQL 逻辑复制的 ReorderBuffer 与事务机制:从一行 WAL 到一致性变更流的全链路绑定
数据库🔥 913
16
DDL同步架构(美化版)
数据库深入🔥 908
17
PostgreSQL Latch 机制详解:从一行 SetLatch 到 epoll 的内核之旅
数据库🔥 871
18
pgbench 源码全解:一个 C 文件如何撑起 PostgreSQL 官方压测工具
数据库🔥 860
19
PostgreSQL libpq 机制与缓冲区详解
数据库🔥 850
20
PostgreSQL 逻辑复制 spill 文件深度剖析:从 `xid-*.spill` 到 TPC-C 的增长方程
数据库🔥 845