诊断应用数据库的性能瓶颈

 

J2EE的崛起

J2EE作为Web应用开发的标准企业计算平台面世,其实力越来越强大,日益普及。J2EE支持遗留应用程序和接口、多种操作系统、分布式和群集式环境,以及高量关键任务应用程序,同时支持安全和管理与监控。

通过提供一种开发分布式、可伸缩应用程序的框架和蓝图,J2EE使公司及其开发者能够集中注意力去编写模块化的定制应用程序代码,并且不必担心安全、资源管理和可伸缩性的细节。

行业领先的应用程序服务器,例如BEA WebLogic Server,能提供许多功能和服务。多个WebLogic服务器实例的群集和故障恢复功能提供了可靠性、可用性和可伸缩性。它们也提供诸如安全和资源管理之类的其他服务,例如执行线程池、EJB高速缓存和JDBC池。第三方所写的Java数据库连接(Java Database Connectivity,JDBC)驱动程序进一步抽象和简化不同数据库管理系统(DBMS)间数据存取的编码。

这就是一个强大的平台,该平台能够大大简化并且抽象开发分布式、高量企业应用程序的低层细节。

 

J2EE性能的挑战

听上去没有什么会出问题,是吗?的确如此,除了应用程序不能达到最终用户或者服务级协定所要求的性能标准之外。一个开发项目和商业创新是否成功要视其迅速检测、诊断和解决这些性能问题的能力而定。

由于它们的复杂性日益增加,与早期的僵硬应用程序环境相比,在这些多层的、分布式J2EE应用程序中的性能瓶颈更难以诊断。J2EE环境包含多层相互连接的软件和硬件组件,它们相互作用,满足任何既定的最终用户请求。性能团队的成员——设计师、开发者、应用服务器管理员和数据库专家——他们有自己对系统的见解,而且可能拥有他们自己的经验仓库或者“设备中心”的诊断工具。但是这个团队的成员怎样一同工作将问题分离出来呢?如果没有对J2EE系统所有组件的全面广泛的了解,如果他们不能相互交互,那么一位性能专家怎样找出哪台服务器速度慢?哪个组件速度慢?哪种资源不足?数据库引擎是主要瓶颈,还仅仅是一个次要因素呢?

这种挑战可能会使无准备的人或装备不良的人畏缩不前。这种困惑可能使团队的成员焦头烂额,或者更严重时,他们会任意指责过失。

 

“是应用程序,还是数据库?”

公司里最常见的挫折就是面对表现不佳的分布式J2EE应用程序感到困惑:“它是应用程序,还是数据库?我们应该从哪里开始修理?”通常情况下,会有人斗胆作出一种推测,不再继续在浩繁的代码寻找错误的或者低效率的算法,或者放弃彻底搜寻SQL语句和数据库表格的作法。换句话说,他们挑出应用程序或者数据库(或者在最坏情况下,两个都选),并努力将从谜团中分离出来的片断最优化。

令人遗憾的是,这种解决问题的观点经常是过度简化的,这是因为应用程序、数据库、WebLogic Server都不是在孤立地运转。对这个问题的一种综合解决方法需要提高对这个相互联系的系统内所有三个部分的能见度:WebLogic资源利用和配置、应用程序架构以及数据库查询的执行,而且包括基本的硬件基础设施性能。

有了对这些子系统相互作用的了解,性能团队的成员就能有效地分离有问题的组件,并将问题交给正确的职能专家进行处理。

 

对相互之间关系的了解是关键

在此,了解和相互关系是两个关键词。没有这两个关键词,检测和根除的要求就使诊断极其艰难。

我们需要了解WebLogic服务器运行时JMX的性能度量。我们需要了解SQL查询的计时和结构,以及数据库引擎所运行的存储过程。而且最重要的是,我们需要了解最终用户发出的请求在跨越整个分布式系统时的端到端计时,而且所有的组件要与JDBC连接池交互,DBMS的调用要以显式的基于单个请求的方式来制定。

我们不仅需要了解方法层级的应用程序计时,而且需要了解每个组件与其他组件相互作用形成应用程序架构的方式。大多数J2EE性能监视工具所提供的分离度量方法和JDBC计时都脱离了应用程序架构,所以几乎是没有用的。分离的SQL语句执行响应次数也是如此,脱离了该语句产生的位置、被调用的次数或者与其最终用户请求的数据库的相互作用,几乎也是没有用的。

理想的工具解决方案对定制的J2EE应用程序与WebLogic服务器资源和DBMS相互作用的方法提供深入的洞察,并提供对这些相互作用怎样影响每个最终用户事务的总响应时间的深入理解。这些相互作用的高级表现概述如图1。

典型的应用数据库性能瓶颈

现在或许你正在想:“现在理论知识足够了,我怎样修复我的应用程序呢?”下面的章节将讨论性能瓶颈的三个主要类型,它们是通过测量和分析许多现实世界的J2EE应用程序的性能后提炼出来的,包括几个卓越的金融、电信、以及“财富100强”制造业公司的J2EE系统。这绝不意味着它是一个毫无遗漏的列表,或者是一个可以用来调优任何应用程序的、一步一步遵循的“调试清单”。由于分布式的本质和错综复杂的架构,每个J2EE应用程序都有它自己的独特的性能特点,但是也有一些需要避免的共同的缺陷。

下面我将介绍应用数据库的三种性能,其中具体的示例是通过运行BEA PetStore示范应用程序收集的,并且采用Quest Software的PerformaSure收集和分析性能数据。

 

过量的数据库调用

 

“客户”端数据处理和结果集的卷动

到目前为止,在J2EE数据库应用程序中最严重的性能瓶颈来自从用户应用程序对数据库引擎的过量调用。这些不必要的额外调用不一定是SQL查询的Execute()或Update(),但几乎总是跟与数据库的其他交互有关,例如ResultSet操作。一个常见的错误是指定了过于精确的查询条件,然后使用ResultSet.Next()详细地搜寻返回的数据,每次一行。这种作法会使应用程序的性能由DBMS内的数据集来决定——对于大的表格来说,搜寻量可能是巨大的;我曾见过客户站点的数据所执行的每个SQL查询都向ResultSet.Next()发出了超过50,000个调用指令。

如果有些数据处理必须在应用程序代码内进行,应考虑从DBMS大批取得所要求的数据,避免让应用程序反复回调数据库,从数据集里取回每一记录。

例1:过量的结果集卷动

图2显示:当PetStore 应用程序内的“commitorder”HTTP请求执行servlet、JSP和EJB代码,并最终调用DBMS的SQL语句时,该请求的PerformaSure重建。右边彩色编码的标尺表明哪个方法执行迅速(冷蓝色),以及哪个方法是昂贵的热点方法(火红色)。右边的工具提示提供了请求的全部计时和调用数。显然,在右边的数据库节点是一个热点,并且需要更进一步的调查。

通过放大所识别的热点,我们能看出该事务执行的全部JDBC操作的详细计时和调用数,例如打开一个JDBC连接、“SELECT”SQL语句的创建和执行、ResultSet.Next()卷动以及最后还有关闭连接。EJB方法“GetItem”被调用了7次(对应于7个commitorder HTTP请求),快速运行的SQL语句被执行了7次,并且在ResultSet中移动了672次。与执行实际查询相比较,过度地与DBMS反复交互反而花费了更多的时间!这不是一种可伸缩的架构——随着DBMS中的数据集增长,并且随着更多并发的最终用户执行事务,这类性能问题只能会恶化。

两个查询而不是一个查询

另一个经验法则是将SQL查询和更新的设计留给数据库专家,因为他们对各种各样的表格大纲和索引非常熟悉。编写EJB的开发者往往对他(或者她)想从数据库引擎获取什么样的数据,或者需要更新什么样的数据有自己的看法。困难在于怎样编写执行任务所必需的最少、最有效率的查询和更新。学会只选择在应用程序中真正需要的数据是至关紧要的。这样,降低了RDMS必须执行的处理数量,并且将查询和网络中发送数据的数量极小化。

任何基于集合的处理都以DBMS实现最有效,而不是通过网络获取并在WebLogic服务器EJB层的应用程序逻辑内执行处理最有效。封装应用查询和更新数据的规则及条件的“业务逻辑”保存在EJB层,而实际的实施细节最好由数据库引擎来处理。低级的查询处理逻辑,例如为临时表选择初始数据,并基于该数据进行进一步的查询,最好由DBMS以存储过程的形式进行处理。

 

数据库连接池问题(JBDC)  

连接池泄漏

当用户应用程序内的一个组件(通常是一个EJB)从一个连接池请求一个连接、查询或者更新某些数据,最后释放连接失败时,就会发生连接池泄漏。虽然通过检查WebLogic JMX性能度量(“连接数目”和“等待数目”)以及观察DataSource.GetConnection()缓慢的反应时间,能够很简单地检测到一个连接池迅速达到它的最大连接数目,但是很难在应用程序代码本身内准确指出泄漏的源头。如果存在多个最终用户请求(因此应用程序代码有多个部分),这些请求从同一个JDBC连接池分配连接,这时找出连接池泄漏更加困难:到底哪个请求没有释放连接呢?

 

为了解决这个问题,需要一个工具,它能够基于单个请求明确地映射应用程序与数据源的交互。下面的例子展示了PetStore的“CommitOrder”HTTP请求分配和释放与数据源连接的过程。

例2:连接池泄漏

图4显示了两个WebLogicJDBC度量指示连接池泄漏的证据。第一个图显示连接池的连接数目迅速增加到了连接池的最大数目——400个连接。在连接池达到最大连接数目的同时,等待连接的请求数量(等待空闲连接的请求数目)不断增加,这表明可能已经发生了连接泄漏。但是连接泄漏发生在源代码的哪个部分呢?

图5显示了“产品”请求,并且标识了该请求中两个单独的应用程序代码片分配和释放JDBC池的连接。通过对这些区域的快速分析提供了关于连接池是如何使用的详细信息。

在图5中我们标识的第二个区域内,我们能立即找出性能问题的根源。在这一时间段内,EJB共调用GetProduct()804次,从而调用executeQuery()804次(工具提示不显示)。比较getConnection()和Connection.close()的次数,可以看出:尽管请求JDBC连接1,012次,但是只释放了756次。那些GetConnection()调用的红色显示的信息和计时信息可以明确证实这将引起性能退化。值得注意的是,尽管连接泄漏很容易用度量数据加以检测,但是如果多个事务或者在同一事务内的多片代码都在同一个JDBC池内分配连接,那么这些泄漏的代码根源不是无法精确识别,就是难以精确识别。

JDBC连接池的大小

一个良好的经验法则是测量执行线程池的大小,乘以每个最终用户事务(线程)使用的并发数据库连接平均数,再加上10%的峰值时期负荷。平均每个事务的连接数通常是一个,无论所需的峰值负荷缓冲区是多大,JDBC连接池和执行队列大小都相同。通过使用大的JDBC池运行测试,并观察在任何给定的执行队列大小前提下所用JDBC连接池的平均值和峰值,可以反复调整该连接池的大小。还应该监控调用Database.GetConnection()的时间,以保证没有花太长的时间来等待JDBC资源。

在生产系统里使用JDBC池时,应牢记的第二点是:要设置最初池大小总是等于最大连接池大小。通过这样做,在池中创建所有连接的性能开销都将发生在WebLogic Server启动的过程中,而不是发生在最终用户运行时中。

 

错误组建的数据库查询

组建不良的SQL语句或者储存过程

这个常见问题与早先讨论的“两个查询而不是一个查询”的性能瓶颈有关。在这种情况下,虽然每个最终用户请求仅仅调用一个SQL语句或调用储存过程一次(或者适当的次数),然而仍然需要花费相当多的时间去执行。幸运的是,很容易用先进的性能工具检测到运行时间长的语句,并且只要确认应用程序与数据库之间有效交互,就可以让数据库管理员调优不合适的SQL语句或者储存过程。

关键是拥有一种工具,通过拆分具有排他性计时信息和调用次数的每个语句所执行的各个JDBC操作,单个用户就能够排除是应用程序设计导致了问题。然后,该问题可以交给DBA,DBA使用其特有的DBMS性能工具进一步深入诊断表架构、索引和锁定。

 

数据库操作的另一个性能问题是:在执行存储过程时或者Rollback()完成数据库事务发生错误时抛出的异常。在我们遇到的几个场景中,对DBA而言,能非常迅速地确定这些异常——即能在比较短的时间里完成确定。问题是需要了解正在调用应用程序代码中的哪些方法、哪些存储过程正在抛出异常,以及筛选合适的信息传送给合适的专家。例如,当应用程序需要调用Rollback()和Commit()操作时,前面示例所示的工具提示会提供异常退出信息。

 

没有有效利用语句缓存

在应用程序开发期间,当要执行的一个查询的结构已知,但是在运行时将使用不同的参数时,这时最好使用事先准备好的语句,并在运行时将参数传递给语句。这样可以缓存事先编译好的查询,该查询可以通过WebLogic准备好语句缓存来访问,而不用反复地将其传递给DBMS并在DBMS中进行反复地编译。

默认值是零——在没有用户修改设置时,不缓存任何语句或者数据。为应用程序选择的缓存大小的有效性可以通过比较WebLogic JMX性能度量中的“准备好语句缓存的命中率和非命中率”来验证――通常而言,那些高速缓存尺寸应该等于所有最终用户请求类型中通常执行的查询数目。

结束语

在一个承认频繁前期测试价值的组织内实行这样的经验法则,能够降低花费在无限QA循环上的时间,并且极大的加快应用程序的准备就绪过程。通过把资源如实地重新组合,使资源能够在硬件架构和期望的最终用户负荷方面密切反映生产环境的分段运输环境,就可以在引起生产问题和使顾客不满意之前检测到许多性能瓶颈,并加以诊断和解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值