ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

JMeter压测数据库全指南:JDBC配置、参数化与并发实战

JMeter压测数据库全指南:JDBC配置、参数化与并发实战 1. 先想清楚压数据库和压接口到底差在哪很多人用 JMeter 做接口测试、压 Web 服务驾轻就熟。但一说到用 JMeter 直接压数据库不少人就懵了——数据库也能用 JMeter 压怎么连连上了能测啥先说结论不仅能压而且非常好使。我在实际项目里既用 JMeter 压过 MySQL、PostgreSQL也压过 Oracle甚至给第三方数据中台做过压测靠的都是 JMeter 自带的 JDBC 请求组件不需要装任何额外插件。但有一个认知必须先纠正用 JMeter 测数据库不等于在数据库客户端里手动执行几条 SQL 看返回时间。它做的是“模拟真实业务场景下大量并发请求同时打向数据库”这件事——比如 100 个用户同时登录每个登录动作背后对应着几条 SQL这些 SQL 同时打到数据库上数据库扛不扛得住平均响应时间多少有没有慢查询连接池会不会被打满这才是压数据库的核心目的。所以这篇文章我不会只讲“怎么连上数据库”而是把 JMeter 测试数据库的完整方法拆开揉碎环境准备、JDBC 连接配置、JDBC Request 详解、参数化传值、断言校验、并发场景搭建、常见问题排查一条龙讲清楚。你照着做基本可以覆盖日常工作中 90% 的数据库压测需求。适合谁看刚接触 JMeter、想用它做数据库压测的测试新人被领导临时派活、需要在两天内给出数据库性能结论的测试工程师以及那些已经会基本接口压测、但没系统搞过 JDBC 请求的老手。看完你至少能独立搭出一套带参数化、带断言、带并发模型的数据库压测脚本。2. 环境准备驱动、版本、连接方式一步都不能错2.1 先确认 JMeter 版本和 JDBC 驱动开始之前先确认你的 JMeter 版本。我这边用的是 JMeter 5.xJDBC 请求相关的组件从 3.x 就有界面和配置项基本一致所以你用 4.x 或 5.x 都行不用太纠结版本。关键在于JDBC 驱动 Jar 包。JMeter 本身不自带数据库驱动你必须手动把对应数据库的驱动 Jar 包放到 JMeter 的lib目录下然后重启 JMeter 才能生效。常见数据库对应的驱动 Jar 包MySQLmysql-connector-java-8.0.x.jar如果你连的是 MySQL 5.x建议用 5.1.4x 版本MySQL 8.x 一定要用 8.0 版本的驱动不然连接会报错。PostgreSQLpostgresql-42.x.x.jarOracleojdbc8.jar或ojdbc11.jar看你数据库版本SQL Servermssql-jdbc-9.x.x.jar驱动包放好之后重启 JMeter然后用一个最简单的 JDBC 请求验证一下能不能连上。别一上来就搞复杂场景连接都不通的话后面全是白搭。2.2 JDBC 驱动类名和连接 URL别记混了很多新手死在第一步就是驱动类名和 URL 写错。这里直接给你一份对照表抄作业就行数据库JDBC Driver ClassJDBC URL 格式MySQLcom.mysql.jdbc.Driver5.x/com.mysql.cj.jdbc.Driver8.xjdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiPostgreSQLorg.postgresql.Driverjdbc:postgresql://127.0.0.1:5432/testdbOracleoracle.jdbc.OracleDriverjdbc:oracle:thin:127.0.0.1:1521/ORCLSQL Servercom.microsoft.sqlserver.jdbc.SQLServerDriverjdbc:sqlserver://127.0.0.1:1433;DatabaseNametestdb注意 MySQL 这里有两个坑第一MySQL 5.x 和 8.x 的 Driver Class 不一样。8.x 是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver写错直接报ClassNotFoundException。第二MySQL 8.x 连接 URL 里建议加上useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai。不加 serverTimezone 会报时区错误不加 allowPublicKeyRetrieval 会在某些认证方式下报Public Key Retrieval is not allowed。这两个参数我每次都会带上省得排查半天。经验我一般会在连接 URL 里额外加一个connectTimeout5000socketTimeout10000避免数据库挂了之后线程一直卡在等待上压测时能很快暴露连接超时问题。比如jdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiconnectTimeout5000socketTimeout100002.3 环境验证用一个最简单的查询打通链路驱动放好、配置想清楚了先做个连通性测试——在 JMeter 里建一个测试计划加一个 JDBC Connection Configuration再选一个库建一个线程池跑一条SELECT 1如果返回正常链路就通了。这一步别看简单它价值很大能一次性排除驱动、URL、账号密码、网络四个环节的问题。如果SELECT 1都跑不通那后面再复杂的脚本也跑不通不如先在这里把问题解决掉。3. JDBC Connection Configuration连接池配置才是压测的关键3.1 配置项逐行拆解JDBC Connection Configuration 是 JDBC 请求的前置条件相当于给所有 JDBC 请求提供数据库连接池。它不是一个“填个 URL 就行”的简单配置每个选项背后都有讲究。关键的几个配置项Variable Name for created pool连接池变量名随便起比如db_pool但你要记死它因为 JDBC Request 里要通过这个名字来引用这个连接池。一个测试计划里可以配置多个连接池给不同库用靠名字区分。Max Number of Connections最大连接数。这个值很关键它决定了 JMeter 能同时从池里拿多少个连接。如果你压 100 个并发但最大连接数只有 10那 JMeter 的请求就会排队等连接测出来的结果不是数据库的真实瓶颈而是连接池的瓶颈。Max Wait最大等待时间单位毫秒。连接池没空闲连接时请求最多等多久超时抛异常。一般设 10000。Time Between Eviction Runs清理线程运行间隔。JMeter 会定期清理空闲过久的连接一般设 60000。Auto Commit自动提交。只做查询压测时设 True 就好涉及事务、插入、更新时按需改成 False。Transaction Isolation事务隔离级别默认 DEFAULT 即可不用特别改。Pool Validation Query连接有效性检查 SQL。MySQL 下写SELECT 1PostgreSQL 下写SELECT 1Oracle 下写SELECT 1 FROM DUAL。JMeter 每次从池里拿连接时可以用这个 SQL 验证连接是否还活着避免拿到失效连接。3.2 连接池参数怎么定一个简单的估算方法连接池最大连接数怎么定很多教程会说“设大一点就行”但实际不是这么回事。连接数设太大数据库可能会因为并发连接过多直接拒绝服务设太小JMeter 这边又会排队。我的做法是连接池最大连接数 目标并发线程数。比如你要模拟 100 个用户并发那么 Max Number of Connections 就设 100 或略大于 100比如 100。这样每个线程拿到一个独立连接不会出现两个线程抢一个连接的情况。这里有个容易混淆的细节JMeter 的线程数和连接池连接数不是一一绑定的关系。线程可以复用连接但如果你想测的是“数据库在 100 个并发连接下的表现”那连接数就必须给够否则测出来的曲线是连接池排队曲线不是数据库性能曲线。3.3 为什么要用连接池而不是每个线程单独连JMeter 里还有一种做法是每个线程自己创建数据库连接不用连接池——比如用 BeanShell 或者 JSR223 写代码去DriverManager.getConnection()。我不推荐这种方式做压测原因有两个第一性能差。每次请求都重新创建连接开销全花在 TCP 握手和认证上测出来的响应时间虚高。第二资源不可控。并发 100 个线程同时创建 100 个连接压测机本身的文件描述符、内存压力都会变大干扰结果。所以标准做法就是一个 JDBC Connection Configuration 管连接池N 个 JDBC Request 从池里取连接执行 SQL。连接池的创建、复用、回收JMeter 都在底层帮你处理好了你要做的就是配置好那几项参数。4. JDBC Request 详解增删改查都能压4.1 SQL 查询和变量名规则JDBC Request 是真正执行 SQL 的地方。它长得很像一个 HTTP 请求 Sample但参数完全不同。核心参数有三个Variable Name of Pool declared in JDBC Connection Configuration填连接池变量名也就是你在 JDBC Connection Configuration 里填的那个名字。如果这里填错JMeter 会直接报错找不到连接池。Query TypeSQL 类型有 Select Statement、Update Statement、Callable Statement、Prepared Select Statement、Prepared Update Statement 等。如果是普通查询用 Select Statement如果 SQL 里有占位符?必须用 Prepared Select Statement 或 Prepared Update Statement。QuerySQL 语句本体。这里有个新手高频疑问Query Type 选 Select Statement 和 Prepared Select Statement 有什么区别区别在占位符。如果你要执行SELECT * FROM user WHERE id ?就必须用 Prepared Select Statement然后通过参数列表传入?的值。如果你执行的是完整 SQL没有?用 Select Statement 就行。相同点是结果都存放在变量里供后续使用。4.2 查询结果的三种用法JDBC Request 执行完之后结果存在哪答案是JMeter 会把结果集存到变量里变量名规则是你在 JDBC Request 里填的Variable Name。比如你在 JDBC Request 的 Variable Name 填了mysql_result那么执行完查询后mysql_result整个结果集mysql_result_#结果集行数比如返回 10 行这个变量值就是 10mysql_result_1第一行数据mysql_result_1_id第一行的 id 字段值mysql_result_2_id第二行的 id 字段值用Variable Name 下划线 行号 下划线 列名这个规则就能取到任意一行任意一列的值。这个特性相当有用。最常见的场景是你从表 A 查到一批订单 ID然后要把这些 ID 作为参数传给下一个 JDBC Request 或 HTTP 请求。具体做法就是先执行一条查询 SQL然后在下一个请求里用${mysql_result_1_id}来引用第一行 ID。4.3 实际案例查询用户表数据我来演示一个最简单的例子。假设数据库里有一张user表字段有id、username、age我想压测一条查询 SQLSELECT id, username, age FROM user WHERE age 20配置如下JDBC Request Variable Nameuser_resultQuery TypeSelect StatementQuerySELECT id, username, age FROM user WHERE age 20跑完之后在察看结果树里可以看到这个 JDBC Request 的响应数据里面是查到的所有行。如果你想把这批数据取出来给后续请求用比如遍历前 10 个用户 ID就可以用${user_result_1_id}、${user_result_2_id}这种形式去引用。注意结果集会按位置存储mysql_result_1是第 1 行mysql_result_2是第 2 行以此类推。如果你查询结果本身有排序需求在 SQL 里把ORDER BY写好否则取出来的行顺序可能不稳定。4.4 执行更新操作怎么配置压测不光是查询有时也要模拟写入、更新的业务压力。比如 100 个用户同时更新自己的资料。如果是带参数的更新比如UPDATE user SET age ? WHERE id ?那么 Query Type 要选Prepared Update StatementSQL 里的?是占位符需要通过“Parameter values”和“Parameter types”传入实际值。这里有个重要细节执行 Update/Insert/Delete 操作时如果数据库没有返回结果集JMeter 会返回受影响的行数。比如某个 UPDATE 影响了 3 行JMeter 的响应数据里会显示3。如果你想验证更新是否成功一种做法是更新后再执行一条 SELECT 去查这条数据配合断言来校验。5. 参数化取值让压测数据动起来5.1 CSVDataset 参数化每个线程分块取值做数据库压测时你常常需要模拟“不同的用户操作不同的数据”而不是 100 个用户都查同一条记录。前者才是真实业务场景也能更有效地压出数据库的查询能力。JMeter 里最常用的参数化方式就是CSV 数据集配置CSV Data Set Config。它允许你从一个 CSV 文件里按行读取数据然后把每一列的值赋给变量。配置的时候有几点要注意FilenameCSV 文件路径Variable Names列名用英文逗号分隔比如id,username,ageDelimiter分隔符默认是英文逗号Recycle on EOF读完最后一行后是否回到开头继续读。如果你压测时长较长或者并发数大于 CSV 行数建议设 True否则读到末尾线程会直接结束或报错。Stop thread on EOF读完最后一行是否停止线程。如果 Recycle on EOF 是 True这个就设 False。Sharing mode共享模式。默认 All threads 是所有线程共享一个文件指针按顺序往下读如果你想每个线程各读各的可以选 Current thread。我在实际项目里压“100 并发用户登录”时会准备一个 100 行的 CSV每行一个用户 ID 和密码然后把线程组并发数设为 100CSV 共享模式用默认的 All threads这样每个线程拿到一个不同的用户数据谁也不会抢。5.2 从上一个查询结果取参JDBC 请求结果复用CSV 适合数据预先准备好的场景但有些场景数据是动态生成的——你先要查一下有哪些订单然后针对每个订单做后续操作。这种时候就要用 JDBC Request 的查询结果来做参数化。思路是先跑一条 SELECT把结果存到一个变量里然后在后一个请求里用${var_行号_列名}去引用。举个例子第一步JDBC Request 查订单 IDSELECT order_id, amount FROM orders WHERE status pending这个 JDBC Request 的 Variable Name 设为order_result。第二步在 HTTP 请求或下一个 JDBC Request 里引用订单 ID${order_result_1_order_id} 订单金额${order_result_1_amount}这样就实现了“把数据库查出来的数据作为下一个接口的参数”这个高频需求。但如果查询返回了多行而你希望循环处理每一行数据那还是建议配合ForEach 控制器来做。ForEach 控制器可以遍历一个变量集合比如order_result下的所有行然后逐一执行后续请求。配置时输入变量前缀order_result循环变量名随便取一个比如current_order然后在后续请求里用${current_order_order_id}就能依次取到每一行的数据。5.3 JDBC 请求结果里的特殊变量行数和 NULL使用查询结果时有两个场景很容易踩坑。第一空结果集。如果 SQL 查不到数据order_result_#会是 0此时不能引用order_result_1_order_id否则 JMeter 会输出一个空值。为了不报错可以搭配If 控制器判断order_result_#是否大于 0再执行后续逻辑。第二字段值本身是 NULL。比如某个字段在某些行里是 NULL引用之后得到的字符串是null而不是空字符串。如果你的下游接口对参数格式有严格要求需要在 BeanShell 或 JSR223 里做一个 null 值替换处理否则接口调用可能会因为多传了null字符串而报错。5.4 直接用 JSR223 脚本搞定更复杂的数据加工如果上面的方式都满足不了需求比如你要对查出来的数据进行 MD5 加密后作为参数传给接口那就得上 JSR223 脚本了。JSR223 Sampler 里可以用 Groovy 直接查数据库处理结果集生成任意格式的字符串。Groovy 的语法比 BeanShell 简洁太多性能也更好是 JMeter 官方推荐的方式。虽然这篇文章主要讲 JDBC Request 这种图形化配置但在自定义逻辑非常复杂的时候JSR223 是终极兜底方案。简单示例import java.sql.* // 获取连接池里的连接 def conn vars.get(db_pool) // 这只是示意 // 真实的连接获取可以通过 DataSource 或者直接用 JDBC 驱动获取这里省略不过说实话只有你需要写复杂逻辑时才用 JSR223一般情况下 JDBC Request CSV 已经能覆盖绝大多数场景。过度使用 JSR223 反而会让脚本难以维护。6. 断言校验怎么证明数据库返回是对的6.1 响应断言最快最直接的校验方式压数据库不是把 SQL 跑完就完事了你得确认返回内容是符合预期的。比如你查询age 20的用户结果返回里至少有数据行而不是空结果集或者一堆错误信息。JMeter 的响应断言可以加到 JDBC Request 上用来校验响应内容。配置方式很简单在 JDBC Request 上右键 → Add → Assertions → Response AssertionField to Test选 Text ResponsePattern Matching Rules选 ContainsPatterns to Test填你要校验的关键字比如查询user表你想确认结果里有username这个字段名就可以在响应断言里填username。如果响应里没有这个关键字断言就会失败在聚合报告里会看到错误率上升。这里有个技巧有时候 SQL 查出来的数据量很大响应内容很长你没法直观地看到是否有数据这时可以直接用响应断言校验 “返回行数不为 0”。具体做法是把 SQL 改成SELECT COUNT(*) AS cnt FROM user WHERE age 20然后断言响应里包含cnt10或某个具体数字。注意JMeter 响应里 COUNT 查询的显示格式是类似cnt10这样的配合 Contains 断言就能判断数据量是否符合预期。6.2 BeanShell 断言处理复杂校验逻辑响应断言只能做简单匹配如果你要做复杂判断比如“查询结果里最大金额大于 1000”或“返回行数在某个区间内”就得用 BeanShell 断言或者 JSR223 断言。JSR223 断言可以用 Groovy 写逻辑清晰多了。比如判断结果集行数def rowCount vars.get(user_result_#).toInteger() if (rowCount 10) { // 失败手动设置断言结果 AssertionResult.setFailure(true) AssertionResult.setFailureMessage(查询结果集行数小于10实际为: rowCount) }这段脚本的逻辑是从 JMeter 变量里取user_result_#的值转成整数判断是否小于 10如果小于就手动标记断言失败并输出失败原因。这样在聚合报告或察看结果树里你能很直观地看到哪些请求没通过校验。经验断言别加太多加的每一个断言都会消耗额外的性能。压测脚本里的断言越精简越好能用一个响应断言解决的就不要加脚本断言。高并发压测时过重的断言会拉高 JMeter 本身的 CPU 开销影响测试数据准确性。6.3 结合“用结果树看数据”排查问题压测跑完后第一件事永远是看察看结果树View Results Tree。在结果树里每个 JDBC Request 都能展开看它的请求数据、响应数据、断言结果。响应数据里会显示 SQL 执行后返回的内容格式。我遇到过很多测试小白写完了脚本直接开压压完发现错误率 100%然后开始怀疑数据库出问题了。其实根本不是数据库的问题而是 SQL 写错了或者参数没传进去。这种时候不用慌打开察看结果树找一个失败的请求看它的响应数据里的报错信息十有八九一次就能定位到问题。7. 并发压测实战模拟 100 个用户同时查库7.1 线程组参数设计前面把 JDBC Request 和参数化都讲完了接下来进入实战模式——模拟 100 个用户同时访问数据库看数据库的表现。线程组配置建议Number of Threads (users)100Ramp-Up Period (seconds)10Loop Count50 或者填 ∞根据你的压测时长来定这里解释一下 Ramp-Up Period 的作用它表示 100 个线程在 10 秒内全部启动完毕也就是说每秒启动 10 个线程。这样做是为了模拟用户逐步进入系统的真实场景而不是 100 个请求在同一毫秒内全部打到数据库。如果你希望一开始就是满负载可以把 Ramp-Up 设成 1 秒如果你要做阶梯压测可以用 Stepping Thread Group 插件但这个插件需要额外安装基础场景下普通线程组就够用了。Loop Count 表示每个线程执行多少次查询。假如一个 JDBC Request 执行一条 SQL100 个线程 × 循环 50 次 总共 5000 次 SQL 查询。这个总数可以用来估算压测时长和数据库负载。7.2 实战脚本结构一个标准的数据库压测脚本结构如下测试计划线程组100 线程10 秒内启动JDBC Connection Configuration连接池配置最大连接数 100CSV Data Set Config用户数据参数化JDBC Request执行 SQL响应断言校验返回结果监听器聚合报告Aggregate Report察看结果树View Results Tree需要注意JDBC Connection Configuration 和 CSV Data Set Config 都是配置元件Config Element它们的作用范围是“所在层级之下的所有请求”。放在线程组下就对该线程组里的所有 JDBC Request 生效。7.3 压测过程中的实时监控压测跑起来之后除了看 JMeter 的聚合报告我强烈建议你同时监控数据库端的指标。光看 JMeter 的响应时间是不够的你还需要知道数据库的 CPU、内存、连接数、慢查询数等指标才能完整评估数据库性能。具体做法如果是 MySQL可以在压测时执行SHOW PROCESSLIST;查看当前所有连接和正在执行的 SQL。查看数据库的 CPU 和内存占用比如用top或数据库自带的性能监控工具。开启慢查询日志压测完后分析哪些 SQL 的执行时间超过了阈值。这一步非常重要因为 JMeter 的聚合报告只能告诉你“响应时间变慢了”但到底慢在数据库的哪个环节——是 CPU 打满了、还是锁等待、还是磁盘 IO——必须结合数据库端的指标来看。7.4 聚合报告怎么解读压测结束看聚合报告的几个核心指标Samples总请求数Average平均响应时间单位毫秒Min / Max最小/最大响应时间Std. Dev.响应时间标准差越大说明响应时间波动越明显性能越不稳定Error %错误率正常压测场景下应为 0%有少量错误要具体分析Throughput吞吐量单位通常是req/sec意思是每秒能处理多少请求举个例子100 个线程、循环 50 次的脚本跑完Samples 是 5000Throughput 如果是 200/sec说明数据库每秒能扛住 200 次这个 SQL 查询。如果你把循环次数加大Throughput 会逐步逼近数据库的极限值这个极限值就是数据库的处理能力上限。经验压测数据库时平均响应时间和吞吐量要一起看。如果平均响应时间很低但吞吐量也低说明 JMeter 本身或网络成了瓶颈不是数据库不行如果吞吐量上去了但平均响应时间也跟着飙升这才说明数据库开始进入高负载状态。8. 常见问题与排查技巧实录8.1 连接不通ClassNotFoundException 和 URL 报错问题现象JDBC Request 报ClassNotFoundException: com.mysql.jdbc.Driver或Cannot create PoolableConnectionFactory。排查思路先看驱动 Jar 包有没有放到 JMeter 的 lib 目录下然后看是否重启了 JMeter。如果确定 Jar 包在再看 Driver Class 写对没有——MySQL 8.x 驱动对应的是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver。如果报错信息是Unknown database xxx检查连接 URL 里的数据库名是否正确。如果是Access denied for user检查账号密码和数据库权限。8.2 连接池不够用Too many connections问题现象压测过程中JMeter 报Too many connections或者数据库端出现Connection refused。原因分析这个报错有两个方向一个是 JMeter 连接池最大连接数设得太大打爆了数据库的最大连接数另一个是数据库自身max_connections配置太小。解决方案先查看数据库的max_connections比如 MySQL 可以用SHOW VARIABLES LIKE max_connections;查看。然后调整 JMeter 连接池的最大连接数让它的上限略低于数据库的max_connections留一些余量给其他应用连接。顺便说一句做压测之前一定要和 DBA 或运维确认好数据库的连接上限否则压到一半数据库直接把连接全踢掉测试结果就废了。8.3 响应时间异常长慢 SQL 还是连接池排队问题现象聚合报告显示平均响应时间高达几秒远大于你手动执行 SQL 的时间。原因分析首先要区分是数据库真慢还是连接池在排队。手动执行 SQL 快说明 SQL 本身没问题。这时候要去看 JMeter 连接池的最大连接数是不是小于线程数——如果 100 个线程去抢 20 个连接剩下 80 个线程全在等连接响应时间自然飙升。解决方案把 Max Number of Connections 调大使其等于或大于线程数再跑一次对比。如果响应时间正常了说明之前的瓶颈是连接池如果响应时间依然很大那就要去数据库端看慢查询日志定位是不是索引失效、锁等待等问题。还有一个细节如果连接 URL 设置了socketTimeout当数据库处理查询超过这个值时JMeter 会直接报 socket 超时。这种情况下不是数据库变慢而是策略性地断开了连接。压测时可以把 socketTimeout 调大一点避免误判。8.4 变量引用出来是 null结果集行号与字段大小写问题现象在后续请求里用${var_1_id}引用 JDBC 查询结果发现值是 null 或者空字符串。原因排查第一先看结果集是不是空的。如果 SQL 查不出来数据任何引用都是空。用察看结果树看响应数据确认是否有数据返回。第二看字段名大小写。MySQL 的字段名默认全小写如果 SQL 里写了别名比如SELECT id AS userId FROM user那么引用时要写成${var_1_userId}。建议 SQL 里起别名时统一用带下划线的命名如user_id然后引用时保持大小写一致。第三行号从 1 开始不是从 0 开始。如果变量名是order_result那么第一行是order_result_1没有order_result_0这个变量。8.5 中文乱码与编码问题问题现象JDBC 查询返回的中文变成问号或乱码。原因排查大多是连接 URL 里没有指定字符集。在 MySQL 连接 URL 上加上characterEncodingutf8jdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaicharacterEncodingutf8另外检查数据库表本身的字符集是不是 utf8mb4。如果表是 latin1 而应用按 utf8 读取也会乱码。8.6 一个容易被忽略的问题压测机性能不够最后提一个很多人踩过的坑——压测机本身的性能不够。JMeter 是 Java 应用100 个并发线程跑起来时压测机 CPU 和内存消耗不小。如果你在本地笔记本上压远程数据库压测结果可能不是你 SQL 的真实性能而是笔记本 CPU 打满后的结果。怎么判断压测时打开任务管理器或top看看 Java 进程的 CPU 占用率。如果已经超过了机器 CPU 的 70% 以上建议换一台性能更强的机器或者把 JMeter 部署到另一台服务器上尽量让压测机资源充足确保瓶颈在数据库端而不是客户端。8.7 问题排查速查表问题现象可能原因解决方案ClassNotFoundException驱动 Jar 未放到 lib 目录放入 Jar 包并重启 JMeterCannot create PoolableConnectionFactoryURL 或账号密码错误检查 URL 格式、账号权限Too many connections连接数超过数据库上限调大数据库 max_connections 或调小 JMeter 连接池响应时间超长连接池排队或慢 SQL调大连接池查看慢查询日志变量值为 null结果集为空或字段名大小写不一致确认查询结果检查 SQL 别名中文乱码连接 URL 未指定字符集添加 characterEncodingutf8压测结果不稳定压测机资源不足更换压测机或降低线程数9. 一个完整的数据库压测脚本示例讲了这么多最后给你一个完整的 MySQL 压测脚本搭建过程从零到一可以直接照抄。9.1 测试场景模拟 100 个用户并发查询订单表每个查询根据不同的订单 ID 查询订单信息持续压测 10 分钟观察数据库的吞吐量和响应时间变化。9.2 准备工作MySQL 数据库提前创建好订单表orders字段包括order_id、user_id、amount、status准备一个order_ids.csv文件里面放 100 个订单 ID用于参数化JMeter 5.xmysql-connector-java-8.0.x.jar已放入 lib 目录并重启9.3 脚本搭建步骤第一步创建测试计划并添加线程组线程数 100Ramp-Up 10 秒循环次数勾选无限在调度器配置里设置持续时间为 600 秒。第二步添加 JDBC Connection Configuration配置项如下Variable Name for created poolmysql_poolMax Number of Connections100Max Wait10000Auto CommitTruePool Validation QuerySELECT 1Database Connection ConfigurationDatabase URLjdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiJDBC Driver Classcom.mysql.cj.jdbc.DriverUsernametest_userPasswordyour_password第三步添加 CSV Data Set ConfigFilename/data/order_ids.csvVariable Namesorder_idDelimiter,Recycle on EOFTrueStop thread on EOFFalseSharing modeAll threads第四步添加 JDBC RequestVariable Name of Poolmysql_poolQuery TypePrepared Select StatementQuerySELECT * FROM orders WHERE order_id ?Parameter values${order_id}Parameter typesVARCHAR这里说明一下因为 SQL 里有?占位符所以 Query Type 必须选 Prepared Select StatementParameter values 里填 CSV 读取出来的订单 IDParameter types 填数据库字段类型。第五步添加响应断言Field to TestText ResponsePattern Matching RulesContainsPatterns to Testorder_id这样就确保了查询请求返回的结果中包含 order_id 字段证明查询是成功的。第六步添加监听器加一个聚合报告和一个察看结果树。压测结束后聚合报告里可以看到平均响应时间、吞吐量、错误率等核心数据。9.4 跑完怎么分析压测结束后我一般这么看数据先看错误率。如果错误率为 0%说明 100 并发下这条 SQL 没有出错数据库连接和查询都正常。如果错误率大于 0查询具体的错误信息常见的是连接池不够用或超时。然后看吞吐量变化。如果 JMeter 的 Throughput 在持续稳定上升后趋于平缓说明已经摸到了数据库的处理上限。此时如果平均响应时间还能接受说明数据库还有冗余如果响应时间已经超标比如超过 500ms说明数据库快要撑不住了。最后结合数据库端监控比如 CPU、慢查询日志看瓶颈到底在哪个环节然后给出测试结论和优化建议。这里有一个小建议压测时先把线程数保守一点比如 50跑一轮看结果再逐步升到 100、200。这样做的好处是你能看到数据库在不同并发下的表现曲线而不是一上来就压到天花板拿到一堆根本没法分析的数据。阶梯式加压是我在实际项目中比较喜欢用的方式。最后说几句实在话用 JMeter 做数据库压测门槛真的不高但想做好需要你对数据库本身的运行机制有基本认知。我在实际测试项目里见过不少人把压测脚本配好了、跑出了漂亮的报告但一问数据库端的指标完全答不上来——CPU 多少、内存多少、慢查询有没有、连接数有没有到上限全是空白。这种压测报告说实话价值有限。我的个人建议是压测数据库永远要把 JMeter 端的数据和数据库端的数据结合起来看。JMeter 告诉你发生了什么数据库的监控告诉你为什么会发生。两边对照才能真正判断数据库的性能瓶颈在哪里也才能给出可信的测试结论。如果你只是刚接触这块先从最简单的单条 SQL 查询压测入手把 JDBC Connection Configuration 和 JDBC Request 玩熟再慢慢加参数化、加断言、加并发场景。不要一上来就想测多表关联或存储过程底子打好后面的内容都是水到渠成的事。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表