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 = yyy、SET @@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 及更早)的行为:
- 解析
SET autocommit = 0→ 记录autocommit=0 UPDATE不是 SET → 不更新变量COMMIT不是 SET → 不更新变量 → 会话上下文仍为autocommit=0
结果:COMMIT 之后,MySQL 已经把 autocommit 恢复为 1,但 ProxySQL 还以为它是 0。下一个请求如果需要 autocommit=1,ProxySQL 要么分配一个"脏"连接(变量不匹配),要么丢弃当前连接再建一个新连接。
新版 ProxySQL 3.0.8 的行为:
- 解析
SET autocommit = 0→ 记录autocommit=0(SET 解析仍然保留) UPDATE的 OK 包不含变量变更 → 不更新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 的改动很小:
- 升级 ProxySQL 二进制,配置文件无需修改。
- 设置
mysql_init_connect,让后端连接开启session_track_system_variables。 - 观察
stats_mysql_connection_pool中ConnOK和ConnERR的变化——追踪生效后,连接复用率应上升,新建连接数应下降。 - 逐步收紧追踪范围:先用
*验证功能正常,再根据业务实际用到的变量缩小范围,减少协议开销。
对于还在 ProxySQL 2.x 的用户,3.0 系列的升级幅度较大(包括配置表结构变化),建议先在独立环境验证,再逐步迁移。但 session-variable tracking 这个特性本身值得迁移——它直接减少了"连接不敢复用"和"变量状态漂移"两类最常见的问题,是连接池从"基本可用"走向"生产可靠"的关键一步。