JDBC 连接 Oracle 时,用 rs.absolute(n) 真的不如 n 次 next() 性能好
前面写过一篇:Oracle 驱动版本引起的显示字段奇怪编码问题。讲到因 Oracle 8.0.5 不支持子查询排序,为改善原来那种每次翻页时都捋出所有数据成对象到 List 中,然后从中拣取页面实际要显示的记录的性能问题时,采用了 rs.absolute() 直接跳到起始记录游标的方法,但又引入了乱码问题,例如:"无效",变成了 "0xE697A0E69588"。
虽说,换个驱动,如 8.1.7.0.0 以上版本的驱动就能解决乱码的问题,但这一换又怕会影响到其他的应用。有朋友评论说,其实循环 next() 到某处比 absolute() 定位要好,乍一看,有些牵强,不过试试就知道了。下面就来做样一个测试,测试代码如下:
测试环境:
Oracle 数据库 8.0.5.1.0
驱动文件:classes12.zip
驱动版本:8.1.6.0.0
驱动类:oracle.jdbc.driver.OracleDriver
测试数据(未列出每一次的测试数据,只求了不同条件下的平均值,startCuror 为定位的游标位置,stepByStep 表示是用 n 次 next() 移动游标,还是用 absolute(n) 直接定位,true 为前者):
换着用 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) 进行许可。
虽说,换个驱动,如 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) 进行许可。