【问题标题】:Temporary tablespace of CLOB not freedCLOB 的临时表空间未释放
【发布时间】:2015-11-07 06:53:18
【问题描述】:

我遇到的问题是我的 Java 应用程序正在从数据库中导出大量的 clob,但由于未释放旧的 clob,所以总是用完临时表空间。

我如何做的简化代码示例是:

public void getClobAndDoSomething (oracle.jdbc.OracleCallableStatement pLSQLCodeReturningClob) {
    try (OracleCallableStatement statement = pLSQLCodeReturningClob) {
        statment.registerOutParameter(1, Types.CLOB);
        statement.execute();

        oracle.sql.CLOB clob = statement.getCLOB(1);
        clob.open(CLOB.MODE_READONLY);
        Reader reader = clob.getCharacterStream();
        BufferedReader bufferedReader = new BufferedReader(reader);

        doSomethingWithClob(bufferedReader);

        bufferedReader.close();
        reader.close();
        clob.close();
        clob.freeTemporary();
    } catch (SQLException e) {
        if (e.getErrorCode() == 1652) {
            //Server ran out of temporary tablespace
        } else
            handleException(e);
    } catch (IOException e) {
         handleException(e);
    }
}

如果在循环中调用此方法,它总是会在某个时候耗尽临时表空间。

释放空间的唯一可靠方法是关闭连接并打开一个新连接(例如使用clob.getInternalConnection.close()),但这会减慢应用程序的速度并使当前的多线程方法无法使用。

遗憾的是 ojdbc 上的 oracle 文档并没有真正的帮助,谷歌只发现文章告诉我使用 lobs 的 free() 方法,甚至没有被 oracles 临时 clob 实现。

补充说明
使用 oracles APEXExport.class 导出大工作区时也会出现此问题。

驱动程序和系统细节:

  • 操作系统:Windows 7 Professional x64
  • Java:1.8.0_45 64 位
  • ojdbc: 6(有更具体的版本吗?)
  • 数据库:Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production

如果您有 APEX 应用程序,请测试代码:

java.sql.Connection con = getConnection();
String gStmtGetAppClob = "begin ? := wwv_flow_utilities.export_application_to_clob(?, ?, ?, ?); end;";
int appId = 100;

while (true) {
    OracleCallableStatement exportApplicationToClob = (OracleCallableStatement) con.prepareCall(gStmtGetAppClob);
    exportApplicationToClob.setString(3, "Y"); //Public reports
    exportApplicationToClob.setString(4, "N"); //Saved reports
    exportApplicationToClob.setString(5, "N"); //Interactive report notifications
    exportApplicationToClob.setBigDecimal(2, new BigDecimal(appId));

    getClobAndDoSomething(exportApplicationToClob);
    try {
        Thread.sleep(50);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        break;
    }
}
con.close();

更新:
经过更多测试后,我发现在没有关闭连接的情况下,clob 会在某个时候被释放。所以看起来free() 实际上是lazyFree()。但这可能需要一分钟以上的时间。
我也可以将CLOB转换为Clob,不知道我之前做错了什么。如果使用 Clob,问题保持不变。

【问题讨论】:

  • 在坚持使用 JDBC 标准 API,而不是使用 ojdbc API 时,我从未遇到过任何这些问题。不过,请确保在异常情况下也执行 free() 调用!
  • @LukasEder 好点,在 catch 块中带有 free()。没有放在例子中。不幸的是,我不能使用标准的 JDBC API,因为 DB 确实返回了一个临时 clob。标准 JDBC 只能处理存储在表中的 clob。 (强制转换或转换会导致异常)
  • 对不起,我错过了那个细节。也许,您可以使用您正在调用的 PL/SQL 的摘录来更新您的问题?甚至更好的是,一个最小的可重现示例也很棒

标签: java oracle clob ojdbc


【解决方案1】:

在 pl/sql 世界中,这将通过临时 CLOB 处理并在循环中重用它。

假设您使用的是 java.sql.CLOB.,它似乎没有 createTemporary CLOB 选项,但 oracle.sql.CLOB 有。它还有freeTemporary() 方法来清除临时空间。

https://docs.oracle.com/cd/E18283_01/appdev.112/e13995/oracle/sql/CLOB.html

您的调用例程可以创建一个临时 CLOB 并将其作为参数(比如说 p_clob)传递给此方法。每次都将query的返回值赋给p_clob,而不是创建新的CLOB(例如CLOB clob = statement.getCLOB)。

现在时间不多,但稍后会编辑详细的代码。如果您可以使用上述方法,那就太好了。

【讨论】:

  • 我正在使用的 CLOB 已经是 oracle.sql.CLOBjava.sql.Clob 只有大写的 C),但我会将确切的类添加到示例中以完成。重用相同的 CLOB 是解决问题的好主意,但只有当我调用自定义 PL/SQL 包时才可以选择,而不是 DBMS 包。
  • 如何在循环之前创建 Clob (While Loop)..并清理它并在循环内重用相同的 Clob。
  • 我不确定如何在这种情况下重用 CLOB,至少在我调用标准包(如 wwv_flow_utils)时。他们总是会给我一个新的 CLOB,所以保留旧的引用对我没有任何好处。
猜你喜欢
  • 2016-08-24
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 1970-01-01
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多