ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MySQL线程池插件压测报告:高并发下TPS提升90%的实测数据

MySQL线程池插件压测报告:高并发下TPS提升90%的实测数据

简介:面向数据库运维与后端开发人员的MySQL线程池插件性能测试文档,针对高并发请求下线程频繁创建销毁带来的性能瓶颈,给出从插件原理到压测落地的完整参考。压缩包共1个PDF文件,约3.26MB,内容为一份结构完整的性能测试报告,涵盖线程池工作原理、安装配置参数说明、测试策略与方法、人力与时间安排、测试环境与网络拓扑、软硬件配置、测试数据与代码改造,以及新增线程池前后单库与四库负载测试、稳定性测试等章节,并列出sysbench、JMeter等常用压测工具。读者可据此了解线程池插件的启用方式,掌握基准测试、压力测试、稳定性测试与负载测试的执行思路,对照报告中的测试类型设计自己的验证方案,评估优化前后的QPS、TPS与响应时间变化。已有443人学习,适合需要开展数据库性能压测与调优实践的中高级技术人员参考。

1. 高并发下 MySQL 线程池插件到底值不值得上:一份压测报告能告诉你什么

现网数据库一到晚高峰就出现响应抖动,翻监控发现 CPU 没打满、I/O 也不高,但连接数一冲上去 TPS 就往下掉——这种“资源没到顶、性能先塌方”的现象,十有八九是线程模型的问题。MySQL 默认是一连接一线程,高并发时线程创建销毁的开销、上下文切换的消耗,会把本该用在查询上的 CPU 时间吃掉。这份资料就是围绕“新增线程池插件前后到底差多少”做的一整套性能压测方案与实测记录,包含测试目标、环境配置、JMeter 脚本执行方式、四库与单库两种负载场景、12 小时稳定性验证,以及线程组数对比数据。适合正在做数据库优化选型、需要拿数据说服团队上线程池的 DBA 和后端工程师,也适合想照着复现一套压测流程的测试同学。

2. 线程池插件的工作原理与压测方案设计:先想清楚测什么再动手

2.1 一连接一线程为什么会成为瓶颈

MySQL 默认的thread_handling=one-thread-per-connection模型下,每个客户端连接对应一个服务端线程。连接数在几百以内时这套模型很省心,但并发冲到上千,问题就集中爆发:线程创建和销毁本身要消耗内核资源,活跃线程数超过 CPU 核数后上下文切换频率飙升,调度器在大量可运行线程之间来回切换,真正执行 SQL 的时间片被切得稀碎。表现出来就是 CPU 使用率看着不低但 TPS 上不去,响应时间的长尾越来越长。

线程池插件的思路是把“连接”和“执行线程”解耦:预先创建固定数量的工作线程组,连接进来后不新建线程,而是把请求投递到线程组的队列里,由空闲工作线程取走执行。这样线程总数可控,上下文切换次数大幅下降,高并发下的吞吐和延迟稳定性都会好很多。资料里给出的配置项是thread_pool_size(线程组数量)、thread_pool_stall_limit(线程阻塞判定毫秒数)和thread_handling=pool-of-threads,这三个参数决定了线程池的基本行为,后面调优也主要围绕它们转。

2.2 压测目标怎么定:指标不是拍脑袋写的

这份方案把测试目标分成三类:验证性(特定并发下系统最大处理能力)、探测性(接口和整体系统的压力承受值)、调优性(发现性能瓶颈)。落到具体指标上,方案给了一张明确的验收表,我把它整理成下面这样更直观:

指标项阈值要求说明
CPU 平均使用率≤70%超过即停止加压,定位问题
内存平均使用率≤70%关注是否有内存溢出
系统 I/O Wait≤30%排除磁盘瓶颈干扰
网络带宽≤70%避免压测机侧成为瓶颈
90% 响应时间≤100ms长尾延迟的核心约束
业务成功率≥99.99%错误率必须压到极低

这套阈值的设计逻辑值得说一句:CPU 和 I/O 留 30% 余量是为了给突发流量和后台任务兜底,90% 响应时间卡 100ms 是因为业务侧对查询接口的体感阈值大概就在这个量级,成功率 99.99% 则是高并发下不能出现批量失败的底线。压测时任何一项越界就停止加压,这个“停止条件”比“加压步长”更重要,很多人压测翻车就翻在没有明确的熔断规则,一路加到系统雪崩才反应过来。

2.3 测试环境与数据准备:别让环境差异毁掉结论

方案里的单实例配置是 48 核、755G 内存的 MySQL 服务器一台,配 4 台同规格负载机。这个配置量级说明测试目标是冲高并发去的,普通开发机复现不了绝对值,但流程和相对结论完全可以照搬。基础数据量给得很具体:client_app20 万行、app_ability30 万行、ability_attr120 万行、conf_attr1 万行。数据量级直接决定索引命中情况和缓存效果,数据太少测出来的 TPS 虚高,没有参考价值。

代码侧做了一处改造:把日志级别log.level调到error。这一步很容易被忽略,但压测时如果还在打大量 info 日志,磁盘 I/O 和字符串拼接开销会污染结果,测出来的瓶颈可能根本不在数据库。测试脚本的请求体按现网最大请求体改造,参数化数据量根据并发量和持续时间反推,这两条保证了压测流量和真实流量在形态上接近。

2.4 JMeter 脚本的编写与命令行执行

施压工具选的是 JMeter 3.3,方案里给了完整的命令行执行链路,这套流程在无图形界面的负载机上跑压测非常实用。先在本地图形界面写好.jmx脚本,再把整个 JMeter 目录传到负载机,然后按下面两步执行:

# 第一步:非 GUI 模式执行脚本,结果写入 jtl 文件 ./jmeter -n -t query_ability_attr.jmx -l logs/result.jtl # 第二步:把 jtl 结果转成 HTML 报告,输出到指定目录 ./jmeter -g logs/result.jtl -o report_output

-n表示非 GUI 模式,压测必须用它,图形界面本身会消耗资源影响结果;-t指定脚本文件;-l指定结果日志路径,这个 jtl 文件是后续分析的原始数据,别覆盖。第二步的-g读取已有 jtl 生成报告,-o指定输出目录且该目录必须为空,否则会报错——这是新手最常踩的一个小坑。生成的index.html用浏览器打开就能看到聚合报告、响应时间趋势和 TPS 曲线。

脚本里的查询语句是select ... from ability_attr where pkid = ${__property(pkid)} and attrcode = ${__property(attrCode)},两个参数通过${__property()}从属性文件读取做参数化。参数化的意义在于让每次请求命中不同的行,避免所有请求打到同一行导致缓存命中率虚高,测出来的数字好看但不真实。

2.5 加压节奏:40 线程一档,5 分钟一停

方案里四库和单库负载测试用的是同一套加压逻辑:从 40 个请求流起步,稳定跑 5 分钟,再加 40 个,再跑 5 分钟,如此阶梯上升。每一档结束后检查 CPU、内存、I/O Wait、带宽、成功率和 90% 响应时间,全部达标才继续加,任何一项越界立即停止并定位。找到最大能力对应的请求量后,再用这个量跑 5 分钟做一次防突发验证。

这个“阶梯加压 + 逐档校验”的方法比一次性打满更科学,因为你能看到性能随并发变化的拐点在哪里。线程池的价值恰恰体现在拐点位置上:没有线程池时拐点来得早、拐点后 TPS 掉得快;有线程池后拐点后移、平台期更长。稳定性测试则是取不低于最优容量的稳定业务流,连续跑 12 小时,观察 TPS 是否衰减、成功率是否掉、内存是否缓慢上涨。

3. 四库与单库负载实测:线程池前后的数字对比怎么读

3.1 新增线程池前的基线数据

先看没有线程池的基线。四库场景 80 线程持续 300 秒,TPS 达到 129871,90% 响应时间 ≤1ms,成功率 100%,服务器 CPU 使用率 69%,网络流量 40.125MB,I/O 消耗很小。单库场景同样是 80 线程 300 秒,TPS 131676,90% 响应时间 ≤1ms,成功率 100%,CPU 68%,网络流量 41MB。

这两组数字透露出一个关键信息:80 线程时 CPU 已经到 68%~69%,逼近 70% 的停止线。也就是说默认线程模型下,这台 48 核机器在 80 并发左右就基本到顶了,再往上加要么 CPU 越界,要么 TPS 不涨反跌。基线测试的意义就是把这个“天花板”量化出来,后面所有提升都以它为参照。

3.2 新增线程池后的提升幅度

启用线程池后,四库场景用 100 线程跑 300 秒,TPS 冲到 171907,90% 响应时间仍 ≤1ms,成功率 100%,CPU 69%,网络流量 52.25MB。单库场景更夸张,400 线程跑 300 秒,TPS 达到 250274,90% 响应时间 ≤2ms,成功率 100%,CPU 67%,网络流量 75.125MB。

把数字摆在一起看更清楚:

场景线程数TPS90% 响应时间CPU 使用率
四库·无线程池80129871≤1ms69%
四库·有线程池100171907≤1ms69%
单库·无线程池80131676≤1ms68%
单库·有线程池400250274≤2ms67%

四库场景 TPS 提升约 32%,单库场景提升约 90%,而且单库是在并发从 80 拉到 400 的情况下实现的,CPU 反而还低了一个点。这说明线程池把“并发数”和“CPU 消耗”之间的强耦合拆开了:以前加并发就加 CPU,现在并发翻五倍 CPU 基本不动,多出来的并发被线程池的队列和工作线程复用消化掉了。90% 响应时间从 ≤1ms 变成 ≤2ms 属于可接受范围,毕竟并发量级完全不同。

3.3 12 小时稳定性与线程组数对比

稳定性测试用 52 线程持续请求 12 小时,TPS 稳定在 95000 左右,90% 响应时间 ≤1ms,成功率 100%,CPU 60%,网络流量 27.5625MB,I/O 消耗很小。12 小时不掉 TPS、不掉成功率,说明线程池没有内存泄漏或线程堆积问题,这是上线前必须过的一关。

资料里还有一段线程组数对比的补充记录值得单独拎出来:新增线程池、32 个线程组、单库场景下,150 并发(5 台压测机从 10 逐步加到 50)时 TPS 达到 240000,CPU 67%;继续加并发,TPS 和 CPU 上升都不明显;但并发瞬间冲到 225、250 时,CPU 使用率反而只有 60%。这个“并发越高 CPU 越低”的反直觉现象,大概率是瞬时高并发下大量请求在队列里排队、实际执行的工作线程数受线程组数限制,CPU 没被真正压满。它提醒我们:线程池不是并发越高越好,thread_pool_size设小了会成为新的瓶颈,设大了又退化成接近默认模型,这个参数必须结合压测数据调。

4. 线程池参数配置与压测脚本落地:照着改就能跑

4.1 my.cnf 里的三个核心参数

线程池的启用和调优集中在配置文件里,方案给出的配置片段如下:

[mysqld] # 启用线程池调度模型,替代默认的一连接一线程 thread_handling = pool-of-threads # 线程组数量,通常设置为 CPU 核数附近,需结合压测调整 thread_pool_size = 32 # 线程被判定为阻塞的毫秒阈值,超过则调度器补充新线程 thread_pool_stall_limit = 500

thread_handling是总开关,改成pool-of-threads后 MySQL 走线程池调度。thread_pool_size决定线程组数量,每个组内部有自己的队列和工作线程,组数太少会让请求在组间排队,组数太多则调度开销回升,资料里 32 组对应 48 核是一个偏保守的起点。thread_pool_stall_limit控制阻塞判定,值太小会频繁补线程、削弱线程池意义,值太大则阻塞请求迟迟得不到处理,500ms 是资料里的取值,实际调优时可以按业务 SQL 的平均执行时间上下浮动。

提示:这三个参数改完必须重启实例生效,压测前先在测试库验证,别直接动现网配置。

4.2 用 sysbench 做一轮快速对照

JMeter 适合复现资料里的 HTTP 层压测,但如果只想快速验证线程池对纯数据库查询的影响,sysbench 更轻量。下面这组命令可以做前后对照:

# 准备 120 万行测试数据,对应资料里 ability_attr 的量级 sysbench oltp_point_select \ --mysql-host=127.0.0.1 --mysql-port=3306 \ --mysql-user=test --mysql-password=test \ --mysql-db=perf_test \ --tables=1 --table-size=1200000 prepare # 80 并发跑 300 秒,记录 TPS 和 95% 延迟 sysbench oltp_point_select \ --mysql-host=127.0.0.1 --mysql-port=3306 \ --mysql-user=test --mysql-password=test \ --mysql-db=perf_test \ --threads=80 --time=300 --report-interval=10 run

--table-size=1200000对齐资料里的数据量级,--threads=80对齐基线测试的并发数,--time=300对齐 5 分钟单档时长,--report-interval=10每 10 秒打一次中间结果方便看趋势。先在不启用线程池时跑一遍记下 TPS,改配置重启后再跑一遍,两组数字对比就是线程池在这个数据量级下的真实收益。注意 sysbench 的oltp_point_select是主键点查,和资料里的联合条件查询不完全一样,绝对值会有差异,但前后对比的相对趋势是可信的。

4.3 压测期间该盯哪些监控项

资料里监控工具列了 zabbix、JvisualVM 和 Linux 命令三类。落到实操,压测时我一般会开三个窗口:一个跑top -H -p <mysqld_pid>看线程级 CPU 分布,确认工作线程是否均匀;一个跑iostat -x 1看%iowait和磁盘 util,排除 I/O 干扰;一个跑vmstat 1看上下文切换次数cs和运行队列r。线程池生效后,cs的增速应该明显低于默认模型,这是判断线程池是否真正起作用的一个硬指标,比只看 TPS 更可靠。

5. 压测避坑清单:五个让结论失真的常见问题

5.1 压测机先成了瓶颈

现象:加压到一定并发后 TPS 不再上涨,但数据库 CPU 只有 50% 出头。原因:负载机自身的 CPU、网络或 JMeter 堆内存先到顶,请求发不出去。解决:压测前先空跑一轮确认负载机余量,JMeter 用非 GUI 模式并适当调大-Xmx,多台负载机分布式施压,同时监控负载机资源。

5.2 参数化没做,缓存把结果美化了

现象:TPS 高得离谱,响应时间几乎为 0。原因:所有请求查同一行,InnoDB 缓冲池和查询缓存全命中,等于在测内存。解决:像资料里那样用${__property()}做参数化,确保请求分散到不同主键,参数化数据量按并发量乘以持续时间估算。

5.3 日志级别没调,磁盘 I/O 背了黑锅

现象:压测中%iowait偏高,但数据库本身没做写操作。原因:应用或数据库还在打大量 info 日志。解决:按资料里的做法把log.level调到error,压测期间关闭不必要的慢查询日志和通用日志,测完再恢复。

5.4 线程组数拍脑袋设,压测结论不可迁移

现象:线程池启用后 TPS 提升不明显,甚至不如默认模型。原因:thread_pool_size设得过小,请求在少数线程组里排队;或设得过大,退化成近似一连接一线程。解决:以 CPU 核数为起点做几组梯度对比(比如 16、32、48),每组跑同一加压曲线,选 TPS 平台期最长、CPU 最平稳的那组。

5.5 只看平均值,漏掉长尾和瞬时峰值

现象:平均响应时间很漂亮,但业务侧仍反馈偶发卡顿。原因:平均值掩盖了长尾,资料里 90% 响应时间这个指标就是为此设的,但瞬时并发冲高时(比如记录里并发瞬间到 225、250 那一段)CPU 反而掉到 60%,说明请求在排队。解决:压测报告里同时看 90%、99% 响应时间和 TPS 时间序列曲线,关注拐点和抖动,别只盯聚合报告的平均值。

6. 把压测结论变成可复用的调优习惯

压测报告最容易犯的错,是把一堆截图和数字堆完就结束,下次换台机器又得从头摸索。这份资料里真正有价值的东西,是那套“阶梯加压 + 逐档校验 + 停止条件前置”的方法论,以及四库/单库、有无线程池、稳定性、线程组数四个维度的对照设计。我自己的做法是把这套流程固化成一张检查表:压测前确认数据量级和参数化、确认日志级别、确认负载机余量;压测中按 40 线程一档加、每档 5 分钟、逐项核对阈值;压测后除了 TPS 还要看cs上下文切换和 99% 响应时间。

线程池参数调优上,我一般会做一个小实验矩阵:thread_pool_size取 16、32、48 三档,thread_pool_stall_limit取 200、500、1000 三档,交叉跑同一加压曲线,把 TPS 和 CPU 画成表,选平台期最宽的那组。资料里 32 组、500ms 的配置在 48 核机器上表现不错,但换到核数更少的实例上未必适用,参数必须跟着硬件走。还有一个容易忽略的点:线程池上线后要持续观察一段时间的连接数和活跃线程数,确认没有连接堆积或线程饥饿,压测通过不等于现网就稳。

从那以后我每次做数据库性能对比,都强制先跑一轮基线、再改配置跑对照、最后补一轮 12 小时稳定性,三步缺一不可——跳过基线,提升幅度就是无源之水;跳过稳定性,压测数字再好看也不敢上线。希望这套流程能帮到你,少走几段我当年踩过的弯路。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进