数据库性能测试脚本运行报错不管你用的是 JMeter、Python、Java(如结合 TestNG)编写的脚本,这套思路都适用。
错误信息快速定位
报错信息是调试的第一切入点。拿到报错后先做三件事:
看清错误的堆栈:尤其注意最底层的Caused by,那往往是根本原因。
区分是客户端报错还是数据库返回的错误:如 CommunicationsException 是客户端连接问题,而 DeadlockLoserDataAccessException 则表示数据库端发生死锁。
记录报错前的执行上下文:包括执行的 SQL、输入参数、并发线程数、当前压测不断时间等,这对复现问题非常重要。
按常见报错类别逐一排查
1. 连接错误
典型报错:Connection refused、No suitable driver found、Access denied for user、Communications link failure
排查清单:
检查数据库URL、端口、服务名/数据库名是不是拼写正确。
确定用户名和密码无误,且用户有对应数据库的访问权限。
检查防火墙/安全组是不是允许压测客户端 IP 访问数据库端口。
确定数据库驱动是不是存在版本不一致(如 MySQL 8.0 使用了老版驱动)。
如果使用连接池(HikariCP、Druid 等),检查maximumPoolSize是不是设置过低,导致资源耗尽后新请求无法获取连接。
测试网络连通性:在压测客户端机器上执行 telnet <db_host> <db_port> 或 nc -vz,排除网络层问题。
2. SQL执行错误
报错:语法错误、字段不存在、类型不一致、主键/唯一键冲突、外键约束违反
排查清单:
将报错的 SQL 拷贝出来,用数据库客户端工具(如 Navicat、DBeaver)手动执行,确定 SQL 本身是不是正确。
检查参数绑定:脚本中是不是使用了预编译语句,参数类型和数据库字段是不是一致(如字符串用单引号,日期格式正确)。
注意DDL和DML 的兼容性:某些高级SQL特性(如窗口函数、CTE)在旧版本MySQL/PostgreSQL中可能不支持。
对于数据冲突错误(唯一键重复),判断是测试数据准备问题,还是并发导致竞态条件,需要优化脚本或 SQL 思路(如 INSERT ... ON DUPLICATE KEY UPDATE)。
3. 超时和锁相关错误
典型报错:Lock wait timeout exceeded、Deadlock found when trying to get lock、Query execution was interrupted、SocketTimeoutException
排查清单:
针对事务持有锁时间过长:
检查脚本中是不是在事务内进行了非数据库操作(如 HTTP 调用、文件读写),应移出事务。
确定索引是不是合理:缺少索引会导致扫描行数过多,锁范围扩大。
死锁:
查看数据库的死锁日志(MySQL 用 SHOW ENGINE INNODB STATUS,PostgreSQL 查 pg_locks 视图),分析发生死锁的 SQL 和锁请求顺序。
在脚本中统一资源访问顺序,避免交叉锁。
超时设置过短:
检查数据库侧的 lock_wait_timeout(MySQL)或 statement_timeout(PG);
检查客户端侧的 connectTimeout、socketTimeout、queryTimeout,性能测试情形下可能需要适当调大这些值,但需权衡真实业务要求。
4. 资源短板引发的错误
典型现象:连接数满、OOM(内存溢出)、CPU 100%、磁盘 IO 高、长事务回滚、响应时间急剧上升
排查清单:
数据库连接数:
max_connections 是不是已达到上限?检查当前连接数 SHOW PROCESSLIST 或 pg_stat_activity。
确定脚本是不是正确释放连接(TestNG 的 @AfterMethod 中关闭连接;Python 用 try-finally)。
内存:
数据库服务器内存是不是分配合理(如 MySQL 的 innodb_buffer_pool_size,PG 的 shared_buffers)。
压测客户端本身可能因持有大量结果集而 OOM,如果是,改用流式查询或分页。
CPU和IO:
通过 top、iostat 观察压测过程中资源利用情况。如果是CPU短板,优先优化慢SQL(见下一步)。
如果磁盘IO高,检查是不是有未命中索引的全表扫描,或temp table的频繁写入。
5. 脚本并发问题
典型报错:空指针、数组越界、变量被包括、数据污染导致断言失败
排查清单(如果使用 TestNG,可结合其特性调试):
线程安全问题:确定共享变量是不是做了并发控制;避免多个线程同时修改同一份基础数据,导致测试互相干扰。
数据隔离:每个并发线程是不是使用独立的测试数据?可以用DataProvider为不同线程提供不同参数组合。
依赖顺序:如果测试方法间有依赖,检查dependsOnMethods或 groups配置是不是正确。并行方式下可能因依赖方法未完成而导致报错。
监听器辅助调试:实现 ITestListener的onTestFailure方法,在测试失败时自动记录当前线程的SQL、参数和响应时间,方便还原现场。
调试技巧
1. 从单开始,逐步放大
先单线程、单次执行 脚本,保证功能正确。
再少量并发(如5线程) 测试事务和锁。
逐步加量,找到性能和错误的临界点。
2. 启用全量日志
在测试脚本中添加详细的执行前/后日志,打印 SQL、参数和耗时。
如果使用的是 MyBatis/Hibernate,将 org.apache.ibatis 或 org.hibernate.SQL 日志级别设为 DEBUG,可以看到实际执行的 SQL 和绑定值。
数据库侧开启 慢查询日志(MySQL long_query_time=1),并设置 log_queries_not_using_indexes,捕获全表扫描。
3. 使用执行计划分析慢 SQL
将高耗时 SQL 拿出来,加上 EXPLAIN(或 EXPLAIN ANALYZE)查看访问途径:是不是走索引、扫描行数、临时表使用等。
对于瞬时慢的查询,注意性能抖动,可能是统计信息过旧,执行计划不优,可尝试 ANALYZE TABLE。
4. 利用数据库实时诊断
MySQL:SHOW FULL PROCESSLIST 或 SELECT * FROM information_schema.innodb_trx 查看正在运行的事务和锁信息。
PostgreSQL:SELECT * FROM pg_stat_activity,结合 pg_locks 和 pg_blocking_pids 定位阻塞源头。
使用图形化监控工具(如 Percona PMM、pgHero)直观看到负载、锁等待和慢查询。
5. 单元化拆解和Mock
如果脚本混合了多种数据库操作,将疑似有问题的 SQL 隔离成一个独立小脚本,针对性压测。
对于外部依赖(如 Redis、消息队列),暂时mock掉,排除干扰。