1. MySQL Proxy测试报告:从原理到实战的全方位解析
上周刚完成公司核心系统的MySQL Proxy压力测试,趁着记忆新鲜把完整测试过程和关键结论整理出来。这份报告不仅包含常规性能指标,更会深入分析Proxy在不同业务场景下的表现差异,以及我们趟过的那些坑。如果你正在评估或使用MySQL Proxy,这篇实测数据或许能帮你少走弯路。
MySQL Proxy作为轻量级中间件,本质上是在客户端和MySQL服务器之间建立了一个透明代理层。它通过Lua脚本实现查询拦截、改写、负载均衡等核心功能,特别适合需要灵活控制数据库流量的场景。我们这次测试主要针对Proxy在高并发写入、复杂查询路由和故障转移三个核心场景的表现。
2. 测试环境与架构设计
2.1 硬件配置与拓扑结构
测试采用生产级环境配置:
- 代理层:3台16核32G内存的VM,分别部署MySQL Proxy 0.8.5
- 数据库层:6台物理服务器组成主从集群(2主4从)
- 网络:万兆光纤互联,模拟IDC内网延迟(<1ms)
# Proxy启动命令示例 ./mysql-proxy \ --proxy-lua-script=/usr/share/mysql-proxy/rw-splitting.lua \ --proxy-backend-addresses=10.0.0.1:3306 \ --proxy-backend-addresses=10.0.0.2:3306 \ --proxy-read-only-backend-addresses=10.0.0.3:3306 \ --daemon2.2 测试数据集设计
为模拟真实业务,我们构造了三种典型数据模型:
- 用户行为日志表(高频写入)
- 订单交易表(事务密集型)
- 商品信息表(大字段查询)
每类表初始数据量5000万条,采用与实际业务相符的字段结构和索引设计。
3. 核心性能测试结果
3.1 吞吐量对比测试
| 测试场景 | 直连MySQL QPS | 通过Proxy QPS | 性能损耗 |
|---|---|---|---|
| 纯写入 | 12,458 | 9,327 | 25.1% |
| 纯查询 | 23,671 | 21,845 | 7.7% |
| 混合读写(7:3) | 18,992 | 15,336 | 19.3% |
注意:Proxy的性能损耗主要来自网络跳转和Lua脚本解析开销。在写入密集型场景尤其明显
3.2 延迟分布统计
通过99线(P99)指标观察长尾延迟:
- 简单查询:直连8ms → Proxy 12ms
- 复杂联表查询:直连35ms → Proxy 53ms
- 批量插入(100条/事务):直连110ms → Proxy 168ms
实测发现当并发连接数超过500时,Proxy的延迟波动会显著增大。这与其单线程事件循环的设计有关。
4. 关键功能验证
4.1 读写分离实现
我们修改了默认的rw-splitting.lua脚本,增加基于SQL特征的路由逻辑:
function read_query(packet) if string.match(packet, "FOR UPDATE") then return proxy.PROXY_SEND_QUERY end -- 将分析型查询路由到特定从库 if is_analytic_query(packet) then proxy.connection.backend_ndx = 3 -- 指向专用分析从库 end end这种细粒度控制带来两个收益:
- 将报表查询隔离到独立实例,避免影响交易业务
- 强制关键事务走主库,解决读写分离导致的数据一致性问题
4.2 连接池管理
Proxy默认不维护连接池,我们通过以下参数优化连接复用:
proxy-connection-max-age=600 proxy-connection-pool-size=200 proxy-connection-pool-timeout=60实测表明合理的连接池配置可以降低30%的连接建立开销,特别是在微服务频繁创建短连接的场景。
5. 生产环境踩坑实录
5.1 内存泄漏问题
连续运行72小时后,Proxy进程内存从初始的800MB增长到3.2GB。通过valgrind检测发现是Lua脚本中的全局变量未及时释放导致。解决方案:
-- 错误写法 query_cache = {} -- 正确写法 local query_cache = setmetatable({}, {__mode = "v"}) -- 使用弱引用表5.2 故障转移陷阱
测试模拟主库宕机时,发现Proxy不会自动重连新的主库。需要配合keepalived实现VIP漂移,并在脚本中添加心跳检测:
function connect_server() local ok, err = proxy.connection.backend:connect() if not ok then -- 触发告警并尝试切换备用节点 alert_system("DB failure detected!") proxy.connection.backend_ndx = (proxy.connection.backend_ndx % #proxy.global.backends) + 1 return connect_server() end end6. 性能优化实践
6.1 批量操作加速
针对批量插入场景,我们在Proxy层实现了写合并优化:
- 收集短时间内相同表的INSERT语句
- 合并为多值插入语法(INSERT INTO t VALUES(a),(b)...)
- 批量提交到后端数据库
测试显示该优化使批量写入吞吐量提升4-5倍,但需要注意:
- 合并窗口不宜超过200ms,否则会影响业务感知延迟
- 必须保证同批次操作属于相同业务上下文
6.2 查询缓存策略
对于热点查询,在Proxy层实现结果缓存:
local cache = require "resty.lrucache" local query_cache, err = cache.new(200) -- 缓存200条查询结果 function read_query(packet) local key = packet:sub(2) -- 跳过命令字节 local cached = query_cache:get(key) if cached then proxy.response = { type = proxy.MYSQLD_PACKET_OK, resultset = cached } return proxy.PROXY_SEND_RESULT end end function read_query_result(inj) if not inj.resultset.error then query_cache:set(inj.query, inj.resultset) end end7. 替代方案对比
当测试发现Proxy在某些场景不满足需求时,我们对比了其他方案:
| 特性 | MySQL Proxy | ProxySQL | MySQL Router | 应用层Sharding |
|---|---|---|---|---|
| 协议支持 | 完整 | 完整 | 完整 | 依赖实现 |
| 配置复杂度 | 中等 | 高 | 低 | 极高 |
| 动态调整 | 需重启 | 热加载 | 热加载 | 需发布 |
| 监控指标 | 简单 | 丰富 | 中等 | 自定义 |
| 最大QPS支持 | ~15k | ~50k | ~30k | 无上限 |
最终我们决定在交易核心链路采用ProxySQL,而在需要高度定制的分析场景保留MySQL Proxy。
8. 监控指标体系建设
完善的监控是稳定运行的基础,我们采集的关键指标包括:
基础资源层
- Proxy进程CPU/内存占用
- 网络吞吐量(区分进出流量)
- TCP连接数统计
业务逻辑层
- 查询类型分布(SELECT/INSERT等)
- 各后端节点响应时间
- 错误代码统计(如1062主键冲突)
自定义指标
- Lua脚本执行耗时
- 连接池利用率
- 缓存命中率
这些数据通过Telegraf采集后,在Grafana上呈现为统一监控看板。当P99延迟超过阈值或错误率突增时,会触发企业微信告警。
9. 稳定性测试特别发现
在72小时持续压力测试中,我们观察到几个有趣现象:
- TCP端口耗尽:当客户端使用短连接且QPS>3000时,Proxy机器会出现TIME_WAIT状态端口堆积。解决方案:
# 调整内核参数 sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.tcp_max_tw_buckets=20000- Lua GC卡顿:脚本中频繁创建临时表会导致每隔5-7分钟出现一次200ms级别的延迟毛刺。通过调整GC参数缓解:
collectgarbage("setpause", 80) -- 降低GC频率 collectgarbage("setstepmul", 200) -- 增加单步处理量- 协议解析异常:某些ORM框架生成的SQL包含非常规字符,导致Proxy解析失败。需要在脚本开头添加预处理:
function packet_to_string(pkt) pkt = string.gsub(pkt, "[^%w%p%s]", function(c) return string.format("\\x%02x", string.byte(c)) end) return pkt end10. 生产部署建议
根据测试结果,我们总结出这些最佳实践:
- 拓扑设计
- 每个Proxy实例建议承载不超过800个客户端连接
- 采用双层Proxy架构:前端用HAProxy做LB,后端才是MySQL Proxy
- 代理层与数据库节点保持同机房部署
- 参数调优
# 关键参数调整 event-threads=4 # 与CPU核数匹配 proxy-address=0.0.0.0:4040 admin-address=0.0.0.0:4041 admin-username=monitor admin-password=SuperSecret!- 高可用方案
- 使用Keepalived实现VIP故障转移
- 每台Proxy配置独立的管理端口用于健康检查
- 部署ZooKeeper集群维护后端数据库状态
这套配置在我们生产环境已稳定运行6个月,日均处理20亿+查询。虽然Proxy有性能损耗,但其灵活的流量控制能力在业务高峰时多次救场。