JDBC 连接 Oracle 时,用 rs.absolute(n) 真的不如 n 次 next() 性能好

前面写过一篇:Oracle 驱动版本引起的显示字段奇怪编码问题。讲到因 Oracle 8.0.5 不支持子查询排序,为改善原来那种每次翻页时都捋出所有数据成对象到 List 中,然后从中拣取页面实际要显示的记录的性能问题时,采用了 rs.absolute() 直接跳到起始记录游标的方法,但又引入了乱码问题,例如:"无效",变成了 "0xE697A0E69588"。

虽说,换个驱动,如 8.1.7.0.0 以上版本的驱动就能解决乱码的问题,但这一换又怕会影响到其他的应用。有朋友评论说,其实循环 next() 到某处比 absolute() 定位要好,乍一看,有些牵强,不过试试就知道了。下面就来做样一个测试,测试代码如下:
 1public static void main(String[] args) throws Exception {
 2
 3    int startCursor = 10000;   //要直接移动到的游标位置
 4    boolean stepByStep = true; //true 为 循环 next(),false 为 absolute() 定位
 5
 6    String url = "jdbc:oracle:thin:@10.128.39.23:1521:prod";
 7    oracle.jdbc.driver.OracleDriver.class.newInstance();
 8    Connection conn = DriverManager.getConnection(url, "user", "password");
 9
10    Statement stmt = null;
11    if(stepByStep){
12        stmt = conn.createStatement();
13    }else{ //要能执行 rs.absolute(),必须这么创建 Statement
14        stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
15                  ResultSet.CONCUR_READ_ONLY);
16    }
17
18    ResultSet rs = null;
19    for (int j = 1; j <= 12; j++) {
20        long start = System.currentTimeMillis();
21        rs = stmt.executeQuery("SELECT * FROM customers");
22        if(stepByStep){ //循环 next() 移动游标
23            for (int i = 0; i < startCursor; i++) {
24                rs.next();
25            }
26        }else{ //直接定位游标
27            rs.absolute(startCursor);
28        }
29
30        System.out.print(j+": 游标定位耗时:"+(System.currentTimeMillis() - start)+"毫秒,");
31        start = System.currentTimeMillis();
32
33        for (int i = 0; i < 10000 && rs.next(); i++) {
34            rs.getObject(1);
35            rs.getObject(2);
36            rs.getObject(3);
37            rs.getObject(4);
38            rs.getObject(5);
39        }
40
41        System.out.println(" 获取数据耗时 :" + (System.currentTimeMillis() - start)+ " 毫秒.");
42    }
43
44    //释放资源,省去了
45}

测试环境:
Oracle 数据库 8.0.5.1.0
驱动文件:classes12.zip
驱动版本:8.1.6.0.0
驱动类:oracle.jdbc.driver.OracleDriver

测试数据(未列出每一次的测试数据,只求了不同条件下的平均值,startCuror 为定位的游标位置,stepByStep 表示是用 n 次 next() 移动游标,还是用 absolute(n) 直接定位,true 为前者):

 游标定位平均耗时
获取数据平均耗时
 游标定位平均耗时
获取数据平均耗时
 1条件:startCursor = 1000; stepByStep = true; 条件:int startCursor = 1000; stepByStep = false;
 161.222 毫秒1547.111 毫秒 183.889 毫秒1670.333 毫秒
      
 2条件:startCursor = 10000; stepByStep = true; 条件:startCursor = 10000; stepByStep = false;
 1445.25 毫秒1541.667 毫秒 1532.667 毫秒1717.417 毫秒
      
 3条件:startCursor = 100000; stepByStep = true; 条件:startCursor = 100000; stepByStep = false;
 11719.17 毫秒1225.333 毫秒 11894.67 毫秒1290.25 毫秒
      
 4条件:startCursor = 500000; stepByStep = true; 条件:startCursor = 500000; stepByStep = false;
 56653.83 毫秒1225.083 毫秒 java.lang.OutOfMemoryError: Java heap space

换着用 8.1.7.0.0, 9.0.1.1.0 这两个版本的驱动,和连接到 9.2.0.1.0 版本的数据库测试得出来的数据都相仿。由测试数据大致可以得出如下结论:

1. 用 absolute(n) 比循环 n 次 next() 定位游标的时间要稍长,但不是很明显。也许这只是跟  ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY 类型的 Statement 有关。

2. 创建了 ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY 类型的 Statement 时,取数据记录的时候也会慢一些,也不是很明显。

3. 但最后一点是始料未及的--居然产生了内存溢出。移动游标到 500000 的位置时,用 next() 循环没问题,用 absolute 无论是连接 Oracle 8.0.5.1.0 还是 Oracle 9.2.0.1.0 时均告 OutOfMemoryError。可见 absolute,或者是 ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY 类型的 Statement 占用内存会比较大。看起来似乎真的在执行 absolute(n) 方法的时候预读了数据到内存中。

前面一直是关注 absolute(n) 和 n 次 next() 的速度问题,未考虑到 Statement 本身类型的问题,所以还需看看对 ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY 类型的 Statement 进行 n 次 next() 效果,会如何,是否在 500000 次 next() 也会像 absolute(500000) 那样产生 OutOfMemoryError 堆内存溢出异常,这还有待于求证。

永久链接 https://yanbin.blog/jdbc-oracle-rs-absoluten-or-next-n/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。