You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多用户通过BeanShell采样器JDBC运行Oracle相同循环报错如何解决

问题根因

  • 你代码中插入的主键ID硬编码为固定范围11~12,所有并发线程都会插入相同ID值,触发主键唯一约束冲突,这是报错的核心原因。
  • 你设置的全局删记录逻辑没有线程隔离,多线程并发执行时,删除和插入操作时序混乱,可能出现A线程刚插入数据就被B线程删除,或者多个线程同时插入同ID的情况。

调整方案

1. 替换固定主键为线程唯一取值,彻底避免ID重复

两种可选实现方式:

  • 方式1:通过JMeter内置变量生成线程唯一ID(无需改库)
    用JMeter内置对象ctx获取当前线程编号,给每个线程分配独立的ID区间,比如每个线程插2条数据,就把ID的计算逻辑改为:线程编号*100 + 偏移量,修改后的脚本如下:
    import java.sql.*;
    import oracle.jdbc.*;
    import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
    
    ResultSet rs = null;
    ResultSetMetaData rsmd = null;
    PreparedStatement pstmt = null;
    // 获取当前线程号,threadNum从0开始所以+1避免0值
    int threadNum = ctx.getThreadNum() + 1;
    int startId = threadNum * 100 + 11;
    int endId = startId + 1;
    
    Connection conn = DataSourceElement.getConnection("IMPACTConnectionPrerequisitsII");
    String Query = "DECLARE \n" 
               + "j number(4) \n"
               + "BEGIN \n"
               + "FOR j in " + startId + " .. " + endId + " LOOP \n" // 替换原固定11..12
               + "INSERT INTO FWA_STAGE_TU \n"
               + "(ID,EXT_ID,BATCH_NO,OPERATION,BLOCK_ROLL_UP_LAST_RECORD,LAST_UPDATED_DATE,ERROR_FLAG,TRIAL_NO,TRIAL_ALIAS_CODE, \n"
                + "COUNTRY_CODE,DISPLAY_UNIT_NO,GROUP_NO,TRIAL_UNIT_REFERENCE,PRIMARY_INVESTIGATOR,PRIMARY_CENTRE,OCATION_NO, \n"
                + "PURPOSE_CODE,MANAGING_MED_UNIT_CODE,UPDATING_MED_UNIT_CODE,FINANCE_MED_UNIT_CODE,PATIENTS_ALL_SET_UP_FLAG, \n"
                + "PATIENT_MONITORING_FLAG,COMMENTS,CONFIRMED_FLAG,CONFIRMED_BY,CONFIRMED_DATE,ROLLUP_ROLLDOWN_PLANNED_FIGS, \n"
                + "DOC_COLLECTION_INDICATOR,VALIDATED_PAT_CAP,MAXIMUM_PAT_CAP,PREFERRED_LANGUAGE_CODE,NEW_DISPLAY_UNIT_NO, \n"
                + "TOTAL_OPEN_DCF_ISSUES_NO,ORDER_INTERVAL,ORDER_INTERVAL_UNITS,DATA_SOURCE_CODE,RANK_SEQ) \n"
                + "VALUES (j, 1, 10, 'I', NULL, SYSDATE, 'N', 102922, 'TESTTRIAL4', 'BEL', \n"
               + "j, NULL, 'BELTU'||j, 130262, 124236, NULL, 'PATTR', 'FP', 'FP', 'FP', 'N', 'Y', 'TESTTU'||j, \n"
               + "'Y', 999999, SYSDATE, NULL, 'P', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL) \n"
               + "END LOOP \n"
               + "END";
    
    try {
        pstmt = conn.prepareStatement(Query);
        pstmt.executeUpdate();
    }
    catch(Throwable ex) {
        log.error("Error message: ", ex);
        throw ex;
    }
    finally {
        if (rs != null) {
            rs.close();
        }
        if (pstmt != null) {
            pstmt.close();
        }
        if (conn != null) {
            conn.close();
            log.info("!!!! Connection closed to database !!!!");
        }
    }
    
  • 方式2:Oracle端用序列生成主键(最稳妥)
    在数据库中创建主键序列,插入时直接取序列值,无需代码侧管理ID,天生线程安全:
    -- 先在Oracle执行一次创建序列
    CREATE SEQUENCE SEQ_FWA_STAGE_TU_ID START WITH 1 INCREMENT BY 1 NOCACHE;
    
    插入语句中ID的取值直接替换为SEQ_FWA_STAGE_TU_ID.NEXTVAL即可。

2. 调整删除记录逻辑

不要全局删除全表数据,改为按线程删除自己生成的记录,删除语句加过滤条件:

DELETE FROM FWA_STAGE_TU WHERE ID BETWEEN ? AND ?

参数对应每个线程自己的startId和endId即可。如果需要全局清表,把清表操作放到setUp线程组(所有测试线程启动前仅执行一次)或者tearDown线程组(所有测试线程结束后仅执行一次),不要放在每个业务线程的迭代逻辑中。

3. 可选性能优化

把BeanShell采样器替换为JSR223采样器,选择Groovy语言,JMeter官方推荐高并发场景使用,性能比BeanShell高5~10倍,避免高并发下采样器本身成为性能瓶颈。

内容的提问来源于stack exchange,提问作者user16923013

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 05:06:05