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
相关产品推荐
相关产品推荐

