前面写过一篇:Oracle 驱动版本引起的显示字段奇怪编码问题。讲到因 Oracle 8.0.5 不支持子查询排序,为改善原来那种每次翻页时都捋出所有数据成对象到 List 中,然后从中拣取页面实际要显示的记录的性能问题时,采用了 rs.absolute() 直接跳到起始记录游标的方法,但又引入了乱码问题,例如:"无效",变成了 "0xE697A0E69588"。
虽说,换个驱动,如 8.1.7.0.0 以上版本的驱动就能解决乱码的问题,但这一换又怕会影响到其他的应用。有朋友评论说,其实循环 next() 到某处比 absolute() 定位要好,乍一看,有些牵强,不过试试就知道了。下面就来做样一个测试,测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public static void main(String[] args) throws Exception { int startCursor = 10000; //要直接移动到的游标位置 boolean stepByStep = true; //true 为 循环 next(),false 为 absolute() 定位 String url = "jdbc:oracle:thin:@10.128.39.23:1521:prod"; oracle.jdbc.driver.OracleDriver.class.newInstance(); Connection conn = DriverManager.getConnection(url, "user", "password"); Statement stmt = null; if(stepByStep){ stmt = conn.createStatement(); }else{ //要能执行 rs.absolute(),必须这么创建 Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } ResultSet rs = null; for (int j = 1; j <= 12; j++) { long start = System.currentTimeMillis(); rs = stmt.executeQuery("SELECT * FROM customers"); if(stepByStep){ //循环 next() 移动游标 for (int i = 0; i < startCursor; i++) { rs.next(); } }else{ //直接定位游标 rs.absolute(startCursor); } System.out.print(j+": 游标定位耗时:"+(System.currentTimeMillis() - start)+"毫秒,"); start = System.currentTimeMillis(); for (int i = 0; i < 10000 && rs.next(); i++) { rs.getObject(1); rs.getObject(2); rs.getObject(3); rs.getObject(4); rs.getObject(5); } System.out.println(" 获取数据耗时 :" + (System.currentTimeMillis() - start)+ " 毫秒."); } //释放资源,省去了 } |
测试环境:
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 Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。