0
3
3
3
博客/.../

TiDB 7.5 使用 HAProxy 统一数据库入口与 TiDB Server 切换测试记录

 拍脑袋小助手  发表于  2026-09-03
原创测试

前言

上一篇做 TiDB Server、PD、TiKV 节点故障测试时,发现应用入口还有一个问题,就是当测试程序直接连接:

192.168.56.101:4000

两台 TiDB Server 都正常时,这个地址可以使用。第一台 TiDB Server 停掉以后,第二台 192.168.56.102:4000 虽然还能提供数据库服务,原来建立在第一台 TiDB Server 上的连接不会自动切到第二台。

TiDB Server 是无状态计算层,可以部署多台,但 HIS、EMR、LIS、收费和药房系统不能在每次维护或停掉一台 TiDB Server 时都修改 JDBC 地址。所以这次在测试环境中增加一台 HAProxy,把两台 TiDB Server 放到同一个入口后面,应用只连接:

192.168.56.110:3390

后端连接到 192.168.56.101:4000 还是 192.168.56.102:4000,由 HAProxy 处理。TiDB v7.5 的文档也列出了支持 LVS、HAProxy、F5 等负载均衡方式,用于给多台 TiDB Server 提供统一访问入口。

一、为什么这次不用 TiProxy

TiProxy 是 TiDB 官方代理组件,支持连接迁移和服务发现。但是我这套测试环境使用的是 TiDB v7.5.7,官方 Release Notes:TiProxy 在 v7.6.0 作为实验特性引入,到 v8.0.0 才成为正式功能。为了保证测试与后期生产一致,所以不使用 TiProxy,采用 v7.5 官方架构文档中列出的 HAProxy。以后测试环境升级到 v8.x,再单独比较 TiProxy 和 HAProxy。

二、这次的连接关系

上一篇测试时,应用直接连接 TiDB01:

HIS测试程序
    |
    |
192.168.56.101:4000
    |
   TiDB

现在改成:

                   +--> TiDB01 192.168.56.101:4000
                   |
应用 --> HAProxy --+
       192.168.56.110:3390
                   |
                   +--> TiDB02 192.168.56.102:4000

测试环境保持和原有一样,也没有重新部署数据库集群,只增加了 HAProxy 节点。

组件 地址
HAProxy 192.168.56.110:3390
TiDB01 192.168.56.101:4000
TiDB02 192.168.56.102:4000
PD 192.168.56.101/102/103:2379
TiKV 192.168.56.101/102/103:20160
TiDB v7.5.7
测试库 his_source

HAProxy 单独放在 192.168.56.110,没有和 TiDB Server 安装在一起,便于单独观察代理层。配置前,先从 HAProxy 机器检查到两台 TiDB Server 的 TCP 4000 端口:

nc -vz 192.168.56.101 4000
nc -vz 192.168.56.102 4000

两个端口都能连通后再继续配置代理。

三、HAProxy 配置没有做得很复杂

这次只验证 TiDB Server 后端切换,所以使用了比较简单的 HAProxy 配置。

/etc/haproxy/haproxy.cfg

global
    log         127.0.0.1 local2
    daemon
    maxconn     4096

defaults
    log         global
    mode        tcp
    option      tcplog
    timeout connect 5s
    timeout client  1h
    timeout server  1h

listen tidb-cluster
    bind 0.0.0.0:3390
    mode tcp
    balance leastconn

    server tidb01 192.168.56.101:4000 check inter 2000 rise 2 fall 3
    server tidb02 192.168.56.102:4000 check inter 2000 rise 2 fall 3

TiDB 对外提供 MySQL TCP 协议,这里使用 mode tcp,没有做应用层转发。balance leastconn 会优先把新连接分配给当前连接数较少的 TiDB Server。TiDB 官方 HAProxy 最佳实践给出的示例也使用了 mode tcpbalance leastconn

后面的参数:

check inter 2000 rise 2 fall 3

表示 HAProxy 每 2 秒对 TiDB Server 做一次 TCP 健康检查。连续 3 次失败后把后端标记为不可用,恢复时连续成功 2 次再重新加入。检测时间还会受探测起点和网络连接状态影响,不会把它直接换算成“TiDB 停止后第 6 秒整完成切换”。

配置写好后,先检查语法:

haproxy -c -f /etc/haproxy/haproxy.cfg

确认没有错误后再启动:

systemctl enable haproxy
systemctl restart haproxy
systemctl status haproxy

然后检查监听端口:

ss -lntp | grep 3390

正常情况下,HAProxy 会监听:

0.0.0.0:3390

四、用 MySQL Client 验证入口

不改测试程序,只用 MySQL Client 检查 HAProxy 入口。以前的连接命令是:

mysql -h 192.168.56.101 \
      -P 4000 \
      -u his_app \
      -p his_source

现在改为:

mysql -h 192.168.56.110 \
      -P 3390 \
      -u his_app \
      -p his_source

连接后,先看会话实际落在哪台 TiDB Server:

SELECT
    @@hostname AS tidb_host,
    CONNECTION_ID() AS conn_id,
    DATABASE() AS current_db;

TiDB 7.5 中,hostname 是只读系统变量,返回当前 TiDB Server 的主机名;CONNECTION_ID() 返回当前连接 ID。下面是示例输出格式:

tidb_host : tidb01
conn_id   : 138247
current_db: his_source

再开几个客户端,仍然连接:

192.168.56.110:3390

再分别执行:

SELECT @@hostname, CONNECTION_ID();

正常情况下,新连接会被 HAProxy 分配到当前可用的 TiDB Server。leastconn 根据当时的连接数选择后端,不要求第一个连接一定到 tidb01、第二个一定到 tidb02。这一步只检查同一个 HAProxy 地址能否把新连接交给两台 TiDB Server。

五、再从集群侧确认连接到底在哪

客户端查询 @@hostname 已经比较直观。从数据库侧统一查看连接分布:

SELECT
    INSTANCE,
    ID,
    USER,
    HOST,
    DB,
    COMMAND,
    TIME,
    STATE
FROM information_schema.CLUSTER_PROCESSLIST
WHERE USER = 'his_app'
ORDER BY INSTANCE, ID;

CLUSTER_PROCESSLIST 可以看到整个 TiDB 集群的连接,比普通 PROCESSLIST 多出的 INSTANCE 字段表示会话所属的 TiDB Server。如果只执行:

SHOW PROCESSLIST;

看到的只是当前 TiDB Server 上的会话。加上 HAProxy 后,同一个应用的连接可能分散在多台 TiDB Server 上,排查会话时会使用集群视图。

六、换成一笔更接近医院业务的测试数据

这里没有再建 test_tableha_probe 之类的探针表,继续使用之前的门诊、收费和医嘱表。先通过 HAProxy 登录:

mysql -h 192.168.56.110 \
      -P 3390 \
      -u his_app \
      -p his_source

然后模拟一次门诊挂号:

INSERT INTO outpatient_visit
(
    visit_no,
    patient_no,
    dept_code,
    visit_type,
    visit_time,
    visit_status,
    update_time
)
VALUES
(
    'TESTMZ2609021476',
    'TESTP04261',
    'CARD',
    'OUTPATIENT',
    NOW(),
    'WAITING',
    NOW()
);

TESTMZ2609021476 是模拟门诊号,TESTP04261 是模拟患者编号,CARD 表示心内科测试科室编码。表里不放患者姓名、身份证号、电话和医保卡号等与本次技术验证无关的字段。插入后,用下面的 SQL 查询:

SELECT
    visit_no,
    patient_no,
    dept_code,
    visit_status,
    visit_time
FROM outpatient_visit
WHERE visit_no = 'TESTMZ2609021476';

后面的节点切换和收费操作继续使用这条模拟门诊记录。

七、停掉当前连接所在的 TiDB Server

先看客户端当前落在哪台 TiDB Server:

SELECT @@hostname, CONNECTION_ID();

以下假设当前连接位于 tidb01,对应 192.168.56.101:4000。从 TiUP 中控机执行:

tiup cluster stop tidb75-lab \
  -N 192.168.56.101:4000

这是人为停止 TiDB Server 服务,用来模拟一个后端不可用,不等同于服务器突然断电或交换机故障。TiUP 支持通过 -N 只停止指定节点。

原来建立在 tidb01 上的 MySQL 会话会断开。HAProxy 可以把新连接送到健康的 TiDB Server,但不会把已有的 MySQL TCP 会话从 tidb01 原样迁到 tidb02。较新版本的 TiProxy 支持计划内下线或重启时的连接迁移,但官方文档也明确说明,TiDB Server 意外下线时连接仍会断开,不能把两种情况混在一起。

八、应用地址没有变,再连一次就够了

等 HAProxy 把 tidb01 判断为不可用后,仍然使用原地址连接:

mysql -h 192.168.56.110 \
      -P 3390 \
      -u his_app \
      -p his_source

连接串没有变化。连接后再执行:

SELECT @@hostname, CONNECTION_ID();

预期的新连接会落到仍然健康的 tidb02,也就是 192.168.56.102:4000。应用始终使用 192.168.56.110:3390,后端 TiDB Server 的变化不需要同步修改应用配置。这里还要看应用连接池能否正常断线重连。如果连接池一直使用已经失效的 TCP 连接,HAProxy 无法替它重建连接。

九、TiDB01 停着的时候,再完成后续收费

重新连接后,继续处理刚才的模拟就诊,先查询门诊记录:

SELECT
    visit_no,
    patient_no,
    dept_code,
    visit_status
FROM outpatient_visit
WHERE visit_no = 'TESTMZ2609021476';

然后写入一笔门诊诊查费:

INSERT INTO charge_detail
(
    charge_no,
    visit_no,
    item_type,
    item_code,
    item_name,
    qty,
    unit_price,
    amount,
    charge_time,
    charge_status
)
VALUES
(
    'TESTSF260902318',
    'TESTMZ2609021476',
    'REG',
    'REG-FEE',
    '普通门诊诊查费',
    1,
    15.00,
    15.00,
    NOW(),
    'PAID'
);

写入后查询:

SELECT
    charge_no,
    visit_no,
    item_name,
    amount,
    charge_status,
    charge_time
FROM charge_detail
WHERE charge_no = 'TESTSF260902318';

一次模拟门诊和一次收费用于检查新连接是否能继续访问业务表。TiDB Server 不存储业务数据,业务数据保存在 TiKV 中。

十、已有事务中途断掉,要单独看

单独检查事务执行到一半时 TiDB Server 停止的情况。开始前,先恢复上一节停止的 TiDB01:

tiup cluster start tidb75-lab \
  -N 192.168.56.101:4000

等两台 TiDB Server 都重新进入 HAProxy 可用后端后再继续。如果 TiDB01 还停着,当前连接落在 TiDB02,再停 TiDB02 就没有可用的 SQL 节点,后面的重连测试无法进行。

事务测试不需要在收费表里制造半笔账,用前面的模拟就诊修改一次状态即可。先确认客户端当前连接的 TiDB Server:

SELECT @@hostname;

然后:

BEGIN;

UPDATE outpatient_visit
SET visit_status = 'IN_SERVICE',
    update_time = NOW()
WHERE visit_no = 'TESTMZ2609021476';

这里先不执行:

COMMIT;

然后从另一个终端停止当前会话所在的 TiDB Server,-N 后面的地址按 @@hostname 的实际结果填写。客户端连接中断后,再通过 HAProxy 登录:

mysql -h 192.168.56.110 \
      -P 3390 \
      -u his_app \
      -p his_source

查询:

SELECT
    visit_no,
    visit_status,
    update_time
FROM outpatient_visit
WHERE visit_no = 'TESTMZ2609021476';

TiDB 官方事务文档说明,客户端连接中止或关闭时,未提交事务会自动回滚。前面没有执行 COMMITvisit_status = 'IN_SERVICE' 不应作为已提交状态保留下来,用查询结果核对这一点。

十一、COMMIT 附近断连接,处理方法又不一样

检查提交边界附近的连接异常。假设程序已经发送:

COMMIT;

此时网络或 TiDB Server 出现异常,客户端没有收到明确的成功响应。应用不能直接按下面的判断处理:

连接断了 = 事务肯定没提交

数据库与客户端之间发生通信故障时,业务端不能只凭连接错误判断事务最终状态。这里使用收费业务号 TESTSF260902318,数据库中有相应的唯一约束:

UNIQUE KEY uk_charge_no (charge_no)

发生异常后,先按业务号查询:

SELECT
    charge_no,
    visit_no,
    amount,
    charge_status
FROM charge_detail
WHERE charge_no = 'TESTSF260902318';

查清原业务是否已经存在后,再决定补做还是继续后面的流程。这个问题和 HAProxy 没有直接关系,但断线重连时不能用“报错后再发一次 SQL”代替事务状态确认。收费、退费、发药和医嘱执行都要按业务唯一号核对。

十二、恢复刚才停止的 TiDB Server

检查完成后,恢复刚才停止的 TiDB Server。以下仍以当前会话位于 TiDB01 为例;如果实际停止的是 TiDB02,-N 后面的地址应改为 192.168.56.102:4000

tiup cluster start tidb75-lab \
  -N 192.168.56.101:4000

TiDB 默认的 MySQL 协议端口是 4000,状态服务端口是 10080。先检查状态接口:

curl http://192.168.56.101:10080/status

TiDB v7.5 的 /status 接口会返回版本和当前连接数等状态。服务恢复后,等待 HAProxy 重新把 tidb01 判定为可用,再新建几个连接:

mysql -h 192.168.56.110 \
      -P 3390 \
      -u his_app \
      -p his_source

分别执行:

SELECT @@hostname, CONNECTION_ID();

用查询结果检查后续新连接能否再次分布到两台 TiDB Server。原来建立在 tidb02 上的连接不会因为 tidb01 恢复而强制迁移,HAProxy 也不保证两台 TiDB Server 的连接数始终完全相同。

十三、医院应用切到 HAProxy以后,连接池也要测

MySQL Client 的检查完成后,还要用实际连接池验证 HIS 接入。医院应用常见的连接池有 HikariCP、Druid、DBCP、WebLogic DataSource 和 Tomcat JDBC Pool,也可能由应用服务器管理数据源。连接有效性检测、连接超时、失效连接剔除和事务异常后的连接回收都与具体连接池有关。测试时,让应用持续查询和写入,再停止一台 TiDB Server,记录已有连接的错误、失效连接多久被清理,以及新连接能否通过 HAProxy 重新建立。如果 HAProxy 已经可以建立新连接,应用仍长时间重复使用失效连接,再继续检查连接池配置。

十四、是否需要透传真实客户端 IP

加上 HAProxy 后,还要确认是否需要保留真实客户端 IP。应用直接连接 TiDB 时,SHOW PROCESSLIST 可以看到客户端地址;经过 HAProxy 后,TiDB 默认看到的连接来源可能变成 HAProxy 服务器 192.168.56.110。如果需要按客户端 IP 审计或排查问题,可以使用 TiDB v7.5 支持的 PROXY Protocol。

HAProxy 后端增加 send-proxy

send-proxy

例如:

server tidb01 192.168.56.101:4000 send-proxy check inter 2000 rise 2 fall 3
server tidb02 192.168.56.102:4000 send-proxy check inter 2000 rise 2 fall 3

TiDB 端同时配置 proxy-protocol.networks,只允许指定的代理服务器使用 PROXY Protocol。例如:

server_configs:
  tidb:
    proxy-protocol.networks: "192.168.56.110"

不要为了省事写成:

*

官方文档提示,* 允许任意地址自行报告客户端源 IP,存在安全风险。这次个人测试环境没有开启 PROXY Protocol,主要是先验证统一入口,客户端 IP 透传另行配置和检查。

十五、HAProxy 自己也是一个节点

应用入口从 192.168.56.101:4000 改为 192.168.56.110:3390 后,还要看 HAProxy 自身的可用性。目前只有一台 192.168.56.110,它仍然是单点。这次验证范围只有:

HAProxy 可以给两台 TiDB Server 提供统一地址,并在某个后端 TiDB Server 不可用后,把后续新连接分配到健康节点。

HAProxy 主机关机后,192.168.56.110:3390 仍然无法访问。医院正式环境如果已经有 F5、A10 或其他高可用负载均衡设备,可以复用现有入口;如果使用软件 HAProxy,还要继续处理 HAProxy 双机和统一 VIP。我没这个环境,所以就没测试这一部分。这次改造后,应用只使用 192.168.56.110:3390,两台 TiDB Server 的地址由 HAProxy 管理。后续增加 TiDB Server 或停机维护时,不需要逐个修改 HIS、EMR、LIS、PACS 和接口平台的数据库 IP。应用连接地址与 TiDB Server 节点地址分开后,入口配置由数据库侧统一维护。

结语

这次把测试应用的数据库地址统一改为 192.168.56.110:3390。一台 TiDB Server 不可用时,HAProxy 会停止向它分配新连接,应用地址不需要修改。已有连接仍会断开,应用连接池需要重新建连;提交边界附近的异常仍要按业务唯一号核对。当前只有一台 HAProxy,它本身也是单点,如果生产环境可以复用现有的高可用负载均衡设备,使用软件 HAProxy 时还要继续验证双机和 VIP。

0
3
3
3

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论