这对我来说是一个非常有趣的问题,希望您能帮助我解决。我正在查询从Oracle DB表中选择所有行。这里使用了oracle jdbc驱动程序。为避免连接超时,将使用rownum以100行为增量执行查询。一切都会好起来的,但是程序将冻结在结果集的第91行,尝试执行resultSet.next()。其中没有任何例外。我试图寻找这种行为的原因,并意识到问题在于结果集的获取大小。提取大小的默认值为10。此行为看起来像是从结果集中拉出这10行,并且当我们进入释放空间时冻结了程序。然后,将抓取大小设置为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();
}
答案 0 :(得分:2)
我无法真正回答您的问题,因为缺少许多细节,但是我必须在这里指出的一件事是,代码似乎不必要地位于顶部,这可能是因为貌似是错误的行为。
如果您要选择所有行(或为此选择条件进行选择),我建议您使用简单的select * from [table]
并把其余的排除在外。
您需要了解的是,JDBC驱动程序不仅可以处理查询编译和数据传输,而且处理查询的复杂性也使它无法优化您的工作。
此外,请记住,有边界地运行多个选择会产生大量开销,特别是在Oracle上,即使具有良好的索引编制,也不会总是如此。
例如,运行查询以获取1000条记录将创建一个结果集,打开一个流并查找记录,对其进行过滤,排序,以JDBC驱动程序认为最合理的任何批量大小传输记录。 (在数据库端,只有一个指针从第一个移动到最后一个)
以相同的方式运行10次,但是硬编码100条记录大小会产生开销,这需要创建10个结果集,而db服务器需要查找记录10次,将其过滤10次,对它们进行10次排序,并跳过适当的数量转到您所请求的批处理,即0,然后是100,然后是200 ...(在数据库方面,每个批处理都必须创建一个新的指针,然后将其移动到适当的位置,然后才能开始传输)>
现在,对于1000条记录来说,这并不重要,但是始终编写弹性代码是一个好习惯,但是如果您必须在具有2000万条记录的表上进行操作(我已经完成了,那就是我了解到)工作从(对我来说)几分钟到几周。
对于超时,它们不是问题,除非处理您已经传输的数据花费的时间太长,但是如果是这种情况,我就不希望查询优化,而是一旦处理完就如何处理数据in(也许引入了某种形式的并行im,以便同时完成更多工作)。
如果对于您的特定情况,您可以提供更多详细信息,例如表中存储的内容,有多少记录等等,那么我很乐意更新我的答案。
希望有帮助。
答案 1 :(得分:0)
“为避免连接超时,将使用rownum以100行为增量来执行查询”
您可以检查为什么首先发生连接超时。例如:当您尝试在sqldeveloper或toad中运行查询时,是否超时?
查询“ SELECT * from(从keyspace.table m中选择m。*,rownum r),其中r> =?并且r <?”看起来您希望实现等效的分页。我建议,如果您有兴趣获取一批要在UI上显示的数据,则希望通过内部查询对确定性的记录集进行排序。
例如:它应该是SELECT * from(从keyspace.table m ORDER BY中选择m。*,rownum r),其中r> =?并且r <?。
如果您在12c及更高版本中,请查看行限制子句的选项 https://oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1