
性能压测做了这么多年我越来越觉得一个很关键的点经常被团队忽略大家都盯着接口、服务器、中间件却很少有人认真想过数据库本身的承载能力。很多时候接口压测报告一片绿色结果上线一放大流量数据库先跪了。所以每次做性能测试我都会在Jmeter里用JDBC Request直连数据库做一轮专项压测。这篇文章就把我实际使用JDBC Request结合数据库做性能测试的完整经验整理出来从环境搭建、连接配置、Query Type选型、参数化、断言校验到压测过程中的监控和排坑基本是我踩过一遍之后觉得可以拿来直接用的东西。适合有一定Jmeter基础、想往更深层做性能测试的同学参考。1. 为什么性能压测要直接压数据库JDBC Request解决的痛点1.1 压了一层又一层最后瓶颈在数据库大部分团队做性能测试的路径是先测Web接口再测服务端然后看监控面板。这套流程本身没问题但有个盲区——接口层通过不代表数据库层扛得住。尤其是报表类、统计类、带有复杂关联查询的业务接口可能做了缓存、限流、异步削峰真正落到数据库的请求被掩盖了。等到数据量上来、缓存失效或者查询条件变化数据库的SQL执行计划才会暴露问题那时候再优化代价就大了。数据库专项压测就是把这一层单独拎出来测Jmeter通过JDBC Request直接对数据库发SQL绕过业务接口、缓存层直接看数据库实例在不同并发下的吞吐、响应时间、连接占用情况。这样做的价值有三个提前暴露索引缺失、SQL性能差、连接池配置不足等问题为后续接口压测提供数据库能扛多少并发的基线参考在容量规划时能算出数据库层面的瓶颈水位给出明确的扩容依据。1.2 JDBC Request在性能测试链路里的位置很多刚接触的人会把JDBC Request和接口压测割裂开看其实它不是替代关系而是补充关系。一个完整的性能测试链路至少要覆盖三层客户端/网关层、应用服务层、数据存储层。接口测试主要验证前两层JDBC Request就专门负责数据存储层。我自己习惯的做法是先做一轮轻量级的JDBC Request压测摸清数据库能支持的并发基线再去做接口压测。这样接口压测的结果出来之后能快速判断瓶颈究竟在应用代码还是数据库。如果接口压测的TPS低于数据库基线测算值那就说明瓶颈在服务端代码、缓存策略或者网络而不是数据库的问题。1.3 什么场景适合用JDBC Request不是所有项目都需要单独做数据库压测。根据我的经验下面这几类场景比较值得投入对TPS和响应时间有明确要求的核心业务接口比如订单查询、登录鉴权、账户流水查询大数据量下的复杂查询比如上千万行的订单表做条件筛选、分页、聚合大面积读写混合的业务比如秒杀抢购、库存扣减、积分变动需要做容量规划或数据库迁移验证的场景。反过来如果是简单的CRUD、数据量几十万行以内、数据库负载常年很低的内部系统直接用接口压测覆盖就够了单独搭JDBC Request反而有点过度投入。2. 环境准备驱动版本、连接串与测试计划搭建2.1 驱动选择这一步错后面全是坑JDBC Request要连数据库首先得有对应的JDBC驱动。不同数据库驱动jar包不同版本选择也有讲究。以最常用的MySQL为例我遇到过好多次No suitable driver found的报错排查下来十有八九是驱动包放错位置或者版本不兼容。MySQL驱动分两个大版本线mysql-connector-java 5.x.x和mysql-connector-j 8.x.x。如果你连的MySQL是5.7用5.1.49基本没问题如果是MySQL 8.0建议用8.0.x的驱动包。这个选择影响的不光是兼容性连接串写法也有差异稍后细说。驱动下载好之后要把jar包放到Jmeter安装目录的lib文件夹下同时建议再放到lib/ext目录一份然后重启Jmeter。很多教程只说放lib实际有些版本必须放在lib/ext下才能被加载两个目录都放了就不用纠结了。放完之后记得检查一下Jmeter的Options菜单里应该有Test Plan下能看到JDBC相关的组件或者直接新建一个JDBC Connection Configuration看能不能选到驱动类。其他常见数据库的驱动包分别是Oracleojdbc8.jar或ojdbc6.jar根据数据库版本选择PostgreSQLpostgresql-9.x或42.x版本SQL Servermssql-jdbc-6.x或7.xSQLitesqlite-jdbc-3.x。如果是SQLite这种文件型数据库做压测的意义不大但用来验证脚本编写和参数化逻辑还是可以的。2.2 连接串构造从能连上到连得对连接串是另一个容易出问题的地方。Jmeter的JDBC Request需要一个JDBC URL格式和Java代码里写的一样。以MySQL为例jdbc:mysql://192.168.1.100:3306/testdb?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghairewriteBatchedStatementstrue几个关键参数要解释一下useSSLfalse本地测试环境或者内网压测不开启SSL可以减少握手开销避免不必要的性能损耗。MySQL 8默认要求SSL连接不关掉会报错或者产生SSL握手延迟。serverTimezoneAsia/ShanghaiMySQL 8驱动对时区敏感不指定会报错Server returns invalid timezone加上这个参数省去很多麻烦。rewriteBatchedStatementstrue如果压测场景涉及批量插入这个参数能显著提升性能。它的原理是把多条INSERT语句重写成一条多VALUES的语句减少网络往返。allowMultiQueriestrue这个要小心开启后支持一条语句里写多个SQL用分号分隔。Jmeter里如果要用CSV Data Set或者循环控制器做批量操作可以配合使用。但如果只需要单条SQL不建议开有SQL注入风险也会让日志排查变难。PostgreSQL的连接串写法稍微不同jdbc:postgresql://192.168.1.100:5432/testdbOracle的写法是jdbc:oracle:thin:192.168.1.100:1521:orcl建议把连接串单独放到一个自定义变量里用${__P(db_url,)}这种方式引用方便环境切换。这样测试脚本从dev环境切到生产环境只需要调整JMeter属性不用改线程组里的每个请求。2.3 测试计划结构最少必要框架JDBC Request压测的测试计划结构我目前比较习惯这样搭Test Plan ├── 用户自定义变量数据库连接串、用户名、密码、查询SQL ├── JDBC Connection Configuration配置连接池 ├── 线程组 │ ├── JDBC Request业务查询SQL │ ├── 响应断言校验结果 │ ├── 汇总报告 / 聚合报告监听器 └── 查看结果树调试阶段用正式压测关掉关键点是JDBC Connection Configuration的作用域是它所在的层级及其子级。如果放在线程组下面那这个线程组的所有JDBC Request共享这个连接池配置。如果放在Test Plan顶层那所有线程组的JDBC Request都共用这套连接配置可能会互相抢占连接。我一般建议放在线程组级别针对不同场景用独立的连接池配置这样互不干扰。还有一个细节JDBC Connection Configuration里的Variable Name必须和JDBC Request里的Variable Name保持一致。这个变量名是Jmeter用来关联连接池和请求的名字对不上就会报Connection is not available之类的错误。我见过不少人在这栽跟头——明明配置没问题就是忘了对名字。3. 核心配置JDBC Connection Configuration参数逐项拆解3.1 关键参数对照表每一项都不是摆设JDBC Connection Configuration面板的参数很多人都是默认值一路点过去其实每个参数都有讲究。整理成表格方便对照参数我的推荐值说明Variable Name自定义如db_test连接池标识JDBC Request里必须引用同名变量Max Number of Connections线程数的1.5到2倍连接池上限太大会浪费资源太小会排队超时Max Wait (ms)10000连接池没有空闲连接时线程等待的最大毫秒数Time Between Eviction Runs (ms)60000空闲连接扫描间隔用于剔除失效连接Auto Commitfalse是否自动提交事务压测纯查询时无所谓读写混合时建议手动控制Transaction IsolationDEFAULT保持默认不要随便改隔离级别改了会影响数据库行为Pool PreparedStatementstrue开启预编译语句缓存对频繁执行的查询有明显性能提升Preallocation Count线程数预创建连接数量避免压测启动瞬间连接风暴3.2 连接池参数对压测结果的影响连接池参数的设置直接决定了压测结果是否可信。举个例子如果你的线程组设置20个并发线程但连接池最大连接数只有10那么实际发到数据库的并发只有10个剩下的10个线程在等待连接表现出的响应时间会偏高TPS却上不去。最后你测出来的结果不是数据库的真实瓶颈而是连接池瓶颈。正确的做法是先根据目标并发设置连接池上限一般设置为线程数的1.5到2倍留有缓冲。因为一个线程在执行过程中一个连接占用的时间可能不是整个压测周期有时候查询快、事务短一个连接可以被多个线程复用但为了排除连接池本身成为瓶颈上限不要设得太紧。另外有个参数容易被忽视Time Between Eviction Runs (ms)。如果MySQL服务端配置了wait_timeout长期闲置的连接会被服务端关闭连接池里还存着这个假连接下次使用就会报Communications link failure。把这个扫描时间设置得比wait_timeout短比如60秒一次连接池会自动剔除失效空闲连接压测过程中出错概率小很多。Auto Commit这个参数也值得说两句。压测纯查询场景Auto Commit设置false和true区别不大。但如果是压测事务型场景比如查询订单-扣减库存-插入流水这类操作Auto Commitfalse配合JDBC Request里的Commit操作能让整个事务作为一个整体提交更接近真实业务的行为。如果保持Auto Committrue每条SQL独立提交事务边界就不对压测结果参考价值会打折扣。3.3 验证查询连接池自检的体检表JDBC Connection Configuration里有个Validation Query配置比如MySQL可以填SELECT 1。这个查询会在连接创建和空闲回收时执行确保连接池里的连接都是可用的。付费版的一些连接池组件默认没填建议手动填上。填SELECT 1这个值的成本极低但对压测稳定性很有帮助——尤其是数据库重启过、网络闪断过、连接被服务端踢掉的情况验证查询能提前发现问题而不是等请求发过去才发现连接已经断了。之前我压测过程中遇到过一种情况前20分钟一切正常突然所有请求都报Connection is not available, request timed out排查半天发现是数据库负载高的时候主动断开了大量空闲连接而连接池毫不知情还在继续分配这些死连接。后来加上了SELECT 1的验证查询这类问题基本绝迹。4. 动手写JDBC RequestQuery Type怎么选、SQL怎么写4.1 Query Type选型五类语句各有各的用途JDBC Request的Query Type下拉框默认是Select Statement。这个选项直接影响SQL的执行方式和结果处理选错了轻则数据取不出来重则压测完全失去意义。我逐个说一下我常用的几种Select Statement单条SELECT查询返回ResultSet结果集。适合压测读操作。它每次执行都会调用Statement对象不经过预编译逻辑最简单但SQL执行性能比Prepared Statement差一些。Update Statement单条UPDATE/INSERT/DELETE语句。适合压测写操作。Prepared Update Statement单条预编译的增删改语句支持SQL里的?占位符参数化变量。性能比普通Statement好也是我最推荐的写操作方式。Callable Statement调用存储过程适合压测复杂业务逻辑封装在存储过程里的场景。Commit / Rollback事务控制语句配合Auto Commitfalse使用不属于业务SQL但很重要。一个容易走的弯路是很多人不管什么场景都用Select Statement。如果你压测的是登录接口实际SQL是SELECT * FROM user WHERE username?这种用Select Statement问题不大因为Jmeter会把变量填充进SQL再执行。但如果SQL量很大、执行次数很多建议切换到Prepared Select Statement利用预编译缓存执行效率和数据库负载表现更接近真实业务。4.2 参数化SQL的三种写法变量、占位符、CSVJDBC Request里写SQL参数化是我觉得最灵活也最能玩出花的地方。三种方式我都在不同场景用过第一种变量直接拼进SQL字符串。比如SQL写成SELECT * FROM order_info WHERE user_id ${uid}然后在Jmeter里添加一个用户自定义变量uid或者用函数${__Random(1,100000,)}生成随机值。这种方式最直观但要注意SQL是把变量替换成值之后发送给MySQL的本质上和拼接字符串没有区别高并发下如果变量值的分布不理想比如某几个值出现的概率极高会导致数据库热点数据压测结果偏移。第二种用?占位符配合Parameter Values和Parameter Types。这是Prepared Statement标准的参数传递方式我推荐在大多数场景使用。JDBC Request的Parameter values按逗号分隔填入参数值Parameter types填入对应的SQL类型。比如SELECT * FROM order_info WHERE user_id ? AND status ?Parameter values填${uid},${status}Parameter types填INTEGER, VARCHAR这种写法每次执行都是预编译语句数据库端会对SQL做缓存执行计划可以被复用更贴近生产环境应用真的使用PreparedStatement的方式。另外也天然避免了SQL注入的问题虽然压测环境不太在意这个但专业习惯还是好点。第三种配合CSV Data Set Config做大量数据驱动。压测场景如果要求每条请求都查不同的数据比如模拟不同用户查自己的订单我会准备一个CSV文件放几万条真实用户ID用CSV Data Set Config按线程组循环读取。这样数据分布比随机值更接近真实业务压测也更均匀。4.3 查询结果的再加工从Jmeter到后续处理JDBC Request默认会保存查询结果变量名的前缀在Variable Names字段里指定。如果你填了var后面可以通过var_1、var_2引用第一列、第二列通过var_#获取总列数等。这个功能在联调场景下很有用。比如你想把数据库里某个真实用户ID取出来然后作为下一个接口请求的参数。但性能压测场景我不太建议这样串联调用因为会引入额外开销还会让压测结果里混入数据处理的噪声。如果确实需要在压测中获得数据库数据可以在正式压测前先用JDBC Request跑一次查询把结果保存到CSV再在正式压测中用CSV Data Set Config读取。这样一个环节一个环节分开定位问题容易得多。5. 让压测结果可信断言、测试数据与并发模型5.1 数据正确性校验别让错误结果混进报告性能压测不是只跑并发还要确保压测过程中SQL执行结果是正确的。很多团队跑完一轮只看TPS和响应时间完全没校验SQL返回数据的正确性结果写错的SQL、查出来的数据明显不对报告照样发出去后面要返工不说还会误导排障方向。Jmeter里做数据校验最直接的是响应断言。JDBC Request的响应文本包含查询结果集、更新行数等信息可以用Contains断言匹配关键值。比如查询用户余额的SQL断言文本里包含100.00基本能确定查询返回了预期数据。如果是更复杂的校验逻辑比如对比多个字段的值、校验返回行数就得用BeanShell断言或者JSR223断言。这里要提一个网上经常搜到的东西Jmeter BeanShell断言。BeanShell断言的灵活度很高能用Java代码直接操作SampleResult和变量。但我要提醒的是BeanShell组件在Jmeter里性能很差。单机压测时每个请求都要额外启动一个解释器开销不小并发高的时候会拖慢压测机本身。所以我的建议是调试阶段可以用BeanShell断言做开发正式压测时要么去掉断言要么用JSR223断言配合Groovy脚本——Groovy的启动开销和并发能力都比BeanShell好很多尤其是那种要校验大数据量结果集的场景。5.2 测试数据准备用Python批量制造压测数据性能测试最容易让结果失效的坑之一就是用生产环境数据量级别的表去测试结果因为数据量不同导致索引失效、执行计划偏差。而造数这一步我这些年最常用的工具其实就是Python。Python在数据准备阶段的价值一个是批量生成CSV数据文件另一个是直接用pymysql或者sqlalchemy批量写入数据库。举个例子如果我需要一个100万行的订单表用Python脚本生成包含用户ID、订单金额、状态等字段的数据再按每批5000条commit一次比在Jmeter里发100万次JDBC请求快得多还不会污染压测结果。批量写入时有个细节要注意开启rewriteBatchedStatements参数后用pymysql的executemany或者sqlalchemy的ORM bulk insert效率提升非常明显。我实测过插入10万条记录不加这个参数大概要4分多钟加上之后只需要40秒左右差距就是这么悬殊。造数时说几个个人经验数据分布要接近真实业务比如订单状态字段不能全是已完成要有一定比例的待支付已取消否则压测查询频率高的SQL时数据分布不均会导致索引失效或者热点页。数据量要分级准备。我会准备三套10万行、100万行、1000万行先在小数据量下跑通脚本和断言再逐级放大这样能看出数据量增长对响应时间的影响曲线对容量评估有直接的参考价值。造数脚本建议独立提交到测试工程里和压测脚本分开维护。造数是一件事压测是另一件事混在一起后面维护很痛苦。5.3 并发模型设计循序渐进还是瞬间冲击并发模型直接决定压测报告能不能反映真实场景。两种极端我都试过瞬间冲击全部并发和不加思考地固定并发跑到底。前者会把压测机和数据库的连接建立风暴混进结果里后者又会遗漏系统在灰度扩容、流量突增时的问题。比较稳妥的做法是阶梯式加压。Jmeter里可以用Stepping Thread Group或者Ultimate Thread Group插件设定初始线程数、每阶段增加多少线程、每阶段保持多久。例如10个线程跑1分钟 - 20个线程跑1分钟 - 50个线程跑1分钟- 100个线程跑2分钟。这样每一阶段都能记录到系统在不同并发下的表现画出来的TPS曲线和RT曲线能看到明显的拐点也就是系统的性能拐点那个拐点对应的并发数就接近系统能力上限了。还有一点压测前先花10-20秒做一次单用户sanity check确认SQL本身没问题。别上来就100并发跑压测第一分钟所有的错误可能都是同一个SQL书写问题造成的浪费时间不说还容易让人误判。6. 实测复盘从Jmeter监控到数据库侧交叉验证6.1 压测过程中到底该看哪些指标很多人跑压测就盯着Jmeter的TPS和响应时间这两个数但数据库专项压测光看这些太粗了。我自己的做法是分两层看第一层Jmeter结果层面看TPS事务数/秒、平均响应时间、90%或95%响应时间、错误率、每秒连接数。这里TPS不等于数据库QPS因为一次JDBC Request可能内部执行了多条SQL如果你开了allowMultiQueries这两个概念得区分清楚不然报告写出来会被DBA喷。第二层数据库侧看QPS、并发连接数、Threads_running、InnoDB Buffer Pool命中率、慢查询数、临时表磁盘使用量。这些指标能回答数据库为什么是这个表现。压测结束后的分析逻辑我见过很多性能报告只罗列数据没有归因分析。其实很简单如果TPS随并发上升保持线性增长直到数据库连接数拉满或者CPU到达瓶颈说明数据库吞吐还有空间如果TPS不升反降同时RT大幅上涨大概率是发生了锁等待或者连接排队如果RT一直很低但TPS上不去可能是单条SQL本身太慢——这时就要去数据库侧看慢查询日志了。6.2 我遇到过的三种瓶颈模式模式一连接数打满。线程数加到200数据库最大连接数也是200所有连接全被占满新请求排队等连接TPS停滞在3000左近RT飙到2秒以上。数据库侧Threads_connected200Threads_running也接近200。这种情况调整方案是要么减小连接池上限给其他应用留空间要么数据库端调大max_connections。但说实话绝大多少情况200并发就能打满连接说明单条SQL执行时间偏长得先优化SQL本身。模式二慢查询拖垮一切。TPS看着能到5000实际数据库侧慢查询日志全是同一条聚合SQL每条跑1.2秒。所有快速查询的资源都被这条慢SQL抢占。这种场景用Jmeter单独压慢SQL所在的查询条件能精确地复现问题并验证优化效果。加索引、改SQL写法、优化JOIN顺序每次改动再压一次对比RT和TPS优化效果一目了然的直观。模式三热点数据锁等待。并发高时大量请求都在等同一行数据的行锁比如秒杀场景扣减同一件商品的库存。表现为RT呈梯形分布整体TPS不高且波动明显数据库侧InnoDB的行锁等待次数持续增加。这种属于典型的业务设计问题靠改SQL帮助有限通常要结合缓存更新队列、乐观锁等方式在应用层解决。Jmeter在这里的价值就是能验证你改完应用之后锁等待是否真的降下来了。6.3 交叉验证Jmeter数据要和数据库慢日志对齐这里有个非常实用的技巧压测结束后取Jmeter里最近一段时间的平均RT和数据库慢查询日志里对应时间段SQL的平均执行时间做对比。两者差值如果稳定在20毫秒左右说明网络开销和Jmeter本身的采样开销就这么多往后估算时心里有数如果差值到了几百毫秒那说明Jmeter侧有性能瓶颈比如监听器写日志太慢、断言脚本耗时太高压测机需要优化后再跑一轮。我碰到过一个很典型的例子本地笔记本压测Jmeter结果显示平均RT 800毫秒数据库侧SQL执行只要50毫秒。仔细排查后发现Jmeter一台机器开了汇总报告查看结果树后端监听器还开着BeanShell断言CPU已经占了90%采样和监听的开销把结果拉飞了。换了一台干净的压测机、关掉多余监听器后RT恢复到100毫秒左右。所以跑数据库压测压测机务必用独立的机器至少别和被测数据库共用一台机器不然结果没法看。7. 常见报错与完整排查链路从一次压测故障说起7.1 一次值得复盘的具体故障有一次我压测一个会员积分查询接口JDBC Request设置的并发数不高只有30但跑了大概3分钟后Jmeter里大量请求报错错误信息是Connection is not available, request timed out after 10000ms。数据库侧看Threads_connected一直往上窜最后卡在300左右线程状态里大量Waiting for table metadata lock。我当时的排查链路是这样的第一步看Jmeter配置。连接池Max Connections设的是10明显低于30个并发线程。但问题是前几分钟是好的到第三分钟才开始报错说明不是单纯连接数不够。第二步查数据库侧。看到大量Waiting for table metadata lock的时候思路就清晰了肯定有条SQL持有表的元数据锁把后面的查询全部堵住了。去information_schema.innodb_trx查了当前事务列表发现有一条跑了八九分钟的ALTER TABLE语句——是DBA在压测期间改了表结构。第三步定位根因。那天的压测数据里混入了一个新增字段的DDL操作DDL没有加ALGORITHMINPLACE直接升级成了COPY操作把表锁住了。Jmeter这边积累的等待请求越来越多连接池被占满后面所有请求都超时。这个故障的教训是数据库专项压测前一定要和DBA确认压测窗口内没有任何DDL变更、备份、抽数任务。压测是验证不是排雷。我后来会把这类检查做成一个压测前checklist每次都逐项确认省掉了不少麻烦。7.2 No suitable driver最常见的启动时报错这个报错几乎每个入门JDBC Request的人都遇到过。常见原因有三类驱动jar包没放到Jmeter的lib目录驱动版本和数据库版本不兼容MySQL 8.0下没指定serverTimezone导致的错误看似是连接串问题实际和驱动加载无关。排查顺序建议先在Java环境下试用同样连接串如果Java能连而Jmeter报错那问题大概率是jar包位置或者classpath。确认jar包在lib下之后再检查驱动类名是否填写正确——MySQL 5.x写com.mysql.jdbc.DriverMySQL 8.x写com.mysql.cj.jdbc.Driver很多旧教程用的是5.x的类名放到8.x驱动下就会加载不到。7.3 字符集乱码问题中文数据查出来是压测环境经常出现的一种情况从Jmeter发过去的SQL如果包含中文条件而连接串没指定characterEncodingutf8数据库接收到的就是乱码查询结果自然不对。这个坑不大但很影响心情。排查方法先看连接串确认有useUnicodetruecharacterEncodingutf8再确认数据库表、字段、连接三方的字符集一致。MySQL里可以执行show variables like %character%查看统统一配置。如果表是utf8mb4而连接用utf8部分表情字符会存不进去压测时插入操作会报错这个细节很容易被忽略。顺带提一句Jmeter那边的设置在jmeter.properties里确保sampler.default.encodingUTF-8的配置存在避免Jmeter发送SQL时的默认编码问题。7.4 关于could not delete existing file这类Windows环境问题有一些团队习惯在Windows本机跑Jmeter压测偶尔会遇到could not delete existing file之类的报错常见于Jmeter临时文件管理或者结果文件输出路径被设置成只读、被杀毒软件锁定。这个报错和JDBC Request本身关系不大但会让整轮压测中断。建议压测环境的Jmeter临时目录路径jmeter.properties里的jmeter.save.saveservice.output_format和user.dir相关配置指向一个SSD盘下的独立目录结果文件另存到同一台机器其他路径不要放在系统盘。如果杀毒软件在扫描时锁住了结果文件可以考虑把Jmeter安装目录加入白名单实测下来能少很多莫名其妙的bug。8. 进阶应用JDBC Request与自动化测试、CI/CD的整合思路8.1 在自动化回归里加入数据库校验性能压测之外JDBC Request在接口自动化回归里也是个好帮手。日常回归测试经常遇到一个痛点接口返回200但数据库里数据的状态、金额对不对不知道。现在很多团队用Python写自动化脚本直接在脚本里连数据库做断言这也是一种做法。但如果你已经有现成的Jmeter接口自动化脚本加一个JDBC Request跟在接口请求后面查询一下数据库里的对应记录配合断言比对结果就省掉了另一套连接数据库的代码。比如登录接口跑完后加一个JDBC Request查询该用户的最新登录时间断言时间是否落在一个合理区间。这样接口自动化和数据完整性校验就合并到一条链路里了排查问题时能直接给到开发一段完整的链数据比口述接口返回正常但不知道数据有没有写对强得多。8.2 性能回归测试的轻量落地很多中小团队没有专门的性能测试环境每次发布前做一轮完整的JMeter压测不现实。我的做法是维护一套轻量性能回归脚本场景只有两三个核心查询并发50持续3分钟。每次上线前在测试环境跑一轮对比上个月的TPS和RT基线。如果TPS下降超过20%或者RT翻倍基本上能推断最近这次变更对数据库层面有负面影响提前扼杀掉线上事故。这套脚本的关键是SQL不要写死用CSV参数化数据量最好保持一致——否则数据分析没意义。另外建议把压测结果自动汇总到指定的CSV文件配一个简单的趋势图或者直接人脸肉眼对比一周看一次成本极低但收益不小。8.3 关于Python和Jmeter怎么分工回到标题里的Python这个词我多说两句我和Python配合Jmeter做性能测试的感悟。不少人在网上找免费python源码或者想用Python完全替代Jmeter做性能测试查一下locust这类工具也能做压测。但我的经验是同一个项目里Jmeter和Python各干各的最佳分工方式是这样的Jmeter负责生成压力包括并发控制、定时调度、多线程组织。Python负责所有外围工作数据准备批量造数、结果文件解析Jmeter的CSV结果有特定的格式用pandas按需要聚合统计、辅助监控脚本定时采集数据库状态、以及基于CSV结果生成自定义的报告或者告警规则。很多python源码网站能找到现成的Jmeter结果分析脚本改一改字段名就能用比从零写方便很多。用Python解析Jmeter结果CSV时注意Jmeter的字段分隔符默认是逗号但如果响应内容里本身有逗号解析下来列数就对不齐。用开源库pandas读取时指定enginepython和quotechar通常能解决问题。实测下来pandas读一个几百MB的CSV结果文件也就十几秒统计TPS、RT分布、分位数这些比Jmeter自带的监听器导出还方便。9. 写到最后数据库压测最容易忽略的几件小事压测做完、报告写完之后有几件小事我想特别提一下因为这几件事我都吃过亏。第一压测前务必确认数据库的备份策略和监控告警是开启状态。数据库专项压测一定会制造高负载如果监控告警阈值设置得太低比如CPU超过70%就告警压测期间会收到大量告警运维团队可能会自动介入甚至限流压测就废了。正确的做法是压测前和运维确认告警窗口明确压测期间告警只记录不处理特殊情况电话联系。第二压测数据用完之后要处理干净。压测往库里插的测试数据如果不清理过几天就混在生产数据里了。我见过有同事压测插了一百万条测试订单数据后面开发调试查出来一堆脏数据排查了两个小时最后发现是压测残留。所以压测结束后的数据清理应该和压测执行一样写进流程用Python写清理脚本或者预留一批带标识的数据压测后按标识统一delete。第三报告里一定要写明压测环境和压测参数。不然三个月后你自己回来看这份报告都记不清当时用的是MySQL 5.7还是8.0、连接池多大、并发模型是阶梯式还是瞬间冲击。数据没有上下文等于没有数据。我会在报告开头固定加上环境版本、硬件配置、连接串参数、压测时长、并发模型这一页后面的数据图才有解释力。这几年做性能测试一个很大的体会是性能测试不仅仅是把工具跑起来更重要的是理解每个环节背后的为什么。JDBC Request本身只是一个组件但当你理解了它的连接池模型、参数语义、对数据库的压力传导方式再配合Python做数据准备和结果分析整个性能测试体系才算真正闭环。也希望这篇文章里踩过的坑你不需要再踩一遍。