java - 应用结果集的获取大小会终止程序

标签 java oracle resultset ojdbc

这对我来说是一个非常有趣的问题,希望您能帮我解决。我正在执行查询以从 Oracle DB 表中选择所有行。这里使用的是oracle jdbc驱动程序。为了避免连接超时,使用 rownum 以 100 行为增量执行查询。一切都会好起来的,但是程序在尝试执行 resultSet.next() 时卡住在结果集的第 91 行。其中无一异常(exception)。我试图寻找这种行为的原因,并意识到问题在于结果集的获取大小。获取大小的默认值为 10。此行为看起来像是从结果集中拉出这 10 行,并且当我们进入释放的空间时程序被卡住。然后我们设置 fetch size 0,一切都很完美。这是预期的行为吗?如果是这样,为什么?在下面的示例中,通过按行号退出循环来绕过此问题。

private static volatile int bottomRow = -99;
private static volatile int topRow = 0;
private static final String SQL = "SELECT * from (select m.*, rownum r from keyspace.table m) where r >= ? and r < ?";

public static void select(Connection connection) {
    try (PreparedStatement preparedStatement=connection.prepareStatement(SQL)){
        while (true) {
            incrementCounters();
            preparedStatement.setInt(1, bottomRow);
            preparedStatement.setInt(2, topRow);
            ResultSet rs = preparedStatement.executeQuery();
          // rs.getFetchSize(); -> default value is 10
            if (rs.next()) {
                do {
                    rs.getString("id");
                    rs.getString("customer_name");
                    /* some logic */
                    if (rs.getRow() == 90) {
                            break;
                    }
                 } while (rs.next());
            } else break;
        }
    } catch (Exception e) {
    } 
}

private synchronized static void incrementCounters() {
    Thread.sleep(700);
    if (topRow != 0) {
        bottomRow += 90;
        topRow = bottomRow + 100;
    } else {
        bottomRow += 100;
        topRow += 100;
    }
}

jdbc驱动版本

<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>jdbc</artifactId>
    <version>11.2.0.3</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
    <version>11.2.0.3</version>
</dependency>

池属性的配置

private static DataSource ds=null;
public static Connection getConnection() throws SQLException{
    if (ds==null){
        synchronized (Source.class.getName()) {
            if (ds==null)
                try {
                    DriverManager.setLoginTimeout(1);
                    String driverClassName="oracle.jdbc.OracleDriver";
                    PoolProperties p = new PoolProperties();
                    p.setUrl(url);
                    p.setDriverClassName(driverClassName);
                    p.setUsername(username);
                    p.setPassword(password);
                    p.setJmxEnabled(false);
                    p.setTestWhileIdle(false);
                    p.setTestOnBorrow(true);
                    p.setValidationQuery("SELECT 1 from dual");
                    p.setTestOnReturn(false);
                    p.setTestOnConnect(false);
                    p.setValidationInterval(5*1000);
                    p.setTimeBetweenEvictionRunsMillis(120000);
                    p.setMaxActive(500);
                    p.setInitialSize(0);
                    p.setMinIdle(30);
                    p.setMaxIdle(100);
                    p.setRemoveAbandonedTimeout(60);
                    p.setMinEvictableIdleTimeMillis(120000);
                    p.setLogAbandoned(false);
                    p.setRemoveAbandoned(true);
                    p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer; org.apache.tomcat.jdbc.pool.interceptor.StatementCache");
                    ds = new DataSource();
                    ds.setPoolProperties(p);
                } catch (Exception e) {
                    log.error("error {}",e.getMessage());
                    throw new RuntimeException(e);
                }
        }
    }
    return ds.getConnection();
}

最佳答案

我无法真正回答你的问题,因为缺少很多细节,但我必须在这里指出的一件事是,代码似乎不必要地过度,这可能就是它的原因看似错误的行为。

如果您要选择所有行(或运行带有相关条件的选择),我建议您使用简单的 select * from [table] 并保留其余内容。

您必须了解的是,JDBC 驱动程序处理的不仅仅是查询编译和数据传输,过于复杂的查询会阻止它优化您正在执行的任何操作。

此外,请记住,运行带有边界的多个选择会产生大量开销,特别是在 Oracle 上,即使有良好的索引,也并不总是能够实现。

例如,运行查询来获取 1000 条记录会创建一个结果集,打开一个流并查找记录,对它们进行过滤、排序,并以 JDBC 驱动程序认为最有意义的任何批量大小传输记录。 (在数据库端只有一个指针从第一个移动到最后一个)

使用硬编码的 100 个记录大小运行相同的操作 10 次会产生开销,其中需要创建 10 个结果集,数据库服务器需要查找记录 10 次,过滤它们 10 次,对它们排序 10 次并跳过适当的数量到达您请求的批处理,这意味着 0,然后 100,然后 200...(在数据库端,每个批处理都必须创建一个新指针,然后在传输开始之前将其移动到适当的位置)

现在,对于 1000 条记录来说,这并不重要,但始终编写弹性代码是一个很好的做法,但是如果您必须在包含 2000 万条记录的表上执行此操作(我已经这样做了,这就是如何我了解到)工作时间从(就我而言)几分钟到几周。

至于超时,它们不是问题,除非处理已经传输的数据需要太长时间,但如果是这种情况,我不会考虑查询优化,而是考虑数据处理一旦完成就如何处理(也许引入了某种形式的并行以同时完成更多工作)。

如果针对您的具体情况,您可以提供更多详细信息,例如表中存储的内容、有多少条记录等,我将很高兴更新我的答案。

希望有帮助。

关于java - 应用结果集的获取大小会终止程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57649880/

相关文章:

java - JTextArea - setText() 和 append() 在 Listener method() 中不起作用

java - 保存LinearLayout与多个TextView。安卓

java - availableProcessors() 为双核手机返回 1

php - 如何在 oracle 查询中转义双引号?

java - 为大文件构建动态查询

java - java jdbc 异常中结果集异常开始之前

java - 使用自定义词汇表将结果集流式传输为 RDF

java - 列出 ThreadPoolTask​​Executor 中所有正在运行/排队的线程

SQL 语法错误 : "Missing Right Parenthesis"

使用 jdbc 时泛型类型的 java 反射