听棠.NET

用积极乐观的心态,面对压力
posts - 307, comments - 10812, trackbacks - 112, articles - 5
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

系统性能提升之道--内存镜像表

Posted on 2005-03-23 21:51 听棠.NET 阅读(...) 评论(...) 编辑 收藏
 

系统性能提升之道--内存镜像表

 提出问题

对于一个系统,我们在设计开发时,不得不考虑系统的性能问题,硬件的提速可以缓减系统日益增长的消耗,但我们也不能肆无忌惮的扩展系统而不考虑性能的提高,我们应该重视资源的有限性。

为了说明问题,我先举个例子,有两个表如下:

Items物料表

字段名

数据类型

描述

ID

Varchar(50)

主键(PK

Name

Varchar(50)

物料名称

CatalogId

Varchar(50)

物料组ID(FK,关联Catalog)

……

….

…..

 

Catalog物料组表

字段名

数据类型

描述

ID

Varchar(50)

主键(PK

Name

Varchar(50)

物料名称

 

物料表的CatalogId关联物料组表的主键

要求:我们在显示物料信息时,显示此物料的物料组名称

      传统的非ORM模式下,我们一般可以采用

Select Items.*,Catalog.Name as “catalogName” from Items,Catalog where Items.CatalogId=Catalog.ID

 

 

的语句来读取,然后可以在Grid上直接绑定”catalogName”来显示物料组名。这种非ORM模式开发的系统,系统扩展性非常差、数据库移植性差、开发效率低下,所以大家也都开始使用ORM的开发模式。此文章不推荐、不讲述非ORM的开发。

      ORM模式下,我们把这两个表映射为“ItemsEntity物料实体类”与“CatalogEntity物料组实体类”。然后我们在Retrieve到一个物料对象aItem后,在显示时要求显示物料组名的话,就需要CatalogEnityRetrieveg一个物料组对象aCatalog,使用此aCatalogName属性才能显示出来。

      我们也发现了,在ORM模式下,由于采用了OO的编程模式,对开发者来说带来了很多的方便,但在过程中,进行了两次数据库访问,我们试想一下,要是此Items物料表有十个类型的外键关联,那么一个Items的显示,将进行十一次数据访问,对于极其宝贵的数据库访问资料来说,性能大打折扣了。

 

NHibernate中的解决方案:

NHibernate中,可以采用“Relation”的方式来解决,需要在XML中配置十个one-to-many的“Relation”,然后采用非Lazy的方式进行一次性读取,这种方式是把十一次的数据访问放在一起执行了,这看起来可以很方便的实现;但如果有N个人都要进行这样的操作,那么数据访问数还是非常大(N*11)。而且我非常不喜欢NH里的“Relation”,因为这“Relation”还存在一个问题,返回的是对象数组,在没有Grid控件绑定的Java中,这可能是不错的方式,可是在.NET中,我们更会选择Grid进行DataTable的数据绑定,因此这种“Relation”无法很方便的返回DataTable,所以从目前来看,这种“Relation”在.NET中非常不适用的。这也是我的SPL是没有添加“Relation”的原因之一。

 

缓存内存表的概念:

   对于上面的这种情况,我们可以思考一下,采用另一种更好的方式解决。我们可以把这个物料组表的内容放到内存里,作为一个“内存镜像表”,此“内存镜像表”是数据库中实际表的一个镜像,实时保持与实际表的同步,我们在读取时:

if(内存镜像存在)

       从内存直接读取;

else

       {

              从数据库读取;

              建立内存镜像表,把数据库中的数据镜像到内存表中;

}


   这样的方式可以减少很多的数据访问,就拿上面的例子中,在第一个人进行了十一次访问后,以后的N个人执行,只要执行N次数据访问了,速度将会有一个突破性的提高。

 

 

 

内存镜像表的可行性分析:

   对于这种“内存镜像表”的可行性我们先从数据库数据来分析。

   一个系统将包括很多的数据,但我们可以对这些数据进行分一下类:

   “维护性数据”:

        这是指那些在系统中的基本维护性数据,比如前面的物料组、客户类型、付款方式、结算方式、销售类型、货币种类等等,大家都可以举出很多很多这种类似的数据,这些数据存在的共同点是:

       数据量小:这种数据一般在50条记录以内,都是一些类型,分类之类的数据

       字段少:这种数据的字段都是很少的,主要是ID、名称、状态等

       使用频繁高:这种数据的使用率非常频繁,因为在其他表中作为外键,经常会被使用

       维护少:这种数据一般在系统启用前期进行维护,大部分情况下不维护

      因此,具有这些特点的数据,我们就非常有理由放到内存中进行缓存起来。

“操作型数据”:

    这种数据的特点是数据量比较大,字段也比较多,比如物料、客户等信息,不适合用到内存中。

 “日志型数据”:

    这种数据数据量就更多的,比如订单、订单明细、操作记录等,这就更不合适了。

因此我们主要是考虑“维护性数据”,大家想想在你的系统中,会有多少是这种“维护性数据”,对于结构相似的“维护性数据”都会采用“数据字典”的方式来对待。

  

内存镜像表的可操作性分析:

在可行性分析后,我们要考虑,在实际操作起来要注意哪些。

大家也很容易发现,唯一注意点就是“如何保持实时的镜像关系”,也就是要实时保持内存中镜像与实际的数据库表数据一致。

那么我们就要保证在进行更新(包括新增,更新,删除)时能同时更新内存表数据就可以了。

对于非ORM的开发模式来说,使用Sql语句进行数据访问的,要实现这种“实时同步”是很难的,幸好的是我们现在有了ORM的开发模式,在ORM上,我们的数据访问不是直接使用SQL的,而是通过中间的持久层(PersistenceLayer)来实现的,因此,这个持久层(PersistenceLayer)为实现这种同步提供了必不可少的条件。

OR Mapping时,我们标识一个实体为“需保存到内存镜像”,那么持久层(PersistenceLayer)在进行此对象的Save()Update()Delete()时,自动进行“内存同步处理”即可,而且这是完全可实现的。

好了,我们回到前面的例子中,那样的一个物料Retrieve()操作,其实只要两次访问数据库,而且当有N个用户进行类似的操作时,由于“内存镜像表”在第一次就载入到内存了,因此访问数据库次数为N次了。

 

内存镜像表的弊端性:

   从上面的操作性我们就可以看出了,效率的提高是建立在内存的牺牲上的,如果有过多的“维护型数据”使用“内存镜像”,那么,整个内存消耗将非常大,所以,在考虑使用多少“内存镜像表”时,是要考虑服务器的承受能力的。

   但我想,增加内存,提高性能这是很多客户所在追求的。因此,我们只要能仔细分析,是可以得出一种好的“内存镜像表”量的。

 

内存镜像表的实现性:

   这种“内存镜像表”在我的SPL(SmartPersistenceLayer)已经实现了,不知道NH等其他的持久层中是否也有类似的功能。

   SPL中的使用:

ClassMap中定义实体类的IsSaveToMemorytrue即可。例:

   

 <class name="StudentEntity" table="Student" database="MyDB" IsSaveToMemory="true">

    
<attribute name="Id" column="Id" type="Integer" increment="true" key="primary" />

    
<attribute name="No" column="No" type="String" key="primary" />

    
<attribute name="Name" column="Name" type="String" />

    
<attribute name="Birthday" column="Birthday" type="Date" />

    
<attribute name="Grade" column="Grade" type="Integer" />

    
<attribute name="Score" column="Score" type="Decimal" />

</class>


SPL的实现细节:

 

 

SPL中有个静态类,它的ArrayList里存放各种“内存镜像表”的“镜像DataTable”,在进行Retrieve()时,先判断此ArrarList中是否存在此实体的“镜像”,如果有的话,则直接从此“镜像DataTable”中Retrieve()出来,还有RetrieveCriteira(获取标准)时,会从此“镜像DataTable”中使用Select符合条件的,生成新的“镜像DataTable”返回出来。

SPL的实体在进行Save()Delete()UpdateCriteria(更新标准)以及DeleteCriteria(删除标准)时都是会进行“镜像DataTable”的同步更新。

  但这种也不能完全意义上的保证“同步”,因为SPL支持Sql语句执行,所以不要使用Sql来更新“内存镜像”的表。要不然,就无法保证“内存同步”了。

 

 SPL的更多资料,请参考SPL专栏:http://www.cnblogs.com/tintown/category/12787.html 

                                                          听棠

                                                       2005-3-23