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

Oracle存储过程SQL Developer秒级执行,Spring Boot调用超2秒问题排查

Spring Boot调用Oracle存储过程性能远低于SQL Developer的排查方向

问题背景

我有多个执行简单插入或更新操作的Oracle存储过程,在Oracle SQL Developer中执行几乎瞬间完成,但通过Spring Boot应用调用相同存储过程时,耗时可达2秒甚至更久。当前使用ojdbc11-production驱动。

存储过程示例

create or replace PACKAGE BODY EXAMPLE_PACKAGE AS

CURSOR c_readexisting(i_id NUMBER)                      
        RETURN EXAMPLE%ROWTYPE is                       
        SELECT * FROM EXAMPLE                       
        where id = i_id             
        for update;

PROCEDURE saveOrUpdate(
    i_id NUMBER,
    i_name varchar2,
    i_numbertest BINARY_DOUBLE,
    i_prompted NUMBER,
    i_captured NUMBER,
    out_id OUT number) is
      v_new EXAMPLE%ROWTYPE;
      v_existing EXAMPLE%ROWTYPE;

    BEGIN

    open c_readexisting (i_id);                     
      fetch c_readexisting into v_existing;                             

    if c_readexisting%notfound then

        v_new.id := HIBERNATE_SEQUENCE.nextval;
        v_new.version := 0;
        v_new.name := i_name;
        v_new.numbertest := i_numbertest;
        v_new.prompted := i_prompted;
        v_new.captured := i_captured;

      insert into EXAMPLE values v_new;

      out_id := v_new.id;

      close c_readexisting;

    else

      v_existing.version := v_existing.version + 1;
      v_existing.name := i_name;
      v_existing.numbertest := i_numbertest;
      v_existing.prompted := i_prompted;
      v_existing.captured := i_captured;

      update EXAMPLE set row = v_existing where id = v_existing.id;

      out_id := v_existing.id;

      close c_readexisting;                     

    end if;

    EXCEPTION                       

      WHEN OTHERS THEN                      

        IF c_readexisting%ISOPEN THEN                       
          CLOSE c_readexisting;                     
        END IF;                     
          dbms_output.put_line(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);                        
        RAISE;  

    END;

END EXAMPLE_PACKAGE;

Spring Boot数据源配置(application.yml)

datasource:
    url: URL
    username: USER
    password: PASSWORD
    driver-class-name: oracle.jdbc.driver.OracleDriver
    type: oracle.ucp.jdbc.PoolDataSource
    oracleucp:
      connection-factory-class-name: oracle.jdbc.pool.OracleDataSource
      min-pool-size: 20
      max-pool-size: 20
      max-idle-time: 300000
      initial-pool-size: 20
      fast-connection-failover-enabled: true

Repository实现代码

@Repository
public class ExampleRepository {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public ExampleRepository(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.setResultsMapCaseInsensitive(true);
    } 
 
    public Long saveOrUpdate(Example example) {

            SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
                .withCatalogName("EXAMPLE_PACKAGE")
                .withProcedureName("saveOrUpdate");

            SqlParameterSource mapSqlParameter = new MapSqlParameterSource(example.getMapForProcedure());
            
            Map<String, Object> out = simpleJdbcCall.execute(mapSqlParameter);

            var stopId = (BigDecimal)out.get("out_id");

            return stopId.longValue();

    }
}

可能的性能瓶颈及排查方向

  • SimpleJdbcCall重复初始化:每次调用saveOrUpdate都新建SimpleJdbcCall实例,会重复解析存储过程元数据,增加额外开销。应将SimpleJdbcCall作为类成员变量初始化一次,而非每次调用都创建。
  • 连接池配置问题:UCP连接池可能存在连接获取延迟,可检查connection-wait-timeout配置是否合理,是否存在连接泄漏导致频繁新建连接(Oracle新建连接开销较高)。另外fast-connection-failover-enabled开启后若集群配置不当,可能增加心跳开销。
  • 驱动配置差异:SQL Developer与Spring Boot使用的驱动连接参数可能不同,比如defaultRowPrefetch、fetchSize等。可尝试在数据源URL中添加defaultRowPrefetch=100这类参数,或检查是否启用了隐式结果集等特性。
  • 事务上下文差异:SQL Developer执行时可能无显式事务,而Spring Boot默认会给Repository方法添加事务(若开启自动代理),事务的开启与提交会增加开销。可检查是否存在不必要的事务,或事务传播属性是否合理。
  • 参数类型不匹配:MapSqlParameterSource传递的参数类型可能与存储过程参数类型不匹配,导致Oracle进行隐式类型转换,增加执行时间。例如BINARY_DOUBLE类型参数,需确保Java端传递的是Double类型。
  • 网络与环境差异:Spring Boot应用服务器与SQL Developer所在机器到Oracle数据库的网络延迟可能不同,或数据库对不同客户端的资源分配策略有差异。可通过tnsping或在应用中打印连接耗时排查网络问题。
  • dbms_output的额外开销:存储过程异常分支调用了dbms_output.put_line,若应用端开启了dbms_output读取,会带来额外开销。可尝试移除该语句或确保应用端未配置读取dbms_output。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 00:47:07