如何通过Oracle JDBC驱动一次性获取全部数据行
优化大结果集JDBC查询性能:从3秒到0.7秒的实践
最近我处理了一个JDBC查询性能的问题,分享下我的解决过程:
问题背景
我有一个数据库查询会返回约6000行数据,需要把所有数据行取出来,用来生成执行批处理任务的R脚本。最开始用的常规方法耗时3秒,客户反馈这个速度太慢了。
旧方案(耗时3秒)
我一开始的实现是执行查询后,遍历ResultSet逐条把数据填充到ArrayList里,精简后的代码大概是这样:
public List<MyClass> loadFromDB() throws SQLException { List<MyClass> resultList = new ArrayList<>(); String sql = "SELECT col1, col2, ... FROM my_table WHERE ..."; try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery()) { while (rs.next()) { MyClass item = new MyClass(); item.setCol1(rs.getString("col1")); // 其他字段赋值 resultList.add(item); } } return resultList; }
优化方案(耗时0.7秒)
我调整了两步操作,直接把性能提上去了:
- 第一步:先执行一个计数查询,拿到结果集的总行数:
SELECT COUNT(*) FROM my_table WHERE ... - 第二步:在创建PreparedStatement的时候,调用
stmt.setFetchSize(totalRows),把Fetch Size设置为结果集的总条数,这样JDBC会一次性从数据库拉取所有数据,减少了多次网络交互的开销
优化后的核心代码片段:
// 先获取总行数 int totalRows = 0; String countSql = "SELECT COUNT(*) FROM my_table WHERE ..."; try (Connection conn = getConnection(); PreparedStatement countStmt = conn.prepareStatement(countSql); ResultSet countRs = countStmt.executeQuery()) { if (countRs.next()) { totalRows = countRs.getInt(1); } } // 再执行查询并设置Fetch Size List<MyClass> resultList = new ArrayList<>(totalRows); // 提前初始化容量,小幅度优化性能 String dataSql = "SELECT col1, col2, ... FROM my_table WHERE ..."; try (Connection conn = getConnection(); PreparedStatement dataStmt = conn.prepareStatement(dataSql)) { dataStmt.setFetchSize(totalRows); // 关键优化点 try (ResultSet rs = dataStmt.executeQuery()) { while (rs.next()) { MyClass item = new MyClass(); item.setCol1(rs.getString("col1")); // 其他字段赋值 resultList.add(item); } } } return resultList;
为什么这个优化有效?
默认情况下,JDBC的Fetch Size通常很小(不同数据库默认值不同,比如Oracle是10,MySQL默认是100),这意味着每次只会从数据库拉取少量数据,需要多次和数据库建立网络连接、传输数据,这在数据量较大时会产生明显的开销。把Fetch Size设置为结果集的总条数,就能一次性拉取所有数据,大幅减少网络往返次数,从而显著提升查询性能。
内容的提问来源于stack exchange,提问作者Dan Newman
相关产品推荐
相关产品推荐

