使用JMeter进行SQL Server JDBC请求压测时遭遇外键引用问题
并发场景下JDBC事务父子表插入外键引用故障排查
问题概述
- 并发线程/用户数约30时触发故障,单线程/单用户测试无异常
- JDBC配置:
Auto Commit=false,事务隔离级别TRANSACTION_READ_UNCOMMITTED(符合内部应用规范) - 预期行为:父表与子表的插入操作包裹在单个事务中,实现原子提交
- 故障现象:事务内先插入父表记录,后续子表插入报外键引用错误;偶发能插入1-2条子表记录,但同外键的后续插入仍失败
- SSMS跟踪结果:压测期间用
READ_UNCOMMITTED隔离级别查询,刚插入后能查到父、子表对应记录,片刻后两条记录均消失
已完成排查动作
- 执行包含错误日志、异常、死锁图、锁获取/释放/超时的SQL跟踪,未发现根因
- 给JDBC采样器添加常量定时器(延迟父表或子表插入操作),故障仍复现
- 将JMeter生成的插入脚本复制到SSMS执行,父表+子表插入操作可成功完成
排查建议
1. 验证JMeter事务边界与连接复用逻辑
- 确认JMeter中事务控制器是否正确包裹父表、子表JDBC采样器,且未开启「独立事务」选项(开启该选项会导致父子操作分属不同事务)
- 检查JDBC连接池配置:
Max Connections是否小于并发数(30),若连接池不足会导致线程复用未提交的连接,破坏事务边界;同时确认Connection Validation Query配置正确,避免复用失效连接 - 查看JMeter日志,确认每个线程的JDBC连接是否为独立会话,是否存在跨线程的连接泄漏或复用情况
2. 检查事务提交/回滚逻辑
- 确认JDBC采样器的
Commit设置:若父表插入采样器单独设置了Commit,会导致父表插入后提前提交,后续子表插入属于新事务,此时若其他线程回滚同ID父表记录(或因并发ID冲突导致插入失败),会触发外键错误 - 在JMeter中添加
JDBC Request执行SELECT @@TRANCOUNT,分别在父表插入后、子表插入前、子表插入后查询事务计数,确认事务状态是否正常,排查是否存在未被日志捕获的隐式回滚
3. 排查ID生成与并发冲突
- 确认父表ID生成逻辑:若为客户端生成(如UUID、客户端获取自增序列),检查是否存在并发场景下ID重复的情况——比如多个线程生成相同
NEW_ID,导致一个线程插入父表后,另一个线程插入失败并回滚,连带已插入的子表记录回滚 - 在SQL跟踪中添加
SQL:BatchCompleted、RPC:Completed事件,过滤INSERT语句,查看是否有相同NEW_ID的多次插入请求,以及对应的事务提交/回滚状态
4. 验证JMeter线程与JDBC连接的绑定关系
- 若JDBC连接池
Max Connections小于并发数,线程会复用连接。如果事务控制器内的父表、子表采样器复用了不同连接(比如线程A先拿连接1插入父表,连接1被其他线程抢走后,线程A拿连接2插入子表),此时连接2看不到连接1未提交的父表记录,会直接触发外键引用错误 - 验证方法:在父表插入、子表插入的JDBC采样器中添加
SELECT @@SPID,将会话ID(SPID)写入测试结果,查看同一事务内的两次操作是否属于同一个SPID。若SPID不同,说明连接被复用,事务边界已被打破
5. 确认隔离级别实际生效与外键约束逻辑
- 在JMeter中添加JDBC采样器,执行
SELECT transaction_isolation_level FROM sys.dm_exec_sessions WHERE session_id = @@SPID,确认当前会话的隔离级别确实为READ UNCOMMITTED(对应数值1) - 注意:SQL Server的外键约束检查不依赖隔离级别,即使设置
READ UNCOMMITTED,子表插入时仍需父表记录已存在(同事务内的未提交记录本应可见,若不可见则大概率是连接复用导致的会话不一致)
内容的提问来源于stack exchange,提问作者Dilip Senapati
相关产品推荐
相关产品推荐

