ProxySQL 3.0.8:从"猜"会话变量到"听"MySQL 亲自说

2026-05-17 17 预计阅读时间:1 分钟
来源:proxysql.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

ProxySQL 的连接复用(multiplexing)是其最核心的性能优势——多个前端会话可以共享同一组后端连接。但复用有一个前提:ProxySQL 必须精确知道每个会话当前的变量状态,否则把一个 sql_mode=TRADITIONAL 的会话路由到一个 sql_mode=DEFAULT 的后端连接,查询结果就会出问题。

3.0.8 之前,ProxySQL 只能靠"猜"——解析客户端发来的 SET 语句来推断变量值。3.0.8 之后,它多了一条路:直接读取 MySQL 后端在 OK 包里返回的 session-state change 通知,变量追踪从被动推断变成了主动确认。

旧方案的盲区

ProxySQL 原先追踪会话变量的方式是字符串匹配:扫描客户端 SQL,识别出 SET SESSION xxx = yyySET @@xxx = yyy 等模式,然后把变量名和值记到会话上下文中。

这个思路直白,但覆盖不了以下场景:

  • 隐式变量变更START TRANSACTION 会把 autocommit 置为 0,但这条 SQL 里没有 SET 关键字,解析器无从识别。
  • 存储过程/触发器内部改变量:客户端只调了 CALL do_something(),过程体里可能执行了 SET sql_mode = 'STRICT_TRANS_TABLES',ProxySQL 在前端看不到这条 SET。
  • COMMIT / ROLLBACK 后 autocommit 回弹:事务结束后 autocommit 自动恢复为 1,旧版 ProxySQL 不知道这件事,会话上下文里的值就过期了。

这些盲区的后果是:ProxySQL 误判会话状态,把"脏"连接分配给新请求,或者不敢复用连接而频繁创建新连接——前者是正确性风险,后者是性能损失。

OK 包里的隐藏通道

MySQL 协议的 OK 包(响应包类型 0x00)不只是说"执行成功"。从 MySQL 5.7 开始,服务端可以在 OK 包尾部附加 session-state change 信息,用 TLV(Type-Length-Value)格式编码,告诉客户端"刚才这条语句执行后,以下会话状态发生了变化"。

常见的 state-change type 包括:

Type 值 含义
0x0000 SESSION_TRACK_SYSTEM_VARIABLE — 系统变量变更
0x0001 SESSION_TRACK_SCHEMA — 默认数据库变更
0x0002 SESSION_TRACK_STATE_CHANGE — 任意状态变更标记
0x0003 SESSION_TRACK_GTIDS — GTID 变更
0x0004 SESSION_TRACK_TRANSACTION_CHARACTERISTICS — 事务特征
0x0005 SESSION_TRACK_TRANSACTION_STATE — 事务状态

MySQL 默认不开启这些通知。需要后端连接在连接初始化时执行:

SET SESSION session_track_system_variables = '*';

或者只追踪特定变量:

SET SESSION session_track_system_variables = 'autocommit,sql_mode,character_set_client';

ProxySQL 3.0.8 正是利用了这条通道:它在后端连接上开启 session_track_system_variables,然后解析每个 OK 包里的 state-change payload,把变量变更实时同步到对应的前端会话上下文。

实际效果对比

用一个具体场景说明差异。假设客户端执行了以下序列:

SET autocommit = 0;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

旧版 ProxySQL(3.0.7 及更早)的行为

  1. 解析 SET autocommit = 0 → 记录 autocommit=0
  2. UPDATE 不是 SET → 不更新变量
  3. COMMIT 不是 SET → 不更新变量 → 会话上下文仍为 autocommit=0

结果:COMMIT 之后,MySQL 已经把 autocommit 恢复为 1,但 ProxySQL 还以为它是 0。下一个请求如果需要 autocommit=1,ProxySQL 要么分配一个"脏"连接(变量不匹配),要么丢弃当前连接再建一个新连接。

新版 ProxySQL 3.0.8 的行为

  1. 解析 SET autocommit = 0 → 记录 autocommit=0(SET 解析仍然保留)
  2. UPDATE 的 OK 包不含变量变更 → 不更新
  3. COMMIT 的 OK 包携带 SESSION_TRACK_SYSTEM_VARIABLE: autocommit=1 → ProxySQL 更新会话上下文为 autocommit=1

结果:会话状态始终与 MySQL 实际状态一致,连接可以放心复用。

配置与验证

下面是一个可以直接跑的验证流程,用 ProxySQL 3.0.8 + MySQL 8.0 演示 session-variable tracking 的变化。

1. 准备 MySQL 后端

确保 MySQL 开启了 session-state tracking:

# 登录 MySQL
mysql -u root -p

# 检查当前配置
SHOW VARIABLES LIKE 'session_track_system_variables';

如果值为空字符串,需要设置它:

SET GLOBAL session_track_system_variables = '*';
-- 或者只追踪关键变量
SET GLOBAL session_track_system_variables = 'autocommit,sql_mode,tx_isolation';

注意:这是 GLOBAL 级别设置,新连接会继承。已有连接需要单独 SET SESSION

2. ProxySQL 配置

# 进入 ProxySQL 管理接口
mysql -u admin -padmin -h 127.0.0.1 -P 6032

# 添加 MySQL 后端(假设 MySQL 在 3306)
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight, status)
VALUES (0, '127.0.0.1', 3306, 1, 'ONLINE');

# 配置后端连接初始化脚本,确保每个连接开启 tracking
DELETE FROM mysql_variables WHERE variable_name='mysql_init_connect';
INSERT INTO mysql_variables (variable_name, variable_value)
VALUES ('mysql_init_connect', 'SET SESSION session_track_system_variables = "*"');

# 配置 MySQL 用户
INSERT INTO mysql_users (username, password, default_hostgroup)
VALUES ('app_user', 'app_pass', 0);

LOAD MYSQL SERVERS TO RUNTIME;
LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
SAVE MYSQL USERS TO DISK;

mysql_init_connect 是关键——ProxySQL 在建立每个后端连接时都会执行这段 SQL,确保 session tracking 已开启。

3. 验证追踪效果

通过 ProxySQL 的 6033 端口(MySQL 协议入口)执行操作,然后在管理端口观察会话状态:

# 客户端连接 ProxySQL
mysql -u app_user -papp_pass -h 127.0.0.1 -P 6033

# 执行事务
SET autocommit = 0;
START TRANSACTION;
SELECT @@autocommit;  -- 应返回 0
COMMIT;
SELECT @@autocommit;  -- 应返回 1(MySQL 自动恢复)

回到管理端口查看会话变量记录:

mysql -u admin -padmin -h 127.0.0.1 -P 6032

-- 查看活跃连接及其追踪到的变量
SELECT * FROM stats_mysql_connection_pool;

-- 查看最近处理的查询日志
SELECT * FROM stats_mysql_query_digest
WHERE digest_text LIKE '%autocommit%'
ORDER BY count_star DESC LIMIT 5;

如果追踪生效,COMMIT 之后 ProxySQL 内部记录的 autocommit 应为 1,该连接可以被下一个需要 autocommit=1 的会话直接复用,无需重置或重建。

4. 用 tcpdump 抓包确认 OK 包内容

想亲眼看到 OK 包里的 state-change 数据,可以抓包:

# 在 ProxySQL 与 MySQL 之间的通信抓包
sudo tcpdump -i lo -s 0 -w /tmp/proxysql.pcap port 3306

# 客户端执行 COMMIT 后停止抓包
sudo killall tcpdump

# 用 tshark 解析 MySQL 协议
tshark -r /tmp/proxysql.pcap \
  -Y "mysql.response_code == 0" \
  -V 2>/dev/null | grep -A5 "session_state"

如果配置正确,你会在 COMMIT 对应的 OK 包里看到 SESSION_TRACK_SYSTEM_VARIABLE 字段,值为 autocommit=1

仍需注意的边界

OK 包追踪不是万能的,有几个现实约束:

  • MySQL 5.6 及更早不支持session_track_system_variables 从 5.7 才引入。如果你的后端还有 5.6,ProxySQL 只能退回到 SET 解析模式。
  • 不是所有变量都追踪:即使设为 *,MySQL 也只追踪标记为 SESSION_TRACK_SYSTEM_VARIABLE 的系统变量。一些状态(如临时表的存在)不在追踪范围内,ProxySQL 仍需靠自身的规则判断连接是否"干净"。
  • 网络开销微增:OK 包多了 state-change payload,每个响应包体积略大。在高 QPS 场景下可以只追踪关键变量而非 *,减少编码开销:
SET SESSION session_track_system_variables = 'autocommit,sql_mode,tx_isolation';
  • SET 解析仍然保留:3.0.8 不是替换旧方案,而是叠加。显式 SET 语句的解析继续工作,OK 包追踪补充了隐式变更的覆盖。两条路径互为冗余,不会因为一条失效而丢状态。

上手建议

如果你已经在用 ProxySQL 3.0.x,升级到 3.0.8 的改动很小:

  1. 升级 ProxySQL 二进制,配置文件无需修改。
  2. 设置 mysql_init_connect,让后端连接开启 session_track_system_variables
  3. 观察 stats_mysql_connection_poolConnOKConnERR 的变化——追踪生效后,连接复用率应上升,新建连接数应下降。
  4. 逐步收紧追踪范围:先用 * 验证功能正常,再根据业务实际用到的变量缩小范围,减少协议开销。

对于还在 ProxySQL 2.x 的用户,3.0 系列的升级幅度较大(包括配置表结构变化),建议先在独立环境验证,再逐步迁移。但 session-variable tracking 这个特性本身值得迁移——它直接减少了"连接不敢复用"和"变量状态漂移"两类最常见的问题,是连接池从"基本可用"走向"生产可靠"的关键一步。


相关推荐