代码改变世界

关于数据访问模式(六)—— 资源管理模式的重要性

2005-08-08 01:12 FantasySoft 阅读(...) 评论(...) 编辑 收藏

        随着硬件的日新月异,现在设计软件已经不再像十年前那样对软件占用的内存、软件运行消耗的CPU斤斤计较了。如果你有在DOS下写程序的体验,那么当年经常为物理内存不够而头痛的经历或许到现在还历历在目吧。还记得十年前会有很多书籍是专门讲述如何去优化程序,使其占用更少的内存。但是到现在,恐怕都难觅这类书籍的芳踪了。硬件厂商现在似乎为我们程序员创造着一个资源永动机,为我们的软件提供永远也用不完的资源。程序员的精力也不会过多花在如何高效利用资源上了,有时候甚至是没有办法去想。譬如,写了那么多所谓的OO程序,你是否会考虑自己的程序里面构造了多少的对象实例,而这些对象又占用了多少内存呢?C++尚可以使用sizeof看个仔细,但是我心爱的Java连sizeof都没有,我该拿什么去看呢?好了,先别想这些偏门的了,但是自己在初学Java的时候,会考虑使用ArrayList带参数的构造函数以减少数组创建的次数[1]吗?噢,我发现自己已经被这强大的硬件资源宠坏了。忆苦思甜一下,俺当年用的是一台有着1M内存和210M硬盘的386。当你面对着这样配置的机子,你还会对资源大手大脚吗?爱惜这些资源,好好地利用它们,它们会给你最好的回报。忽视了资源的管理,你将会面对一个响应时间(Response Time)很长,响应率(Responsiveness)很低以及吞吐能力(Throughput)很小的系统[2]
        天马行空了一番,让我们再次回到数据访问上来。首先我们并非直接使用硬件资源,而是使用操作系统提供的硬件资源的抽象接口。你应该会了解操作系统的主要功能,如进程管理,内存管理,文件管理等,操作系统会为我们的程序分配内存,分配CPU周期,操作文件,我们并不需要在硬件资源使用的细节花太多的心思。在操作系统提供这样的硬件抽象基础上,还有其他相关的软件进行更进一步的抽象,特别的就是数据库软件了。经过操作系统和数据库软件组织、优化并且抽象之后,呈现在数据访问方面的资源不再是内存、CPU周期和文件了,而是Connection、Statement、ResultSet以及Transaction等等。
        1、Connection:应用程序通过Connection来跟数据库进行交互,它是所有数据库操作的基础。初始化一个Connection,需要进行的操作相当复杂:封装Socket操作、用户验证和授权等,因此开销是相当大的。通常我们会采用Connection Pool来存放一定数量的处于Open状态的Connection以减少频繁初始化Connection所带来的开销;
        2、Statement:在Connection基础上创建的Statement负责数据库具体的增删查改操作并且返回相应的ResultSet。尽管创建Statement的开销并不大,但是使用PreparedStatement来存储反复使用的SQL语句是一个提高性能好方法;
        3、ResultSet:负责查询的Statement返回的数据集就是ResultSet。ResultSet使用游标来维护指向当前记录的指针,当你需要更新ResultSet的时候,通常会引起行锁或者全表锁。当然你也可以在返回了结果集之后,调用ResultSet的close方法或者ResultSet对应的Statement的close方法以释放占用的资源,在结果集更新完毕之后再将改动同步至数据库中。
        4、Transaction:Transaction将数据表一系列的改动组织为一个有机的整体,要么执行所有更新操作,要么所有都放弃。Transaction的实现需要对行记录或者表进行锁定,就会阻塞其他更新操作,因此Transaction范围的界定相当重要,一个Transaction包含过多的更新操作必然会影响系统的处理效率。
        以上是数据访问操作中会频繁使用到的资源,那么我们在管理这些资源的时候,应该遵循怎样的原则和模式呢?
        1、及时释放资源:良好的Garbage Collection机制让我们只会一味的申请资源,却忽视了将资源释放这重要一环。除了时常提醒自己使用后放回原处之外,封装一个好用的资源类,让释放资源自动化是一个不错的选择。通常我们通过Resource Decorator模式来实现;
        2、最小化资源打开的间隔:当业务逻辑处理搀杂着数据操作的时候,你心里必须得明白资源应该在什么时候打开。在打开了一个Connection之后仍然需要进行大量的逻辑处理并不是一件好事。对于资源,我们需要的是一种按需索取的原则,当你真的需要的时候,才去获得并且操作相应的资源,而不是过早地据为己有。设置Timeout来防止应用程序占用某一资源的时间过长也是一种很好的方法。对于这一点,常用的模式是Resource Timer模式;
        3、使用资源池:俗话说养兵千日,用在一时,毕竟临阵磨枪是糟糕的。由于资源初始化是一个花销巨大的操作,及早初始化一定的资源,就可以减少书到用时方恨少的尴尬了。想想连接池的应用,你会更清楚地了解这个原则的重要性。与这一点相应的模式就是Resource Pool模式了。

        资源稀缺与否都不应该影响到我们对于资源管理的看重,多从资源的角度去审视自己的程序,你会得到很多你原来不曾发现的顿悟。

         [1]  此Vector非彼Vector
         [2]  参考《Patterns of Enterprise Application》