JDBC 导致服务器挂起

问题描述
在通过由应用程序或 WebLogic Server 本身使用的 JDBC 连接进行调用时,此连接会在整个调用期间内阻塞一个 WebLogic Server 执行线程。尽管在 SQL 查询上阻塞的线程需要等待,但 JVM 将通过其线程调度机制确保 CPU 获得可运行线程。但是,由 JDBC 调用占用的线程将保留给应用程序使用,直至该调用从 SQL 查询返回。

即使事务超时也不会终止由在此事务中登记的资源完成的任何操作,或者使其超时。这些操作将正常地运行,而不会出现中断。事务超时只是在事务上设置一个标记,将其标记为回滚,这样提交此事务的任何后续请求都将失败,系统抛出 TimedOutExceptionRollbackException。但是,如前所述,长时间运行的 JDBC 调用会导致 WebLogic Server 执行线程阻塞,如果所有线程均被阻塞,没有能够处理传入请求执行线程,则最终可导致实例挂起。

最新版本的 WebLogic Server 具有健全性检查功能,能够定期检查线程不响应的时间是否达到特定时长(缺省值为 600 秒)。如果是这样,系统会向日志文件输出一条如下的错误消息:

####<Nov 6, 2004 1:42:30 PM EST> <Warning> <WebLogicServer> <mydomain> <myserver> <CoreHealthMonitor> <kernel identity> <>
<000337> <ExecuteThread: '64' for queue: 'default' has been busy for "740" seconds working on the request "Scheduled Trigger",
which is more than the configured time (StuckThreadMaxTime) of "600" seconds.>


这并不会中断线程,而只是提供给管理员的一项通知。阻塞线程恢复为正常状态的唯一途径是等待它正处理的请求完成。这种情况下,WebLogic Server 日志文件中将出现一条如下的消息:

####<Nov 7, 2004 4:17:34 PM EST> <Info> <WebLogicServer><mydomain> <myserver> <ExecuteThread: '66' for queue: 'default'>
<kernel identity> <> <000339> <ExecuteThread: '66' for queue: 'default' has become "unstuck".>


健康检查功能的时间间隔是可以配置的。请检查 config.xml 文件 <Server> 标记中的 StuckThreadMaxTime 属性:http://e-docs.bea.com/wls/docs81/config_xml/Server.html#StuckThreadMaxTime (English) 或 WebLogic Server 管理控制台帮助中的“Detecting stuck threads”一节:http://e-docs.bea.com/wls/docs81/perform/WLSTuning.html#stuckthread (English)。

返回页首

故障排除
不同的编程技术或 JDBC 连接池配置可以造成死锁或长时间运行的 JDBC 调用,进而使 WebLogic Server 实例挂起。常规服务器挂起模式中提供了有关如何排除和分析挂起 WebLogic Server 实例的一般信息。

本模式讨论导致服务器挂起的 JDBC 调用,并从 JDBC 角度讨论导致 WebLogic Server 实例挂起的常见问题的原因。本模式中引用的其它支持模式位于 WebLogic Server Support Patterns Site (English)。

快速链接

为什么发生此问题?
以下是在 JDBC 调用时导致 WebLogic Server 实例挂起的各种可能原因:
返回页首

同步的 DriverManager.getConnection()
旧版本的 JDBC 应用程序代码有时使用 DriverManager.getConnection() 调用来通过特定驱动程序取得数据库连接。不建议使用此项技术,因为它可能会导致死锁,或者至少相对降低连接请求的性能。究其原因,是因为所有 DriverManager 调用都采用类同步模式,也就是说一个线程中的一个 DriverManager 调用将阻塞一个 WebLogic Server 实例内任何其它线程中的所有其它 DriverManager 调用。

此外,SQLException 的构造器会构造一个 DriverManager 调用,而且大多数驱动程序使用 DriverManager.println() 调用进行日志记录,这其中的任何一项都会阻塞发出 DriverManager 调用的所有其它线程。

DriverManager.getConnection() 在返回为数据库建立的物理连接之前可能会需要相对较长的时间。即使不发生死锁,所有其它调用也需要等到这个线程获得连接。在像 WebLogic Server 这样的多线程系统中,这不是最佳的方式。

此处的信息来自 http://forums.bea.com/bea//thread.jspa?forumID=2022&threadID=200063365&message
ID=202311284&start=-1#202311284
(English)。
此外,我们的文档也明确指出不应使用 DriverManager.getConnection()http://e-docs.bea.com/wls/docs81/faq/jdbc.html#501044 (English)。

如果愿意在 JDBC 代码中使用 JDBC 连接,应使用 WebLogic Server JDBC 连接池,为其定义一个数据源,并从此数据源获得连接。这样您将享有池的所有优点(资源共享、连接重用、数据库关闭后的连接刷新等等)。它还将帮助您避免 DriverManager 调用可能发生的死锁。有关如何使用 JDBC 连接池、数据源及 WebLogic Server 中的其它 JDBC 对象的详细信息,请参阅:http://e-docs.bea.com/wls/docs81/jdbc/intro.html#1036718 (English) 和 http://e-docs.bea.com/wls/docs81/jdbc/programming.html#1054307 (English)。

DriverManager.getConnection() 调用中阻塞的典型线程如下:
"ExecuteThread-39" daemon prio=5 tid=0x401660 nid=0x33 waiting for monitor entry [0xd247f000..0xd247fc68]
  at java.sql.DriverManager.getConnection(DriverManager.java:188)
  at com.bla.updateDataInDatabase(MyClass.java:296)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:865)
  at weblogic.servlet.internal.ServletStubImpl.invokeServlet
(ServletStubImpl.java:120)
  at weblogic.servlet.internal.ServletContextImpl.invokeServlet
(ServletContextImpl.java:945)
  at weblogic.servlet.internal.ServletContextImpl.invokeServlet
(ServletContextImpl.java:909)
  at weblogic.servlet.internal.ServletContextManager.invokeServlet
(ServletContextManager.java:269)
  at weblogic.socket.MuxableSocketHTTP.invokeServlet
(MuxableSocketHTTP.java:392)
  at weblogic.socket.MuxableSocketHTTP.execute(MuxableSocketHTTP.java:274)
  at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:130)




返回页首

长时间运行的 SQL 查询
长时间运行的 SQL 查询在其执行期间将阻塞执行线程,直至它们将结果返回给发出调用的应用程序。这就意味着,需要修改 WebLogic Server 实例的配置来处理应用程序负载要求的足够多的调用。这种情况的限制因素是执行线程数和 JDBC 连接池中的连接数。一般的经验方法是将池中的连接数设置为等于执行线程数,以便能够实现最优的资源利用。如果使用 JTS,则池中的可用连接应更多一些,因为某些连接可能会保留给实际处于非活动状态的事务。

对于在长时间运行的 SQL 调用期间挂起的线程,其在 Thread Dump 中的堆栈与挂起的数据库的堆栈十分相似。有关详细信息,请对比下一小节的内容。

挂起的数据库
对于依赖于数据库的应用程序来说,良好的数据库性能是其性能的关键。因此,挂起的数据库可能会阻塞 WebLogic Server 实例中许多或所有可用的执行线程并最终导致服务器挂起。要诊断这一问题,应从挂起的 WebLogic Server 实例获得 5 到 10 个 Thread Dump,并检查您的执行线程(在缺省队列或您的应用程序线程队列中)当前是否在 SQL 调用之中并在等待来自数据库的结果。当前发出 SQL 查询的线程的典型堆栈跟踪如下例所示:

"ExecuteThread: '4' for queue: 'weblogic.kernel.Default'" daemon prio=5
tid=0x8e93c8 nid=0x19 runnable [e137f000..e13819bc]
  at java.net.SocketInputStream.socketRead0(Native Method)
  at java.net.SocketInputStream.read(SocketInputStream.java:129)
  at oracle.net.ns.Packet.receive(Unknown Source)
  at oracle.net.ns.DataPacket.receive(Unknown Source)
  at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
  at oracle.net.ns.NetInputStream.read(Unknown Source)
  at oracle.net.ns.NetInputStream.read(Unknown Source)
  at oracle.net.ns.NetInputStream.read(Unknown Source)
  at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:931)
  at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:893)
  at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:375)
  at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1983)
  at oracle.jdbc.ttc7.TTC7Protocol.fetch(TTC7Protocol.java:1250)
  - locked <e8c68f00> (a oracle.jdbc.ttc7.TTC7Protocol)
  at oracle.jdbc.driver.OracleStatement.doExecuteQuery(OracleStatement.java:2529)
  at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:2857)
  at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate
(OraclePreparedStatement.java:608)
  - locked <e5cc44d0> (a oracle.jdbc.driver.OraclePreparedStatement)
  - locked <e8c544c8> (a oracle.jdbc.driver.OracleConnection)
  at oracle.jdbc.driver.OraclePreparedStatement.executeQuery
(OraclePreparedStatement.java:536)
  - locked <e5cc44d0> (a oracle.jdbc.driver.OraclePreparedStatement)
  - locked <e8c544c8> (a oracle.jdbc.driver.OracleConnection)
  at weblogic.jdbc.wrapper.PreparedStatement.executeQuery(PreparedStatement.java:80)
  at myPackage.query.getAnalysis(MyClass.java:94)
  at jsp_servlet._jsp._jspService(__jspService.java:242)
  at weblogic.servlet.jsp.JspBase.service(JspBase.java:33)
  at weblogic.servlet.internal.ServletStubImpl$ServletInvocationAction.run
(ServletStubImpl.java:971)
  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:402)
  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:305)
  at weblogic.servlet.internal.RequestDispatcherImpl.includ
e(RequestDispatcherImpl.java:607)
  at weblogic.servlet.internal.RequestDispatcherImpl.include
(RequestDispatcherImpl.java:400)
  at weblogic.servlet.jsp.PageContextImpl.include(PageContextImpl.java:154)
  at jsp_servlet._jsp.__mf1924jq._jspService(__mf1924jq.java:563)
  at weblogic.servlet.jsp.JspBase.service(JspBase.java:33)
  at weblogic.servlet.internal.ServletStubImpl$ServletInvocationAction.run
(ServletStubImpl.java:971)
  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:402)
  at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:305)
  at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run
(WebAppServletContext.java:6350)
  at weblogic.security.acl.internal.AuthenticatedSubject.doAs
(AuthenticatedSubject.java:317)
  at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:118)
  at weblogic.servlet.internal.WebAppServletContext.invokeServlet
(WebAppServletContext.java:3635)
  at weblogic.servlet.internal.ServletRequestImpl.execute(ServletRequestImpl.java:2585)
  at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:197)
  at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:170)


线程将处于运行状态。您应比较不同 Thread Dump 中的线程,查看它们是否及时接收 SQL 调用的返回结果或者它们是否在此同一调用中长时间挂起。如果 Thread Dump 似乎指示 SQL 调用的响应时间较长,则应检查相应的数据库日志,查看是不是数据库中的问题导致这种执行速度缓慢或挂起的状况。

返回页首

低速网络
WebLogic Server 与数据库之间的通信依赖于性能良好且可靠的网络,来及时地处理请求。因此,网络性能低下可导致正在等待 SQL 查询结果的执行线程被挂起或阻塞。相关的堆栈跟踪将与上面挂起的数据库小节中的示例相似。仅仅通过分析 WebLogic Server Thread Dump 不可能找到挂起或 SQL 查询速度低下的根本原因。它们给出 SQL 调用的性能存在问题的第一个提示。下一步是检查是否存在导致 SQL 调用性能不佳的数据库网络或网络问题。

死锁
应用程序级的死锁与数据库级的死锁都可导致线程挂起。您应检查 Thread Dump,查看是否存在应用程序级的死锁。有关如何执行这一操作的信息在服务器挂起 - 应用程序死锁模式中提供。数据库死锁可以在数据库日志中检测,或者通过可在 WebLogic Server 日志文件中找到的“SQL 异常”检测。下面是相关“SQL 异常”的一个示例:

java.sql.SQLException: ORA-00060: deadlock detected while waiting for resource
  at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:170)
  at oracle.jdbc.oci8.OCIDBAccess.check_error(OCIDBAccess.java:1614)
  at oracle.jdbc.oci8.OCIDBAccess.executeFetch(OCIDBAccess.java:1225)
  at oracle.jdbc.oci8.OCIDBAccess.parseExecuteFetch(OCIDBAccess.java:1338)
  at oracle.jdbc.driver.OracleStatement.executeNonQuery(OracleStatement.java:1722)
  at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java:1647)
  at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout
(OracleStatement.java:2167)
  at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate
(OraclePreparedStatement.java:404)


数据库检测死锁并通过回滚引发死锁的一个或多个事务来解决死锁这一过程通常需要一些时间,因此在回滚结束之前,会有一个或多个执行线程被阻塞。

RefreshMinutes 或 TestFrequencySeconds
如果发现数据库性能低下、SQL 调用速度缓慢或连接高峰的时期反复出现,其原因可能在于 JDBC 连接池中 RefreshMinutesTestFrequencySeconds 配置属性的设置。有关内容在探查 JDBC 故障模式中详细介绍。除非 WebLogic Server 实例与数据库之间没有防火墙,否则您应禁用此功能。

池收缩
数据库的物理连接是应当打开一次并尽可能长时间保持打开的资源,因为新的连接请求对于数据库、操作系统内核及 WebLogic Server 而言是一个相当大的资源开销。因此,应在生产系统中禁用池收缩,使这一开销保持最小程度。如果启用池收缩,一旦对该池发出的连接请求不能得到满足,空闲的池连接就将被关闭,然后重新打开。

这些活动可能会需要一些时间,因此相关应用程序请求需要的时间可能会异常的长,使用户以为系统挂起。有关如何优化 JDBC 连接池配置的信息在探查 JDBC 故障模式中提供。

返回页首

分析挂起的 WebLogic Server 实例
有关如何分析挂起的 WebLogic Server 实例的一般信息在常规服务器挂起模式中提供。

大多数情况下,首先从挂起的系统获得 Thread Dump 对于了解进展情况(例如不同的线程在做些什么以及它们为什么挂起)是非常有益的。通常,可以在生产系统上获得 Thread Dump,但是对于很早以前的 JVM 版本 (<1.3.1_09) 则应小心,因为它们可能会在 Thread Dump 期间崩溃。此外,如果 WebLogic Server 实例有大量线程,则意味着完成 Thread Dump 需要一段时间,而其余线程将被阻塞。

请进行多个 Thread Dump(5 到 10 个),这些 Thread Dump 彼此之间有若干秒钟的延迟。这使得您可以检查不同进程的进度情况。而且,它还将指示系统是否确实挂起(根本没有进度)或者吞吐速度是否极低,看起来像是系统已挂起。

有关如何进行 Thread Dump 的信息在“常规服务器挂起”支持模式或我们的文档中提供:http://e-docs.bea.com/wls/docs81/cluster/trouble.html (English)。

此外,还请检查是整个 WebLogic Server 实例挂起还是应用程序挂起。“常规服务器挂起”支持模式也包括此信息。

分析 Thread Dump 可以指示出实例的挂起是否确实是由于前一小节为什么发生此问题?中提到的某一种原因。例如,如果所有线程都在一个 DriverManager 方法(如 getConnection())中,则您已经确定出挂起的根本原因,并需要更改应用程序,以使用数据源或 Driver.connect() 来代替 DriverManager.getConnection()

Samurai 是一个非常有用的工具,可用于分析 Thread Dump 并监视不同 Thread Dump 之间线程的进度。可从 dev2dev 下载此工具,网址:http://dev2dev.bea.com/resourcelibrary/utilitiestools/adminmgmt.jsp (English)。

dev2dev 上有关分析 Thread Dump 的白皮书:http://dev2dev.bea.com/products/wlplatform81/articles/thread_dumps.jsp (English) 也有助于深入探究 Thread Dump,以了解有关服务器挂起的更多内容。

返回页首

优化 JDBC 代码和 JDBC 连接池配置的技巧
对于开发 JDBC 代码和配置 JDBC 连接池,有一些最佳的惯例,可以帮助避免常见问题并优化资源利用,避免服务器实例挂起。

JDBC 编程
为了优化 WebLogic Server 中资源的利用,节约数据库资源,应使用 JDBC 连接池进行应用程序的 JDBC 调用。在应用程序代码中建立和破坏的连接会造成不必要的开销,应当避免。要获得 JDBC 编程的一般文档,请参阅:http://e-docs.bea.com/wls/docs81/jdbc/rmidriver.html#1028977 (English)。此外,有关 JDBC 性能调整的详细信息位于:http://e-docs.bea.com/wls/docs81/jdbc/performance.html#1027791 (English)。

可以在 dev2dev Java Database Connectivity (English) 页上查看有关 JDBC 的综合性信息,它有助于优化 JDBC 代码和 JDBC 资源的利用,网址为:http://dev2dev.bea.com/technologies/jdbc/index.jsp (English)。

JDBC 连接池配置
关于如何针对生产环境配置连接池的建议,请参阅探查 JDBC 故障模式。为避免出现挂起或者性能不佳的情况,应考虑这些配置技巧。


返回页首

已知问题
您可以定期查看所用 WLS 版本的“Release Notes”,了解 Service Pack 中的“Known Issues”或“Resolved Issues”的详细信息及浏览与 JDBC 服务器挂起有关的问题。方便起见,下面提供了这些发行说明的链接:
请注意,WLS 8.1 SP3 中已经进行一些更改来解决 CR134921,其中对于特定的 JDBC 连接,用于回滚事务的调用没有立即处理,因为驱动程序必须等待任何当前正执行的语句返回。

使用搜索功能也可以搜索到“Release Notes”,还可以搜索到其它支持解决办法及与 CR 有关的信息,如需要更多帮助?中所提到的内容。如果客户签订了技术支持合同,则可以登录
http://support.bea.com/,登录后会看到为 Solutions 和 Bug Central 提供的 Browse portlet,可在其中按产品版本浏览最新提供的 CR。

需要更多帮助?
如果您已经理解这个模式,但仍需要更多帮助,您可以:
  1. http://support.bea.com/ 上查询 AskBEA(例如,使用“jdbc server hang”),以查找其它已发布的解决办法。技术支持合同客户:确保已经登录,可以访问提供的与 CR 有关的信息。
  2. http://newsgroups.bea.com/ 上,向 BEA 的某个新闻组提出更详细具体的问题。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值