首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java
根据我个人的第一手经验(这表示,在学习软件开发时我自己也曾犯过错误),我对基于数据库的软件开发为什么如此频繁地遭遇失败有一些看法。先来澄清一下,这里提到的这些项目可能一般不算失败,但是启用和部署所需的时间比原计划多出许多,原因是需要大幅重写,重新建立体系结构,或者需要充分调优。我个人把这些延迟的项目称为“失败”,因为它们原本可以按时完成(甚至可以更快完成)。

数据库项目失败的最常见的一个原因是对数据库的实际认识不足,缺乏对所用基本工具的了解。黑盒方法是指有意让开发人员对数据库退避三舍,甚至鼓励开发人员根本不要学习数据库!在很多情况下,开发人员没有充分利用数据库。这种方法的出现,原因可以归结为FUD[恐惧(fear)、不确定(uncertainty)和怀疑(doubt)]。一般都认为数据库“很难”,SQL、事务和数据完整性都“很难”。所以“解决方法”就是:不要卷入难题中,要知难而退。他们把数据库当成一个黑盒,利用一些软件工具来生成所有代码。他们试图利用重重保护与数据库完全隔离,以避免接触这么“难”的数据库。

我一直很难理解这种数据库开发方法,原因有二。一个原因是对我来说,学习JavaC比学习数据库基本概念要难多了。现在我对JavaC已经很精通,但是在能熟练使用JavaC之前我经受了许多磨炼,而掌握数据库则没有这么费劲。对于数据库,你要知道它是怎么工作的,但无需了解每一个细节。用CJava编程时,则确实需要掌握每一个细枝末节,而这些语言实在是很“庞大”。

让我无法理解这种方法的另一个原因是,构建数据库应用时,最重要的软件就是数据库。成功的开发小组都会认识到这一点,而且每个开发人员都要了解数据库,并把重点放在数据库上。但我接触到的许多项目中,情况却几乎恰恰相反。例如,下面就是一种典型的情况:

q  在构建前端所用的GUI工具或语言(如Java)方面,开发人员得到了充分的培训。在很多情况下,他们会有数周甚至数月的培训。

q  开发人员没有进行过Oracle培训,也没有任何Oracle经验。大多数人都没有数据库经验,所以并未理解如何使用核心的数据库构造(如各种可用的索引和表结构)。

q  开发人员力图谨守“数据库独立性”这一原则,但是出于许多原因,他们可能做不到。最明显的一个原因是:他们对于数据库没有足够的了解,也不清楚这些数据库可能有什么区别。这样一个开发小组无法知道要避开数据库的哪些特性才能保证数据库独立性。

q  开发人员遇到大量性能问题、数据完整性问题、挂起问题等(但这些应用的界面往往很漂亮)。

因为出现了无法避免的性能问题,他们把我找来,要求帮助解决这些难题。我最早就是从构建数据库独立的应用做起的(从某种程度上讲,在ODBC问世之前,我就已经编写了自己的ODBC驱动程序),我知道哪些地方可能会犯错误,因为我以前就曾犯过这些错误。我总会查看是否存在下面这些问题:存在效率低下的SQL;有大量过程性代码,但这些工作原本用一条SQL语句就足够了;为了保持数据库独立性,没有用到新特性(1995年以后的新特性都不敢用),等等。

我还记得这样一个特例,有个项目找我来帮忙。当时要用到一个新命令,但我记不清那个新命令的语法。所以我让他们给我拿一本SQL Reference手册,谁知他们给了我一本Oracle 6.0文档。那个项目开发用的是7.3版本,要知道,6.0版本和7.3版本之间整整相差5年!7.3才是所有开发人员使用的版本,但似乎谁都不关心这一点。不用说他们需要了解的跟踪和调优工具在6.0版本中甚至不存在。更不用说在这5年间又增加了诸如触发器、存储过程和数百个其他特性,这些都是编写6.0版文档(也就是他们现在参考的文档)时根本没有的特性。由此很容易看出他们为什么需要帮助,但解决起来就是另一码事了。

注意    甚至时至今日,已经到了2005年,我还是经常发现有些数据库应用开发人员根本不花些时间来看文档。我的网站(http://asktom.oracle.com)上经常会有:“……的语法是什么”这样的问题,并解释说“我们拿不到文档,所以请告诉我们”。对于许多这样的问题,我拒绝直接做出回答,而是会把在线文档的地址告诉他们。无论你身处何地,都能免费得到这些文档。在过去10年中,“我们没有文档”或“我们无法访问资源”之类的借口已经站不住脚了。如今已经有了诸如http://otn.oracle.comOracle技术网络)和http://groups. google.com (Google Groups Usenet论坛)等网站,它们都提供了丰富的资源,如果你手边还没有一套完整的文档,那就太说不过去了!

构建数据库应用的开发人员要避开数据库的主张实在让我震惊,不过这种做法还顽固不化地存在着。许多人还认为开发人员没办法花那么多时间来进行数据库培训,而且他们根本不需要了解数据库。为什么?我不止一次地听到这样的说法:“Oracle是世界上最可扩缩的数据库,所以我们不用了解它,它自然会按部就班地把事情做好的。”Oracle是世界上最可扩缩的数据库,这一点没错。不过,用Oracle不仅能写出好的、可扩缩的代码,也同样能很容易地写出不好的、不可扩缩的代码(这可能更容易)。把这句话里的“Oracle”替换为其他任何一种技术的名字,这句话仍然正确。事实是:编写表现不佳的应用往往比编写表现优秀的应用更容易。如果你不清楚自己在做什么,可能会发现你打算用世界上最可扩缩的数据库建立一个单用户系统!

数据库是一个工具;不论是什么工具,如果使用不当都会带来灾难。举个例子,你想用胡桃钳弄碎胡桃,会不会把胡桃钳当锤子一样用呢?当然这也是可以的,不过这样用胡桃钳很不合适,而且后果可能很严重,没准会重重地伤到你的手指。如果还是对你的数据库一无所知,你也会有类似的结局。

例如,最近我参与了一个项目。开发人员正饱受性能问题之苦,看上去他们的系统中许多事务在串行进行。他们的做法不是大家并发地工作,而是每个人都要排一个长长的队,苦苦等着前面的人完成后才能继续。应用架构师向我展示了系统的体系结构,这是经典的三层方法。他们想让Web浏览器与一个运行JSPJavaServer Pages)的中间层应用服务器通信。JSP再使用另一个EJBEnterprise JavaBeans)层,在这一层执行所有SQLEJB中的SQL由某个第三方工具生成,这是采用一种数据库独立的方式完成的。

现在看来,对这个系统很难做任何诊断,因为没有可测量或可跟踪的代码。测量代码instrumenting code)堪称一门艺术,可以把开发的每行代码变成调试代码,这样就能跟踪应用的执行,遇到性能、容量甚至逻辑问题时就能跟踪到问题出在哪里。在这里,我们只能肯定地说问题出在“浏览器和数据库之间的某个地方”。换句话说,整个系统都是怀疑对象。对此有好消息也有坏消息。一方面,Oracle数据库完全可测量;另一方面,应用必须能够在适当的位置打开和关闭测量,遗憾的是,这个应用不具备这种能力。

所以,我们面对的困难是,要在没有太多细节的情况下诊断出导致性能问题的原因,我们只能依靠从数据库本身收集的信息。一般地,要分析应用的性能问题,采用应用级跟踪更合适。不过,幸运的是,这里的解决方案很简单。通过查看一些 Oracle V$表(V$ 表是Oracle 提供其测量结果或统计信息的一种方法),可以看出,竞争主要都围绕着一个表,这是一种排队表。结论是根据V$LOCK视图和V$SQL做出的,V$LOCK视图可以显示阻塞的会话,V$SQL会显示这些阻塞会话试图执行的SQL。应用想在这个表中放记录,而另外一组进程要从表中取出记录并进行处理。通过更深入地“挖掘”,我们发现这个表的PROCESSED_FLAG列上有一个位图索引。

注意    12章会详细介绍位图索引,并讨论为什么位图索引只适用于低基数值,但是对频繁更新的列不适用。

原因在于,PROCESSED_FLAG列只有两个值:YN。对于插入到表中的记录,该列值为N(表示未处理)。其他进程读取和处理这个记录时,就会把该列值从N更新为Y。这些进程要很快地找出PROCESSED_FLAG列值为N的记录,所以开发人员知道,应该对这个列建立索引。他们在别处了解到,位图索引适用于低基数low-cardinality)列,所谓低基数列就是指这个列只有很少的可取值,所以看上去位图索引是一个很自然的选择。

不过,所有问题的根由正是这个位图索引。采用位图索引,一个键指向多行,可能数以百计甚至更多。如果更新一个位图索引键,那么这个键指向的数百条记录会与你实际更新的那一行一同被有效地锁定。

所以,如果有人插入一条新记录(PROCESSED_FLAG列值为N),就会锁定位图索引中的N键,而这会有效地同时锁定另外数百条PROCESSED_FLAG列值为N的记录(以下记作N记录)。此时,想要读这个表并处理记录的进程就无法将N记录修改为Y记录(已处理的记录)。原因是,要想把这个列从N更新为Y,需要锁定同一个位图索引键。实际上,想在这个表中插入新记录的其他会话也会阻塞,因为它们同样想对这个位图索引键锁定。简单地讲,开发人员实现了这样一组结构,它一次最多只允许一个人插入或更新!

可以用一个简单的例子说明这种情况。在此,我使用两个会话来展示阻塞很容易发生:

现在,如果我在另一个SQL*Plus会话中执行以下命令:

这条语句就会“挂起”,直到在第一个阻塞会话中发出COMMIT为止。

这里的问题就是缺乏足够的了解造成的;由于不了解数据库特性(位图索引),不清楚它做些什么以及怎么做,就导致这个数据库从一开始可扩缩性就很差。一旦找出了问题,修正起来就很容易了。处理标志列上确实要有一个索引,但不能是位图索引。这里需要一个传统的B*Tree索引。说服开发人员接受这个方案很是费了一番功夫,因为这个列只有两个不同的可取值,却需要使用一个传统的索引,对此没有人表示赞同。不过,通过仿真(我很热衷于仿真、测试和试验),我们证明了这确实是正确的选择。对这个列加索引有两种方法:

q  在处理标志列上创建一个索引。

q  只在处理标志为N时在处理标志列上创建一个索引,也就是说,只对感兴趣的值加索引。通常,如果处理标志为Y,我们可能不想使用索引,因为表中大多数记录处理标志的值都可能是Y。注意这里的用辞,我没有说“我们绝对不想使用索引”,如果出于某种原因需要频繁地统计已处理记录的数目,对已处理记录加索引可能也很有用。

最后,我们只在处理标志为N的记录上创建了一个非常小的索引,由此可以快速地访问感兴趣的记录。

到此就结束了吗?没有,绝对没有结束。开发人员的解决方案还是不太理想。由于他们对所用工具缺乏足够的了解,我们只是修正了由此导致的主要问题,而且经过大量研究后才发现系统不能很好地测量。我们还没有解决以下问题:

q  构建应用时根本没有考虑过可扩缩性。可扩缩性必须在设计中加以考虑。

q  应用本身无法调优,甚至无法修改。经验证明,80%90%的调优都是在应用级完成的,而不是在数据库级。

q  应用完成的功能(排队表)实际上在数据库中已经提供了,而且数据库是以一种高度并发和可扩缩的方式提供的。我指的就是数据库已有的高级排队(Advanced QueuingAQ)软件,开发人员没有直接利用这个功能,而是在埋头重新实现。

q  开发人员不清楚bean在数据库中做了什么,也不知道出了问题要到哪里去查。

这个项目的问题大致如此,所以我们需要解决以下方面的问题:

q  如何对SQL调优而不修改SQL。这看起来很神奇,不过在Oracle 10g中确实可以办得到,从很大程度上讲堪称首创。

q  如何测量性能。

q  如何查看哪里出现了瓶颈。

q  如何建立索引,对什么建立索引。

q  如此等等。

一周结束后,原本对数据库敬而远之的开发人员惊讶地发现,数据库居然能提供如此之多的功能,而且了解这些信息是如此容易。最重要的是,这使得应用的性能发生了翻天覆地的变化。最终他们的项目还是成功了,只是比预期的要晚几个星期。

这个例子不是批评诸如EJB和容器托管持久存储之类的工具或技术。我们要批评的是故意不去了解数据库,不去学习数据库如何工作以及怎样使用数据库这种做法。这个案例中使用的技术本来能很好地工作,但要求开发人员对数据库本身有一些深入的了解。

关键是:数据库通常是应用的基石。如果它不能很好地工作,那其他的都没有什么意义了。如果你手上有一个黑盒,它不能正常工作,你能做些什么呢?可能只能做一件事,那就是盯着这个黑盒子发愣,搞不明白为什么它不能正常工作。你没办法修理它,也没办法进行调整。你根本不知道它是怎样工作的,最后的决定只能是保持现状。我提倡的方法则是:了解你的数据库,掌握它是怎么工作的,弄清楚它能为你做什么,并且最大限度地加以利用。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论