简介:《MySQL新增线程池插件性能测试报告》是一份面向数据库管理员、后端开发与测试工程师的高并发优化与压测参考文档,围绕MySQL线程池插件的引入背景、安装配置及前后性能对比展开,帮助读者评估线程池对吞吐量、响应时间与稳定性的实际影响。资源包为单个PDF文件,约3.26MB,内容组织成完整测试方案而非零散笔记。文档从线程池工作原理与参数配置讲起,给出基准测试、压力测试、稳定性测试和负载测试的实施策略,并覆盖测试环境与网络拓扑、软硬件配置、测试数据准备、代码改造及sysbench、JMeter等工具选型。目录中还细分为新增线程池前四库与单库负载测试、新增后四库与单库负载测试、稳定性测试等章节,附有文档修改记录和测试目标,便于按阶段复现与汇报。目前已有446人学习,适合需要制定数据库性能测试计划、对比优化收益或沉淀压测模板的团队参考。
1. 线程池插件到底值不值得上:先看一组 80 线程到 400 线程的实测数字
高并发下 MySQL 连接数一冲就顶到天花板,CPU 没满、IO 也不高,但响应时间开始抖,这种场景我见过太多次。这次拆的是一份 MySQL 线程池插件的性能测试方案与报告文档,核心就一件事:在同一个查询接口上,对比加线程池前后四库、单库的 TPS、响应时间和资源占用。文档里给出的数字很直接——四库场景 80 线程时 TPS 约 129871,加线程池后 100 线程跑到约 171907;单库场景从 80 线程的约 131676,加线程池后 400 线程冲到约 250274,90% 响应时间仍在 2ms 内。这不是理论推演,是带 JMeter 脚本、参数化数据、12 小时稳定性验证的完整压测记录。适合正在被连接数暴涨困扰的 DBA、后端开发和性能测试同学,尤其适合需要向团队证明「这个插件该不该装」的人。
2. 线程池插件原理与压测方案设计:为什么不是调大 max_connections 就完事
默认的 one-thread-per-connection 模型下,每个连接对应一个线程,连接数上来线程就跟着涨,上下文切换、栈内存、锁竞争全跟着来。线程池的思路是把「连接」和「执行线程」解耦:连接进来先排队,由固定数量的工作线程组去消费,超出处理能力的请求在队列里等,而不是无限制地创建线程。这个模型在短查询、高并发场景收益最明显,长事务多、单查询耗时大的场景收益会被摊薄,甚至可能因为排队引入额外延迟,这一点在选型时就必须想清楚。
2.1 线程池解决的是什么问题
线程创建销毁本身有成本,但真正的成本在于线程数超过 CPU 核数后的调度开销。文档里的被测机是 48 核 755G 内存,属于核数相当充裕的机器,即便如此,默认模型在高并发下仍会出现性能问题,说明瓶颈不在单次查询算力,而在连接管理层面的资源竞争。线程池把线程数控制在 thread_pool_size 附近,让活跃线程数与 CPU 核数保持合理比例,剩下的请求排队等待,系统整体吞吐反而更稳。
需要强调的是,线程池不是万能加速器。它优化的是「大量短小请求并发涌入」这一种负载形态。如果你的业务以大查询、批量导入、长事务为主,工作线程会被长时间占住,队列迅速堆积,表现可能还不如默认模型。这也是为什么这份文档要先做基准测试再下结论,而不是直接上线。
2.2 测试维度怎么拆:四库与单库、加压前与加压后
文档的测试矩阵拆得很干净,四个库负载、单库负载,各自在加线程池前后各测一轮,再加一轮 12 小时稳定性和线程组数对比。这样拆的理由是:四库场景模拟的是请求分散到多个实例的横向压力,单库场景模拟的是单实例被打满的纵向压力,两种形态下线程池的收益曲线不一样。实测结果也印证了这点——四库场景加线程池后线程数只从 80 提到 100,TPS 提升约 32%;单库场景线程数从 80 提到 400,TPS 提升接近 90%,说明单实例高并发时线程池的削峰效果更显著。
压测方法用的是阶梯加压:先 40 个请求流稳定跑 5 分钟,再加 40 个,以此类推。这种阶梯法的好处是能看清 TPS 随并发增长的拐点,而不是一上来就打满导致数据无法归因。停止条件是硬指标:CPU、内存平均使用率超过 70%,IO Wait 超过 30%,网络带宽超过 70%,成功率低于 99.99%,90% 响应时间超过 100ms,任一触发就停测定位。这套阈值不是拍的,是给后续容量规划留出安全余量。
2.3 环境与数据准备清单
测试环境的关键参数整理成表更清楚,复现时按这个对齐即可:
| 项目 | 配置 | 说明 |
|---|---|---|
| 数据库机 | 48 核 / 755G 内存 | 单实例部署,另有其他应用常驻占用约 3% CPU |
| 负载机 | 4 台,各 48 核 / 755G | 单库高并发场景扩到 5 台 |
| 施压工具 | JMeter 3.3 | 脚本在本地编写后上传 Linux 执行 |
| 监控工具 | zabbix、JvisualVM、Linux 命令 | 采集 CPU、内存、IO Wait、网络 |
| 基础数据量 | client_app 20w、app_ability 30w、ability_attr 120w、conf_attr 1w | 贴近现网量级 |
| 日志级别 | log.level 改为 error | 避免日志 IO 干扰压测结果 |
被测 SQL 是一条按主键和属性编码查询的单表语句,参数通过${__property(pkid)}、${__property(attrCode)}做参数化,脚本请求体按现网最大请求体改造。这一步很容易被忽略:如果压测用的是小请求体、常量参数,缓存命中率高得离谱,测出来的 TPS 没有任何参考价值。
2.4 脚本落地:JMeter 命令行压测与报告生成
文档给的操作路径是 GUI 编脚本、命令行跑压测、命令行出 HTML 报告,这个流程适合没有图形界面的压测机。落地命令如下:
# 进入 JMeter 的 bin 目录,脚本与结果路径按实际调整 cd /opt/apache-jmeter-3.3/bin # 非 GUI 模式执行压测脚本,结果写入 jtl 文件 # -n 表示非 GUI,-t 指定脚本,-l 指定结果文件 ./jmeter -n -t threadpool_test.jmx -l logs/threadpool_test.jtl # 压测结束后用 jtl 生成 HTML 报告,-o 指定的目录必须为空或不存在 ./jmeter -g logs/threadpool_test.jtl -o report_threadpool第一条命令的-n是必须的,GUI 模式下压测机自身的图形渲染会污染施压能力,尤其是高并发场景。-l指定的 jtl 文件是原始采样数据,保留它能事后重新出图,别只留 HTML。第二条命令的-o目录如果已存在且非空会直接报错,这是常见翻车点,每次出报告前先清目录或换个新名字。生成后打开report_threadpool/index.html就能看到聚合报告、响应时间趋势和 TPS 曲线。
服务器侧的资源采集要和压测同步启动,文档要求提前 5 分钟开始记录。做法是压测前先起一个采样循环,把 CPU、内存、IO Wait 按秒落到文件里,压测结束后再和 JMeter 的 TPS 曲线对齐时间轴:
# 每 2 秒采集一次关键指标,输出带时间戳的 csv,便于和 jtl 对齐 while true; do ts=$(date '+%Y-%m-%d %H:%M:%S') cpu=$(top -bn1 | awk '/Cpu\(s\)/ {print 100-$8}') iowait=$(top -bn1 | awk '/Cpu\(s\)/ {print $10}') mem=$(free -m | awk '/Mem:/ {printf "%.1f", $3/$2*100}') echo "$ts,$cpu,$iowait,$mem" >> /tmp/db_metrics.csv sleep 2 done这段脚本里$8是 top 输出的空闲 CPU 百分比,用 100 减去得到使用率;$10是 IO Wait 列,具体列号随 top 版本略有差异,跑之前先用一次top -bn1确认列位置再固化。指标落盘后,用时间戳把 JMeter 的 TPS 拐点和 CPU 曲线对照起来看,才能判断到底是压力打满了 CPU,还是卡在了别的地方。
2.5 线程池参数怎么定:从测试结论反推配置
文档给出的参考配置方向是thread_pool_size、thread_pool_stall_limit与thread_handling=pool-of-threads三项。thread_pool_size控制工作线程组数量,文档里 32 个线程组单库场景在 150 并发时 TPS 达到约 240000、CPU 使用率 67%,继续加并发 TPS 和 CPU 都不再明显上升,说明这个配置已经接近该硬件的处理上限。定这个值的经验做法是:先按 CPU 核数的 1/4 到 1/2 起步,压测中观察 TPS 拐点和 CPU 曲线,如果 CPU 还有余量但 TPS 不涨,说明瓶颈在队列或 IO,不在线程数。
thread_pool_stall_limit是线程卡住多久算「停滞」的判定阈值,单位毫秒。设得太小会误判正常的长查询,导致线程池额外开线程来兜底,反而破坏线程数控制;设得太大则卡住的线程长时间占用名额,队列堆积。文档里取 500ms 属于短查询场景的合理区间,如果你的业务有 1 秒以上的查询,这个值要往上调。
thread_handling=pool-of-threads是启用线程池模型的开关,写进 my.cnf 的[mysqld]段后需要重启生效。注意这个参数一旦设错拼写,MySQL 可能直接启动失败或回退默认模型,改完务必确认实例正常起来并且线程数不再随连接数线性增长。
3. 四组实测数据拆解:TPS 提升背后哪些数字值得信
测试结论如果只看 TPS 涨了多少,很容易得出片面判断。这一章把文档里的四组对比数据摊开,逐组看它的并发条件、资源占用和响应时间,说清哪组数据的说服力最强、哪组要打折扣看。
3.1 四库负载:80 线程到 100 线程的 32% 提升
加线程池前,四库场景 80 线程持续 300 秒,TPS 约 129871,90% 响应时间不超过 1ms,成功率 100%,CPU 使用率 69%,网络流量约 40.1MB,IO 消耗很小。加线程池后,100 线程持续 300 秒,TPS 约 171907,90% 响应时间仍不超过 1ms,成功率 100%,CPU 使用率 69%,网络流量约 52.25MB。
这组数据的看点是:CPU 使用率前后都是 69%,说明加线程池并没有让 CPU 更忙,吞吐却上去了,提升来自连接管理开销的下降,而不是算力被榨干。同时网络流量从 40MB 涨到 52MB,和 TPS 涨幅大致同比例,说明请求体大小一致、没有出现重试放大,数据可信度较高。要留意的边界是:这组只加到了 100 线程,没有继续往上探,所以 171907 不是这套配置的上限,只是该并发点的观测值,不能拿它当容量结论。
3.2 单库负载:400 线程下 90% 响应时间为何还能守住
单库场景是全文最有说服力的一组。加线程池前 80 线程,TPS 约 131676,90% 响应时间不超过 1ms,CPU 68%。加线程池后直接上到 400 线程,TPS 约 250274,90% 响应时间不超过 2ms,成功率 100%,CPU 67%,网络流量约 75.1MB。
线程数翻了 5 倍,TPS 提升接近 90%,CPU 几乎没变,90% 响应时间从 1ms 放宽到 2ms。这个组合说明线程池在高并发单实例场景下确实把「线程暴涨带来的调度损耗」压下去了。但 2ms 这个数字要正确理解:它是 90 分位,不是平均值也不是最大值,尾部延迟在这份文档里没有展开,如果你的业务对 P99、P999 敏感,仅凭这份数据还不够,需要自己补测尾部分布。另外 400 线程是压测机的施压线程数,不等于数据库侧的实际并发执行数,后者被线程池限制在工作线程组规模内,两者别混为一谈。
3.3 稳定性测试:12 小时 52 线程跑出 95000 TPS
稳定性测试用 52 线程持续请求 12 小时,TPS 稳定在 95000 左右,90% 响应时间不超过 1ms,成功率 100%,CPU 使用率 60%,网络流量约 27.6MB,IO 消耗很小。这组数据的价值不在绝对数字,而在于「长时间不衰减」——没有出现 TPS 随时间下滑、内存持续上涨或连接泄漏的迹象,说明线程池的线程复用没有引入资源泄漏。
这里有个容易误读的点:稳定性测试的 TPS 低于负载测试的峰值,这是正常的,稳定性测试要的是「不低于最优容量」的持续压力,而不是打峰值。文档选择 52 线程而不是峰值线程数,就是为了留余量观察长时间运行的健康度。如果拿稳定性测试的 95000 去和负载测试的 250274 比,得出「加线程池后退化」的结论,那就是典型的指标误用。
3.4 线程组数对比:150 并发时的饱和拐点
文档补充了一组线程组对比:32 个线程组、单库、150 并发(5 台压测机从 10 逐步加到 50 并发)时,TPS 达到约 240000,CPU 使用率 67%;继续增加并发,TPS 和 CPU 上升都不明显;但并发瞬间冲到 225、250 时,CPU 使用率反而都是 60%。
这组数据的含义是系统已经进入饱和区。TPS 不再随并发上涨、CPU 也不涨,说明瓶颈不在 CPU 算力,可能在队列调度或网络侧;而瞬时高并发下 CPU 反而降到 60%,是典型的请求排队现象——线程池把超出处理能力的请求挡在队列里,CPU 不用再为创建大量线程买单。这个拐点对容量规划最有价值:把线上并发控制在拐点以下,比追求峰值 TPS 更实在。文档里那台机器还有约 3% 的 CPU 被其他应用常驻占用,做基线对比时这部分要扣掉,否则会把别人的开销算到线程池头上。
4. 避坑与排查:这套压测方案落地时最容易翻车的五件事
方案写得再细,执行时该踩的坑一个都不会少。下面五条是这类线程池压测里高频出现的问题,每条按现象、原因、解决三段说清。
4.1 压测 TPS 虚高,线上完全复现不了
现象:本地压测 TPS 十几万,上线后同样接口只有零头。原因通常是参数化没做或者缓存命中最优路径,文档里专门要求按现网最大请求体改造、参数用${__property()}注入,就是为了打散请求。解决:压测前核对采样日志里的实际请求参数是否随机分布,把数据库 buffer pool 预热到接近现网状态,必要时在每轮压测前清缓存重跑,别让第一轮的命中率骗了后续所有结论。
4.2 加线程池后 TPS 不升反降
现象:配置改完重启,压测 TPS 比默认模型还低,队列等待明显。原因多半是thread_pool_size设得过大或过小——过大等于没池化,过小则工作线程不够、请求全堵在队列。解决:从 CPU 核数的 1/4 起步做梯度压测,每次只改这一个参数,观察 TPS 拐点;同时确认thread_pool_stall_limit没有小于业务里正常查询的耗时,否则会频繁误判停滞、额外起线程。
4.3 报告生成命令直接报错退出
现象:执行./jmeter -g xxx.jtl -o report时报目录非空或文件格式错误。原因一是-o指定的输出目录已存在且里面有文件,二是 jtl 文件在压测中途被中断导致末尾数据不完整。解决:每次出报告用带时间戳的新目录名,jtl 文件确认压测正常结束后再使用;如果压测被强制中断,那个 jtl 只能拿来看趋势,不要作为正式结论的数据源。
4.4 监控数据和压测结果对不上时间轴
现象:JMeter 显示 TPS 拐点在某分钟,服务器 CPU 曲线却在另一分钟才起来,两边怎么也对不齐。原因是压测机和数据库机存在时钟偏差,或者监控采集是压测开始后才启动的。解决:压测前统一各机器时间同步,监控脚本按文档要求提前 5 分钟启动,采集间隔压到 2 秒以内,落盘时带上完整时间戳;分析时以数据库机本地时间为基准对齐,别用压测机的相对时间。
4.5 长事务场景照搬这套参数
现象:把短查询场景验证过的线程池配置直接搬到有批量导入、长事务的库上,出现队列堆积、连接超时。原因是线程池的工作线程被长任务长期占用,短请求排不上队。解决:长事务为主的实例要么不启用线程池,要么单独评估并调大thread_pool_size、放宽thread_pool_stall_limit;更稳的做法是分实例部署,短查询库用线程池,长事务库保持默认模型,别指望一套参数打天下。
5. 把结论变成可复用的验证习惯:从一份报告到一套回归方法
拿到这类测试文档,最有价值的用法不是抄它的结论,而是抄它的验证流程。我的做法是把「基准—加压—稳定性—参数扫描」四步固化成一个小型回归清单,每次数据库版本升级、参数调整或硬件变更,都按这个清单走一遍,而不是等线上报警了再回头补测。具体落地时,我会先把测试脚本和监控脚本放进同一个仓库,脚本里把线程数、持续时间、阈值全部参数化,用环境变量注入,这样换一套环境只需要改几个值,不用重新编脚本。
验证方法上,我一般会盯三个东西:TPS 随并发增长的拐点、CPU 与 IO Wait 的联动关系、以及 90 分位响应时间在拐点前后是否突变。拐点前 TPS 线性上涨属于正常区间;拐点后 TPS 走平而 CPU 还有余量,说明瓶颈不在算力;90 分位在拐点处突然跳变,则说明队列开始明显堆积,这时候的并发值就是该配置下的安全上限。把这三个信号记下来,比记一个峰值 TPS 有用得多。
还有一个具体技巧:稳定性测试不要只跑一轮。文档里 12 小时 52 线程那组数据很干净,但单轮干净不代表没有慢泄漏。我通常会在稳定性测试前后各做一次内存和连接数的快照对比,用SHOW GLOBAL STATUS LIKE 'Threads_connected'和系统层面的内存占用做前后差值,如果 12 小时后连接数或内存有明显抬升,即使 TPS 没掉也要当问题查。慢泄漏这种东西,靠单轮压测的曲线是看不出来的,血泪经验就是被这种「看起来稳定」骗过一次。
最后一点,参数扫描一定要一次只动一个。线程池相关的几个参数互相影响,同时改两个以上,出了问题根本没法归因,只能全部回退重来。从那以后我每次做这类压测,都强制先把基准跑三遍取稳定值,再进入单参数梯度,宁可多花半天,也不要在几个变量搅在一起的数据里猜结论。希望这套流程能帮到你。
本文还有配套的精品资源,点击获取