【问题标题】:How do I forcefully close a connection from a connection pool when it's taking too much time to close?当关闭时间过长时,如何强制关闭连接池中的连接?
【发布时间】:2017-11-07 02:11:01
【问题描述】:

有时关闭连接需要很长时间,比如超过 10 分钟到 1 小时,或者更糟,甚至无限期,具体取决于查询的繁重或缓慢。

在客户端因为花费太多时间而取消查询的情况下,我希望尽快释放使用的底层连接。

我尝试取消 PreparedStatement,关闭它,然后关闭结果集,最后关闭连接。取消几乎立即进行。关闭 PreparedStatement 和 ResultSet 花费了太多时间,我不得不将其包装在具有超时的 Callable 中,以便在适当的时候跳过该过程并继续关闭连接本身。我还没有什么可以尝试的运气。

我该如何处理?我不能简单地让连接不关闭,也不能让用户等待 10 分钟才能进行另一个类似的查询。

另外,是什么导致关闭连接花费太多时间?还有什么我可以做的吗?您认为 Oracle 查询提示会有所帮助吗?

顺便说一下,我正在通过瘦型驱动程序使用 Oracle JDBC。

更新:

显然,可以通过在 connectionCacheProperties 中配置 TimeToLive 属性来强制关闭连接,该属性会在特定时间内关闭连接。但是,我需要的是按需提供。这一点值得一提,因为这证明了可以像刚才的连接池那样强行关闭它。事实上,我什至在我的日志中收到了以下消息。

ORA-01013: user requested cancel..

【问题讨论】:

  • 我从未听说过这样的场景,即连接需要时间才能关闭。
  • @OldProgrammer,尝试查询一些足够复杂的东西,以至于从结果集中获取前几条记录都需要花费太多时间。让我们不要进入这个问题的性能方面。我注意到,一旦查询得到几行,它就会很快关闭。我猜查询已经进入了一个不可关闭的阶段(但不是真的,到达 TimeToLive 可以关闭它),然后进入另一个阶段,它开始发送可关闭的记录。
  • 您可以尝试使用 kill 语句从另一个 Oracle SPID 中止它(我们称之为您的应用管理员连接)。
  • @access_granted,您能否将其翻译为答案并使用 JDBC 使用编程 Java 解决方案?如果可行,我会接受。

标签: java oracle jdbc database-connection ojdbc


【解决方案1】:

这是我们在我的 POW 中使用的,而不是“将更改系统授予 $UID”作为 SYS 拥有的过程(简化的工作版本):

CREATE OR REPLACE procedure SYS.kill_session(in_sid varchar2)
as
  l_serial number;
  l_runsql varchar2(1000) := 'alter system kill session ''$1,$2'' immediate';
begin
  begin
  select serial# into l_serial from v$session where username =
  (
    SELECT USER FROM DUAL
  ) and sid=in_sid and rownum<=1;
  exception when no_data_found then
    raise_application_error( -20001, 'Kill candidate not found');
  end;
  l_runsql := replace( l_runsql, '$1', in_sid);
  l_runsql := replace( l_runsql, '$2', l_serial);
  execute immediate l_runsql;
end;
/

这样你只能杀死你自己的会话。

【讨论】:

  • 我刚刚发现我们实际上有公司范围内的政策,不创建这样的存储过程,并且杀死会话的命令必须经过一个特殊的过程。 Still+1 用于提供替代解决方案,尽管在我目前的情况下不是理想且可行的解决方案。
【解决方案2】:

主要功能:

  String g_sid = "";

线程 1:

  String sql = ...;
  Connection conn = ...your connection func...;

  Statement stmt = conn.createStatement();
  ResultSet rset = stmt.executeQuery( "SELECT sid from v$mystat");
  if (rset.next()) g_sid = rset.getString("sid");
  rset.close();
  // now to the actual long-running SQL
  ResultSet rset = stmt.executeQuery( sql );
  // 
  stmt.close();

线程 2:

  String serialN = "";
  Connection conn = ...your admin connection func...

  Statement stmt = conn.createStatement();
  ResultSet rset = stmt.executeQuery( "SELECT serial# serialN from v$session where sid=" + g_sid );
  if (rset.next()) {
    serialN = rset.getString("serialN"); 
    stmt.execute("alter system kill session '" + g_sid + "," + serialN + "'");
  }
  stmt.close();
  // probably keep the admin connection open for further maintenance
  //

【讨论】:

  • 这里没有JSP,这是纯Java。
  • 所以在这种情况下,只需在线程之间共享变量“SID”
  • 现在这看起来需要更高的权限访问。我会试一试。我真的更喜欢这样做的非特权方式。
  • 那么您的 DBA 要么需要允许您更改系统,要么必须编译一个小型 SYS 过程来隐式执行它。一旦 Oracle 孵化了正在运行的进程,实际上要么更改系统,要么杀死 Unix spid(仍然由 oracle uid 所有)将释放它(我只是用引号编辑了 stmt)
  • @access_granted,实际上,这两个数字都应该用单引号括起来。我知道我会得到“SQL 错误:ORA-01031:权限不足”。我想我必须修改我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多