`
dpsnet
  • 浏览: 7701 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

数据库驱动应用程序中影响性能的反模式

阅读更多

数据库驱动应用程序中影响性能的反模式

作者 Alois Reitbauer 译者 张晓庆 发布于 2009年11月13日 上午12时1分

社区
Architecture
主题
数据访问 ,
性能和可伸缩性
标签
测试 ,
ORM

几 乎所有现代应用程序都要通过数据库实现数据持久化。数据库访问层经常要对严重的性能问题负责。一旦遇到数据库的问题,大多数人开始研究数据库本身。正确的 索引和数据库结构对提高性能非常关键。然而,很多时候糟糕的性能或可伸缩性问题的罪魁祸首却是应用程序层,而不是数据库。

应用程序层控制并驱动数据库的访问。这一层的问题不能从数据库上得到补偿。所以要想得到高性能和扩展性,数据访问逻辑的设计非常关键。虽然数据库驱 动的应用程序中使用情况各不相同,但所有问题能够归结到几个反模式上。分析你的应用程序中是否使用了下列的反模式,并且解决他们,能够以最小的代价简单让 你的软件更快、 更稳定。

对象/关系映射的误用

对象/关系映射已经成为现代数据库应用程序的中心部分。对象/关系映射让人从面向对象软件中翻译和访问关系型数据的重担中解脱出来。它们向应用程序 人员隐藏了数据访问大部分的复杂逻辑。由于开发人员更专注于实际的业务逻辑,而不是基础架构细节,会使得生产效率更高。对象关系层不需要看到细节就可以轻 松操作复杂的对象图。这经常让人产生错误的印象,认为这些框架让人从设计数据访问逻辑的重担中解脱了出来。

开发人员经常认为数据访问框架很容易就把一切搞定了;然而,不理解内部工作机制就使用对象/关系映射框架,很多时候会导致程序性能低下。主要有两个误解引起了这些问题──加载的行为和加载的时间。

对象/关系映射基于每个对象加载数据。这意味着只有当一个对象被请求或者访问时,需要的SQL语句才会被创建并执行。这个原则非常普遍,乍一看多数情况下没问题。但同时它也常常是性能和扩展性问题的原因所在。

让我们看一个简单的例子。在一个存储地址信息的数据库中,我们有一张表存储人和一张表存储地址。如果我们想得到每个人的名字及其居住的城市,我们不 得不遍历人那张表,然后访问地址信息。下图显示了使用直接(out-of-the box)查询机制的结果。可以看出,这个简单的例子就导致了大量的数据库查询。

antipattern1.png

这直接引起了对象/关系映射中第二个重要的细节──加载时间。对象/关系映射-如果没有事先告知-会尽量晚地加载数据。这一行为就是延迟加载。延迟 加载保证了数据尽可能晚地加载,目的是执行尽量少的数据库查询,同时避免创建不必要的对象。虽然这个方法通常情况下是可行的,但当它访问那些没有加载的数 据,而数据连接已经不存在时,就可能导致严重的性能问题,以及所谓的LazyLoadingExceptions。

在如上所述的情况下,使用专门的数据查询能够显著提高性能。

因此,虽然对象/关系映射在数据访问的开发方面作用很大,设计合适的数据访问逻辑的重担仍然需要我们挑起。像dynaTrace这样带有工具的动态架构验证,能够帮助有效地识别程序中性能的弱点,并能主动解决。

加载了太多数据,实际不需要这么多

数据库访问中经常出现的另外一个反模式是加载了太多的数据,而实际上不需要这么多。导致这样的原因很多。快速应用程序开发工具提供了简单的方式,能 把数据结构和用户接口控制连接起来。由于数据层由领域对象构成,通常它们包含的数据要比实际显示的多得多。再次使用地址薄作为例子。这一次需要显示人的名 字及其居住城市。两个对象──地址和人──都被加载了,而不是只加载这3个字段。这导致了数据库、网络和应用程序层的大量开销。使用专门的查询能够大大减 少查询的数据量。然而这种性能的提升需要额外的工作去维护。表中新增一列可能需要对数据访问层修改多处。

antipattern2.png

设计的服务接口不合理也经常引起这种反模式。服务接口通常要设计的很通用,以支持大量的用例。其好处是各种各样的用例中都可以使用服务。另外,用例 要比后台服务实现变化的快得多。这会导致服务接口在某些场景下不适合。开发人员然后不得不使用一些补救方法,这可能导致数据访问逻辑效率低下。这个问题在 数据驱动的Web Services上经常出现。

为了克服这些问题,开发过程中需要不断地分析数据访问模式。如果是敏捷开发方法,每个用户故事完成后都应该检查数据访问逻辑。除此之外,应该跨应用程序用例分析数据访问模式,以理解数据访问逻辑,这样能够在开发中相应地优化数据访问逻辑。

未充分利用资源

数据库是应用程序中资源的瓶颈,所以使用越少越好。通常情况下大家对数据库连接的使用关注甚少。像任何共享的资源一样,数据库连接会严重影响整个系 统的性能。尤其是web应用和使用对象/关系映射框架并用了延迟初始化的程序,会让数据库保持连接的时间比需要的更长。处理开始时获得连接,直到页面生成 完成或者再也没有数据访问了才断开。在使用对象/关系映射的应用程序中,连接经常保持着以避免可恶的延迟初始化的问题。通过重新设计数据访问逻辑,把它从 后处理(比如页面生成)中分离出来,应用程序的性能和扩展性能得到极大的提高。

下图展示了10个并发数据处理线程的反应时间。第一个使用了1个数据库连接,第二个使用了2个连接,第三个使用了2个连接,但是有2/3的处理是在释放连接之后执行的。第三个场景数据访问经过更好的设计,仅用了1/10的资源就获得了几乎同样高的性能。

antipattern3.png

一刀切

一刀切是一种反模式,开发过程中经常见到,敏捷团队中则更多。这种反模式的特征是开发了主要功能之后,所有的数据访问就同样对待,好像它们没有任何区别。然而,区别对待不同类型的数据和查询可以显著提高应用程序的性能和扩展性。

应该对数据进行分析,考虑其生命周期的特性。它是否经常变化,它是可修改的还是只读的呢?数据的访问频率和访问模式,就隐含了一些潜在的代码,比如 可以做缓存。访问频率也暗含了一些线索,比如在哪里做优化更有意义。这可以避免过早进行优化以及不必要的优化,保证了性能调优效果最好。

对数据的使用模式进行分析也有助于调整数据访问层。理解真正使用了哪些数据有助于优化加载策略。比如,理解用户怎样浏览搜索结果对优化fetch size很有用。知道了用户是否查看订单详细信息可以给订单选择延迟还是立即加载。

除数据之外,查询也应该被分析并分类。重要的因素包括查询时间、执行频率、是否用于交互用户的上下文或者批量处理的场景中。事务特性有助于更好地调整查询的隔离级别。

比如,在同一个连接中运行用户短暂的交互查询和时间很长的报表查询,很容易导致终端用户的体验很糟糕。报表查询花费的时间很长,会占用大量的数据库 连接,让终端用户的查询无法拿到连接。通过给不同的查询类型使用不同的数据库连接池,会使终端用户的性能更可预测。降低数据库查询中不需要的隔离级别,也 能引起性能和扩展性的显著提高。

糟糕的测试

最后,缺少测试或者测试不正确是数据库访问应用程序性能和稳定性问题的一个主要原因。最近我曾就这一主题作了一个演讲,并询问听众是否把数据库访问 看作应用程序中一个性能问题。虽然他们都赞成,但没人有这样的测试流程,来测试数据访问的性能。所以虽然这个话题看上去是很重要,大家似乎都没有花时间去 做。然而,即使有测试流程,这也不一定说明测试就是正确的。虽然代码完成后能够立刻发现数据访问逻辑中的很多问题,但通常很晚之后才执行测试,比如负载测 试的时候。由于在生命周期的晚期才改动,可能需要修改架构,从而引起额外的开发和测试工作,这带来了很高的不必要的代价。

而且,必须设计一些测试用例,来测试真实世界的数据访问场景。测试数据访问必须在并发模式下进行,并且使用不同的访问类型。只有结合使用读/写访问才可能识别死锁和并发的问题。除此之外,输入的数据应该多种多样,以避免数据库访问时经常命中缓存,这是不切合实际的。

很多时候人们对预期的负载知之甚少,也不知道去测试哪些负载。很不幸的是,根据我的经验这种情况比比皆是。然而,不能把这当作借口,不定义负载和性能标准。要知道,定义一些标准比一点也不定义要好得多。

如果你对性能数据真的毫无头绪,最好是使用负载渐增测试法,逐步增加负载,直到达到了应用程序的最大值。这样你就知道了应用程序的负载峰值。如果负 载峰值既合理又现实,那就说明你做的不错。否则你得知道在哪方面提高性能。大多数情况下初始的测试表明,应用程序能够处理的负载要比期望的少得多。

结论

数据库访问是影响现代应用程序性能和可伸缩性的一个关键点。虽然框架支持构建数据访问逻辑,仍然需要对数据访问逻辑投入相当的精力,以避免种种陷阱和问题。问题之关键是要理解应用程序数据访问层的动态和特性的一切细节。

作者简介

Alois Reitbauer是dynaTrace软件公司的一名高级性能架构师。在研发部门任职期间,他参与制定了dynaTrace的产品策略,并与大客户紧密 合作,为应用程序的整个生命周期实现了性能管理解决方案。Alois Reitbauer在Java和.NET领域有10年的开发和架构经验。

译者注

本文“未充分利用资源”一节中举了这样一个例子:第一个场景使用了1个数据库连接,第二个场景使用了2个,第三个场景使用了2个,但是后处理(比如 页面生成)是在释放数据库连接之后进行。从图表看出,第一个场景的反应时间最长,第三个场景的反应时间与第二个差不多,但是只用了1/10的资源。

根据上下文,实际上应该是这样的:第一个场景使用了1个连接,第二个使用了10个,第三个使用了1个,但是后处理(比如页面生成)是在释放数据库连接之后进行。只有这样,文中的结论才合理。

事实上,InfoQ总站该文后面Ray Davis有一个跟帖,就该问题提出了疑问,认为第二个场景的2个连接应该为10个。除了Ray Davis的疑问,译者认为,第三个场景的2个连接应该是1个,这样上下文不矛盾了。

阅读英文原文Performance Anti-Patterns in Database-Driven Applications


感谢张龙 对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com 。也欢迎大家加入到InfoQ中文站用户讨论组 中与我们的编辑和其他读者朋友交流。

<script type="text/javascript"> var replyEnabled=true; var forumID=1; var threadID=13804; var previewText='预览'; var pleaseWait='请稍候……'; var reply='回复'; var postMessage='发送消息'; var errorSubject='请输入主题。'; var errorBody='您不允许发表无内容的消息。请输入您的消息并重试。'; var cancel='取消'; var goBackOrEdit='返回/编辑'; var re='Re:'; var lastMessage=0; var stopWatchText='取消对此讨论的关注'; var startWatchText='关注此讨论'; var descending='false'; var ctxPath= ''; var postAddress= ctxPath + '/forum/post!post.action?language=' + 'zh'; var postAddWatches= ctxPath + '/forum/watches!add.action'; var postRemoveWatches= ctxPath + '/forum/watches!remove.action'; var loggedIn=true; var forumNotificationDefault=false; </script><script src="http://www.infoq.com/scripts/forum.js" type="text/javascript"></script><script src="http://www.infoq.com/dwr/interface/ForumNotifications.js" type="text/javascript"></script>

 

第二个使用了2个连接 发表人 vg zhang 发表于 2009年11月13日 上午3时59分 <script type="text/javascript"> Event.observe('tooltip_50150', 'mouseover', initializeTooltip); Event.observe('tooltip_50150', 'mouseout', deintializeTooltip); </script>

 

对象和数据谁需要更多的关注?这是一个问题...... 发表人 Peter Yu 发表于 2009年11月17日 下午8时33分 <script type="text/javascript"> Event.observe('tooltip_50312', 'mouseover', initializeTooltip); Event.observe('tooltip_50312', 'mouseout', deintializeTooltip); </script>

 

  1. 返回顶部

    第二个使用了2个连接

    2009年11月13日 上午3时59分 发表人 vg zhang

    应该是 第二个使用了10个连接

  2. 返回顶部

    对象和数据谁需要更多的关注?这是一个问题......

    2009年11月17日 下午8时33分 发表人 Peter Yu

    坦白说,数据对象映射,比如AR这样的实例,对于传统应用中,数据以少量的行为单位,做为对象属性存在,对于开发的便利非常有必要。
    但 对于稍稍宽范一点的web应用---数据以大量的列表集为单位,去做这样的映射必须格外小心。性能瓶颈的陷阱随处可见。这时候的数据访问层通常需要更专业 的程序工程师、DBA来定制实现并对外抽象。访问的范围和形式都是严格约束的。缺少相当的灵活性(对比通常的ORM或AR)却保证了性能和安全。

分享到:
评论

相关推荐

    Java数据库应用程序编程指南随书源码

    通过这些资源,读者可以更直观地学习Java数据库连接(JDBC)API的使用,以及如何设计和实现数据库驱动的Java应用程序。 首先,我们要理解Java数据库应用程序的基础,这通常涉及到JDBC(Java Database Connectivity...

    各种数据库驱动包下载

    数据库驱动包是连接应用程序与不同数据库系统的关键组件,它们提供了标准接口,使得程序可以与多种数据库进行通信。本文将深入探讨这些数据库驱动包,包括DB2、MySQL、Oracle、SQL Server 2000和2005以及Sybase。 ...

    利用Eclipse创建数据库web应用程序.rar

    在IT行业中,开发数据库驱动的Web应用程序是一项常见的任务,而Eclipse作为一个强大的集成开发环境(IDE),提供了便捷的工具来支持这一过程。本教程将详细讲解如何利用Eclipse创建一个数据库Web应用程序,帮助...

    数据库驱动

    数据库驱动是连接应用程序与数据库系统的关键组件,它允许程序通过标准接口与特定的数据库进行通信。在本主题中,我们将深入探讨三个主流的关系型数据库管理系统(RDBMS)——MySQL、SQL Server和Oracle,以及它们...

    数据库驱动大全

    总结来说,"数据库驱动大全"这个压缩包包含的是三种主流数据库系统的连接组件,它们使得开发者可以轻松地在各种应用程序中集成对这些数据库的访问功能,提高开发效率,同时也保证了不同数据库系统的互操作性。...

    H2数据库驱动包

    H2数据库驱动包是专为H2数据库设计的连接组件,它使得开发者能够轻松地在应用程序中集成H2数据库,以便进行数据存储、查询和管理。H2数据库是一款开源、轻量级、高性能的关系型数据库,广泛应用于开发测试和小型项目...

    数据库驱动全集

    在开发过程中,选择合适的驱动对于应用程序的性能和稳定性至关重要。每个数据库驱动都有其特点和适用场景,开发者应根据项目需求和目标平台选择合适的驱动,并确保正确配置和使用,以实现高效、可靠的数据库操作。

    mysql+oracle数据库驱动程序

    在开发过程中,为了连接这两种数据库,我们需要对应的数据库驱动程序。 首先,我们来看MySQL的驱动程序——`mysql-connector-java-3.1.14-bin.jar`。这是MySQL官方提供的Java数据库连接器(JDBC)驱动,用于Java...

    ASP.NET数据库应用程序开发教程

    ASP.NET是一种由微软开发的服务器端Web应用框架,主要用于构建动态网站、Web应用程序和服务。它提供了丰富的工具和功能...通过理解并掌握上述知识点,开发者可以有效地利用ASP.NET构建高效、安全的数据库驱动Web应用。

    Oracle数据库驱动jar包

    Oracle数据库驱动jar包是Java应用程序连接Oracle数据库的关键组件,它提供了Java编程语言与Oracle数据库间的接口。在J2EE(Java 2 Platform, Enterprise Edition)开发中,这些jar包是必不可少的,因为它们包含了...

    web项目中常用的几种数据库驱动

    在Java Web开发中,数据库驱动是连接应用程序与数据库的关键组件。它们允许程序通过标准的Java Database Connectivity (JDBC) API来执行SQL语句,从而实现数据的存储和检索。本篇文章将详细介绍Java Web中常见的几种...

    SQLServer数据库应用程序性能优化方法

    ### SQLServer数据库应用程序性能优化方法 #### 一、引言 SQL Server 是 Microsoft 开发的一款高性能的客户端/服务器模式下的数据库管理系统,它采用单进程、多线程的技术,在核心层实现了数据完整性控制,具备...

    Struts 1.1、Hibernate 3.0和SQL Server 2005数据库驱动包

    Struts 1.1、Hibernate 3.0 和 SQL Server 2005 数据库驱动包是构建基于Java的企业级Web应用程序的关键组件。这些技术在2000年代中期广泛应用于开发面向服务架构(SOA)和模型视图控制器(MVC)模式的系统。 Struts...

    java jdbc 连接数据库驱动程序包(oracle mysql sybase)

    Java JDBC(Java Database Connectivity)是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,它提供了标准的方法来创建、管理以及关闭数据库连接。本压缩包包含的三个文件分别对应于Oracle、MySQL和...

    常用数据库的驱动

    数据库驱动是连接应用程序与数据库系统的关键组件,它允许程序通过标准接口与各种数据库进行通信。在Java编程中,这种接口通常由Java Database Connectivity (JDBC) 提供。JDBC是Java平台上的一个标准API,用于访问...

    常用数据库JDBC驱动程序打包下载

    JDBC驱动程序是连接Java应用程序和数据库之间的桥梁,使得开发者能够通过编写Java代码来执行SQL查询、更新数据库等操作。本压缩包"常用数据库JDBC驱动程序打包下载"提供了三种主流数据库的JDBC驱动,包括Oracle、...

    sqlserver2000数据库驱动包

    这个"sqlserver2000数据库驱动包"包含了用于连接和操作SQL Server 2000数据库的驱动程序,尤其对于Java开发者来说,其中的jar文件至关重要。 驱动包中的jar文件,通常是`mssql-jdbc.jar`或`jtds.jar`,这是Java应用...

    常用数据库的驱动jar包

    在IT行业中,数据库是数据存储和管理的核心工具,而数据库驱动则是连接应用程序与数据库的关键组件。本资源包包含了几个常用数据库的驱动jar包,适用于不同的数据库管理系统(DBMS)。以下是这些驱动jar包的详细信息...

    数据库连接驱动

    数据库连接驱动是Java编程中与数据库进行交互的关键技术,它允许Java应用程序通过特定的API(应用程序编程接口)来建立和管理到数据库的连接。在Java中,这种连接通常基于Java Database Connectivity (JDBC) API。...

Global site tag (gtag.js) - Google Analytics