听棠.NET

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

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

 提出问题

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

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

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

Feedback

#1楼  回复 引用 查看   

2005-03-23 22:03 by 木野狐      
好文章,受教了!

#2楼  回复 引用 查看   

2005-03-23 22:53 by dudu      
对象数组可以直接绑定到DataGrid控件中。

#3楼  回复 引用   

2005-03-23 22:58 by laserman
good,不过分布式处理将无法保证同步。

#4楼  回复 引用 查看   

2005-03-23 23:00 by dudu      
ASP.NET 1.1中的缓存机制也类似于“内存镜像表”, 只不过在数据库更新时, 缓存中的数据不能及时更新。ASP.NET 2.0已经解决这个问题。

#5楼[楼主]  回复 引用 查看   

2005-03-24 08:26 by 听棠.NET      
关于ASP.NET 2.0对缓存的介绍可以看:
ASP.NET 2.0 中改进的缓存功能
但这也不是我们想要的效果,他的这种机制还是无法保持内存的同步的。而且我想我也不会去使用Sql编程。

#6楼  回复 引用   

2005-03-24 09:13 by 努力學習
大哥寫的太好﹑希望能繼續看到大哥的新作。
也要注意休息啊﹗

#7楼  回复 引用   

2005-03-24 09:17 by none
去年夏天,我搞过一个东西,可以自动的管理缓存,包括读取,更新等等,或许能达到你想要的效果的90%

有兴趣的话,可以MSN我发给你
cnlamar(at)hotmail.com

#8楼  回复 引用   

2005-03-24 09:19 by none
同时,数据不要一次性全部放到内存里,这样并不好,可以考虑一条一条,分别放内存里,需要的时候连续起来使用,再建立一个索引来维持连贯性,把缓存的粒度尽量的细化后,效果更佳

#9楼[楼主]  回复 引用 查看   

2005-03-24 09:23 by 听棠.NET      
一条一条是不现实的,我这个不是缓存,是一个数据表的镜像,因为随时都可能要求返回DataTable,因此在内存镜像中必须有一个完全的镜像,要不然分不清哪些是去数据库,哪些不去,反正不方便,增加压力。

#10楼  回复 引用   

2005-03-24 09:37 by Richer
好!

#11楼  回复 引用   

2005-03-24 10:00 by hawk6224

#12楼  回复 引用   

2005-03-24 10:13 by milula
貌似访问多个异种数据库,因此内存还是数据层通过DAL访问,是吗

#13楼  回复 引用 查看   

2005-03-24 10:55 by 小强      
内存镜像表的使用,跟缓存的使用,有相似之处。
以前我也是使用楼主提的类似内存镜像表的结构,
在服务器内存中维护一个DataSet,将稳定型数据(多读少写)
置于DataSet中,然后用特定策略维护数据一致性。
但这种方式对内存的要求比较高,而且对服务器的稳定性有
不少影响。

之后,就采用了类型微软Application Block架构的缓存处理
(也是内存中维护数据,hashtable结构),
而缓存数据以记录为单元,第一次使用时才置入缓存中,然后用特定策略(完全避免脏数据)维护数据一致性。这种方式减少内存中的数据量。但也有缺点,对于返回DataTable记录集,该种方式有明显缺陷。

这两种方式各有其应用范围吧。

#14楼  回复 引用 查看   

2005-03-24 12:16 by Shrek      
企业应用中经常使用Farm的形式
如何处理Scale out?

#15楼  回复 引用 查看   

2005-03-24 16:04 by Yok      
然后采用非Lazy的方式进行一次性读取,这种方式是把十一次的数据访问放在一起执行了
====================
lazy="true"

而且我非常不喜欢NH里的“Relation”,因为这“Relation”还存在一个问题,返回的是对象数组,在没有Grid控件绑定的Java中,这可能是不错的方式,可是在.NET中,我们更会选择Grid进行DataTable的数据绑定,因此这种“Relation”无法很方便的返回DataTable,所以从目前来看,这种“Relation”在.NET中非常不适用的。
====================
没Relation就不叫object-RELATIONAL mapping了.
Winform的Grid控件能绑定的条件是对象实现了IList, 可以用<bag>; Webform的要求更低, 实现IEnumerable就可以了(说白了, 是集合就能绑定), 可以用<bag>或<set>

#16楼[楼主]  回复 引用 查看   

2005-03-24 17:27 by 听棠.NET      
@ Yok :
哦,原来可以直接绑定啊。不过,竟然我对象的基类的IsPersistent属性也给带出来了。
象要是有十个关联的,要配置这么多关系,而且效率上也没有提高,我觉得这种利小于弊,增加这么多复杂度,就只为了那几个值。
我知道没有Relation就不是完整的ORM了,但我想理论归理论,有很多理论没有实际价值,那是美好的憧憬而已,ORM中还有些理论都没有实现的,就像共产主义就是理论,那是永远也到不了的理想社会,没有实际意义。我还是喜欢实践才是检验真理的唯一标准。

#17楼  回复 引用 查看   

2005-03-24 17:40 by progame      
就算是typed dataset,也支持relation的,我无法理解为什么relationship会成为orm的一个解决难题,内存镜像表还是一个缓存的实现,这在xpo、nhibernate中都已经实现,然而这一点恰恰是我不欣赏的地方,为什么?因为cache这一层完全可以在整个系统构架中独立于orm,我会在系统中来处理entity object的缓存和同步问题,而不是交给orm这一层来完成。

#18楼  回复 引用 查看   

2005-03-24 17:45 by progame      
在我的persistore中,entitylist对数据库是在需要时一次读取,延迟加载所有entity object的,也就是说如果我不访问这个entitylist,那么永远都不会去查询数据,如果访问了,只需要查询一次,而具体的对象是在需要时才创建的,而且支持可更新的数据绑定,可是我觉得无论是性能、方便性还是其它,我宁可选择dataset,用entitylist的唯一好处就是我内部是数据库平台无关的sql语句,但我现在已经倾向于通过xml定义data view,从而完成绑定、更新等操作,这样的data view做为一个object,其功能、灵活性、性能都远好于entitylist。

#19楼  回复 引用 查看   

2005-03-24 18:43 by Yok      
估计你没有认真使用过任何一个真正的orm框架
orm的精髓不在于自动处理curd, 而是能用面向对象的方式去操作数据.
Relation并不单指能customer.Orders这样通过关联访问"几个值", 而是在查询中能使用面向对象的关联, 例如
"From Order order = where order.Customer.Name = 'Tom'" 查询属于Tom的订单
"From Customer customer where count(customer.Orders) > 100" 查订单数量大于100的客户
另外多态性也是很常用, 假如在权限系统中, 用户User和用户组Group都可以作为一个被授权的实体, 可以用IActor接口来作为User和Group的抽象. "From IActor actor, Resorces res where res.Name = 'xx' and actor in elements(res.GrantedActors)"可以查出拥有名为"xx"的资源的访问权限的User或Group
要节省写sql的时间的话, 倒不如用DAAB或者用ide生成data adapter + typed dataset

#20楼  回复 引用   

2005-03-25 10:01 by none
Yok所说的问题我也曾经考虑过,所以到最后没有把这个实现为一个o/r mapping因为我觉得如果要实现一个标准意义,且能适应很多应用,在理论和现实都达到某种可用度的O/R MAPPING不是一件简单的事情

to 楼主:
一条一条存的道理很简单,你如果数据量小,你需要用这些吗?几乎是没任何必要,一般的机器就足够去支撑,做缓存没有任何意义,在数据量大的时候,缓存整个数据,仍旧没有任何意义(如果非要这样,建议购买使用数据中心版的操作系统与数据库,配以巨大的内存,整个SQL SERVER都可以镜象到内存之中),所以,我们需要一个东西,能够根据我们的需要,写一些类似于普通SQL的东西,自动去检查内存中是否有缓存,缓存了的直接取出,没缓存的从数据库中读出来,并缓存,再将这些数据如你所愿,结合在一起,成为一个DataTable,最终输出,在你更新或者插入数据的时候,就象操作DataTable那样,透明的操作,然后将数据同步给缓存与数据库,一切操作都透明化,对使用者来说,应该意识不到缓存做了什么工作,只知道有个开关,开启后,缓存对性能的提升也就来了~~~

我想你区别缓存与镜象的区别,大约是缓存对数据的保存不完整,并且无法自动同步,如果是这样,那我想我所说的缓存和你所说的镜象没有实际的区别。

#21楼  回复 引用   

2005-03-25 10:08 by none
没有Relation的OR/MAPPING在一些稍大的项目中会让你用起来异常苦恼,你会恨不得把他丢掉,继续写SQL或者存储过程之类的原始办法。

#22楼[楼主]  回复 引用 查看   

2005-03-25 10:51 by 听棠.NET      
@none:
 ===你如果数据量小,你需要用这些吗?几乎是没任何必要,一般的机器就足够去支撑,做缓存没有任何意义,在数据量大的时候,缓存整个数据,仍旧没有任何意义==
 这个我非常不同意,我针对的就是数据量小,数据量虽小,但你知道影响系统性能的瓶颈是什么吗?就是宝贵的数据访问资源啊!!!就是因为数据量小,访问频率高,才真正有必要放到内存啊。数据量大我也不推荐这样实现的。
 要提高性能就应该在数据访问上找原因,数据库的缓存也是在数据库服务器上进行缓存,而且没有减少访问,没有减少应用服务器与数据库服务器之间的数据传输,而内存镜像呢,是在应用服务器上,根本不用去访问数据库服务器。而且优点就在于数据量小,但访问频繁啊,镜像表的话,只要第一个载入内存,以后N个人都不再去数据库服务器读取了,这性能难道你看不出来吗?
 至于程序要看到效果的话,我觉得很简单,在SQL里监测访问好了,我测试过的,在内存中的数据访问在SQL监测中不会出现了。可以证明数据是从内存读取的。
  至于ORM的Relation我也不想多谈了,我不反对Relation,只是从目前来说,利小于弊而已,你说的什么异常苦恼,恨不得丢掉,只是你一厢情愿说的吧。难道ORM没有了Relation就没有处理方式了,那也太弱了吧,我只是觉得Relation的东西,我用SPL的其他方式处理更简单,虽然在理论上好象“没有Relation”就不是完整的ORM。我想实用就可以了。

#23楼  回复 引用   

2005-03-25 11:48 by none
不用那么激动,将一个话题演变为争吵可不是什么明智的选择。

小数据表的访问,你也许太过于担忧了,仔细想想,真的有那么多吗?你的CPU负载超过80%了吗?当然前提是你花50%以上的软件成本来购买硬件,性能是相对的,这是个永恒的话题,没必要说得更多~~

而且不用强调不用访问数据库服务器这一点,关于这种东西,我去年已经实现过,也在应用,并且在不段的改进,我相信比我做得更早更好的人太多了,顺便再说一句,如果缓存的管理不能自动化,那将会让你非常的疲劳,如果不做一个适合的数据库连接管理器,也将无法最大发挥性能,因为如果数据都有缓存了,那么我还连接数据库服务器做什么呢?

说一个我个人认为比较理想的方法:
1、数据库连接从一个请求到结束,保持住,不用担心这个时间过长,如果你的一个页面消耗太多时间的话,那么无论如何都会占用一个连接,如果仅仅是程序的占用,那可以提前释放连接,看需要而进行,这样可以更好的在多层构架中实现事务,并且可以降低数据库消耗。
2、读取需要数据的时候,如果这些数据全部有缓存,那么不要连接数据库,如果全部或者部分没有缓存,那么把读取没有缓存的,并结合在一起,做最终的数据供应。
3、如果数据量大的时候把数据单条缓存,按需缓存,需要多少缓存多少,没人使用的数据不要缓存,节约资源,如果数据量大,那么建立全表的缓存,如果数据甚至不需要持久化,那么建立所谓的内存表,比如Online表,当然如果你觉得Online表不是那么重要的话
4、数据的插入、更新等操作都应该可以自动的同步缓存与数据库。
5、以上所描述,都必须可以自动完成,不需要手工做更多的干涉,这一点很重要。

再说Relation吧,首先我是一个无名小卒,很弱也是应当,但我想如果同一件事,大家想出来一个办法,为的是让他更简单更方便,却起了反效果,那么这个办法真的不是那么的理想,既然要用or那目的很显然是为了让数据库开发简单方便,如果因为用了OR反而要我每次做联表子表等操作的时候比以前更烦琐,那么对于一个商业程序来说,真的没有任何意义,因为一个商业程序中会充斥着各种Relation,不是吗?不要为了OR而OR

就实用主义而言,我谈谈我是怎么做的,为了省心,我没有自己做代码生成器,没有自己做对象基件,仅仅是用DataTable和DataRow来映射数据,因为如果大型项目,我们会用存储过程,小型项目,DataTable足也,更何况他内部实现了包括find在类的太多东西,我没必要自己再做一套,映射的时候,我也没有自己搞出一套什么对象查询语言,也就是把各个查询语句分离开作为条件,使用的时候ADD进去,然后组合一下,很简单的操作,对于数据自动更新,我有同样能够实现,这就足够了,更复杂的应用呢,我会用存储过程。

#24楼  回复 引用   

2005-03-25 11:50 by 飞飞
或许我是天生长有反骨;虽然看了N篇有关ORM模式的文章,似乎它已经在数据库系统方面欲演欲烈;
但我还是想问ORM比传统模式好在什么地方,权衡代价,它真的值嘛,我看到了一个又一个的“面向对象”的拥肿的系统;除了“面向对象”外,几乎没有多少好处;
我们为什么面向对象????

“传统的非ORM模式下,我们一般可以采用
Select Items.*,Catalog.Name as “catalogName” from Items,Catalog where Items.CatalogId=Catalog.ID

语句来读取,然后可以在Grid上直接绑定”catalogName”来显示物料组名。这种非ORM模式开发的系统,系统扩展性非常差、、开发效率低下”
系统扩展性非常差???具体一点。ORM的扩展时就不改了吗?加个字段改N处;
数据库移植性差??100个项目有几个曾经让你换个数据库,传统方式真的无法换数据库吗?

开发效率低下??我在这个模式下才体会到;
我觉得ORM真的要取代传统还有长长的路要走;
我的看法或许是愚蠢的,真是这样的话请大家让列清醒;




#25楼  回复 引用   

2005-03-25 11:51 by none
3、如果数据量大的时候把数据单条缓存,按需缓存,需要多少缓存多少,没人使用的数据不要缓存,节约资源,如果数据量大,那么建立全表的缓存,如果数据甚至不需要持久化,那么建立所谓的内存表,比如Online表,当然如果你觉得Online表不是那么重要的话

写错了,应该是:

3、如果数据量大的时候把数据单条缓存,按需缓存,需要多少缓存多少,没人使用的数据不要缓存,节约资源(不敢想象把硬盘的东西都反到内存中的结果),如果数据量小,那么建立全表的缓存,如果数据甚至不需要持久化,那么建立所谓的内存表,比如Online表,当然如果你觉得Online表不是那么重要的话

#26楼  回复 引用   

2005-03-25 12:02 by none
其实做缓存这东西,早在一年以前,lostinet大哥就曾经提出来了,也这么做了,但由于他太忙,没有把他的自动化继续做下去而已。

有兴趣你可以到www.lostinet.com看看,他的论坛和他提供下载的东西,仔细看看吧。

#27楼[楼主]  回复 引用 查看   

2005-03-25 12:10 by 听棠.NET      
@none :
你说的缓存机制我不知道是具体什么样的,最好能把你的缓存机制原理讲一下,或许我的机制确实有问题。你的缓存也是在应用服务器上的?那整个机制跟我的应该是差不多的,只是缓存什么内容不同而已,你说的缓存管理机制是不错的,但我想每次都要经常的判断是否值得,还有你说的以上都是自动的,我现在说的内存镜像也是自动的啊。所以我搞不清,你现在跟我意义反歧就在于缓存什么多少东西吗?如果是,你推荐一下原理,把实现机制也讲一下,因为有些道理归道理,实现的代价还是很大的。
至于Relation,我觉得是好的思想,我以后也可能会采用的,只是目前觉得它的存在增加的复杂性胜过了它的功能优点而已。这可能是我个人的想法。

#28楼[楼主]  回复 引用 查看   

2005-03-25 12:18 by 听棠.NET      
@飞飞 :
至于ORM的话题,要讲的话,你可以开个专题,我在这里就简单回答一下你的问题。
使用sql的话,增加字段修改的地方会比较多,而且语句组装会带来非常不便,这Sql是个开发瓶颈吧,看看有多少程序员花多少时间在调Sql语句吧。ORM中的修改只要修改映射,然后赋个值就可以了。不是ORM不修改,而是清晰简单。
项目移植的可能性不会很大,可是当你要把项目标准化,升级为产品,那么你就得考虑多数据库支持,或者一个项目要卖给第二家客户,人家就是要求换数据库,数据库移植已经不再是传说。那么Sql语句带来的不兼容性会让你疯掉,ORM模式下数据库就可以无关,移植几乎不要修改任何代码。

#29楼[楼主]  回复 引用 查看   

2005-03-25 12:21 by 听棠.NET      
@none :
我根本不知道那位大哥,我也根本不知道他的东西,他早就提出来,和我现在提出来有什么关系吗?
再说了,他都没有实现,就算实现了,跟我也没有关系啊。我提出我的,我自己也实现了。
我又不是抄袭。

#30楼  回复 引用   

2005-03-25 19:41 by none
首先呢,大家平静一些,我不是来吵架或者挑衅的:D

现在这样就不错,其实缓存的内容不是重要,关键在于自动化处理会麻烦一些,你说得没错,大家都是缓存在.net组件所运行的进程内的,而我说我的那一套,也没什么先进性可言,区别可能在于我更注重对大数据量的处理而已,关于关系呢,其实真的很好,用过之后才知好啊,呵呵,先接受他吧,接受之后再来给他一个合适的评价也许更合适。

关于LOSTINET呢,个人觉得他算一个技术比较不错的人,你可以到博客堂看看他的专栏,我记得博客园这边他也应该有个,但这边人太多,也不知道是否有按人名搜索的。。。

#31楼  回复 引用   

2005-03-25 19:44 by none
关于每次判断是否值得,这是一个比较头疼的问题,如果不判断,你怎么能确保缓存没有失效?判断一个对象是否为NULL似乎是最直接也最省效的方法了吧?或者你有什么更好的办法?如果有且愿意分享,不妨说一下?

#32楼  回复 引用 查看   

2005-03-25 20:37 by dudu      
@none
通过Google站内搜索可以搜索到, http://www.cnblogs.com/Lostinet/

#33楼  回复 引用   

2005-03-26 02:10 by none
谢谢dudu,不过LOSTINET好象没写太多东西,呵。

#34楼[楼主]  回复 引用 查看   

2005-03-27 20:57 by 听棠.NET      
@none :
说真的,你说的那种把一条条记录缓存起来的方式,我个人感觉没有我的方式好,我的方式强调的前提你注意到没有:是数据量小,使用频率很高的数据,我强调的是数据分类对待,因此,对于这些数据,我个人认为这种方式不好,不管是从内存还是效率上来说。
而你说的缓存,可能适合于所有的数据,你的记录缓存方式,应该是没有考虑数据分类对待。所以在判断方式等势必会带来不方便,而且感觉有些事倍功半。
我感觉把数据分类对待,这对每个设计者来是应该考虑的一个问题,从这些数据中我们可以发现一些共同点,并可以思考一种方式来对待,我的内存镜存表也就是这种方式。

#35楼  回复 引用   

2005-03-27 21:45 by 寒枫天伤
内存同步方面我的思考....
http://www.cnblogs.com/william_fire/articles/126665.html

缓存同步的问题,我认为还是需要缓存自己来维护的,不过,目前的数据库多得让人想杀人,实现的特性也各自不一,有些性能比较高的方案,只适合于一些功能上考虑得比较完善的关系型数据库。

#36楼  回复 引用 查看   

2005-03-28 00:33 by Addie      
以前看过听棠另一篇有关分页的讨论,结合这篇文章,感觉听棠是个实用主义者,开发思想上有自己的原则,非常强调在特定环境下讨论某种方案的优劣性,这一点我非常赞同。

当然,cnblog里有太多大师级的人物,不论哪种方案,大师们都能一眼看出在其他环境中,你的方案存在着这样的不足、那样的缺陷。

其实没必要这么较真,世界上没有万能的工具和解决方案,生活中大家都知道杀鸡没必要用牛刀,牛刀杀鸡也不一定好使,可为什么在讨论软件时就忘了这一点呢?

#37楼[楼主]  回复 引用 查看   

2005-03-28 09:12 by 听棠.NET      
@Addie :
你说的蛮对的,其实我在思考理论的同时,会考虑其可行性与操作性,当然有些东西在不同的环境里是不适合的,但我想,我的理论还是有其存在价值的,可能是因为大家没有对文中提到的“维护型数据”注意吧,我的思想也只是为了解决这部分数据,对于其他的部分,我想大家谈的缓存方式可能更佳,就像NH可提的一样。
其实说真的,不管是什么理论,最好是一些可行的,我最烦的就是说了一堆的理论,但没有人在实现,有些理论是非常好的,但在实现过程中会发现事倍功半。

纯属个人意见!

#38楼  回复 引用   

2005-03-28 09:39 by none
呵,也许我不是很了解你所说的差异化,我目前的缓存策略提供了三个选项:
1、IndexCache
2、EntityCache
3、FullEntityCache

PS:我不会在大量数据的时候还把所有数据都缓存的,那是一种很愚蠢的行为,我曾经在我的1800+/512ddr266的机器上测试过2000万数据的分页读取(不做任何过滤,完全读取),占用系统内存大约160M,读取其中任何一页数据大约15ms-60ms之间,当然这是没有做一些优化措施的情况下的数据,仅仅是简单的用了IndexCache与EntityCache,或许你会认为这没有任何实际的意义,呵呵,我也这么认为,这就是一个测试而已,我没有想过我做任何事情的时候会需要去看2000W的数据,呵呵。。。会把我看死的。。。缓存的同步也解决了,可以实时同步,写的性能大约下降了70%左右,但我认为值得,毕竟写的频率会远低于读。

一些小表数据,如系统配置等,没有超过1000条数据的,我会使用FullEntityCache,看起来似乎有些类似于你所说的镜象表。。。

#39楼  回复 引用   

2005-03-28 10:27 by 飞飞
听棠.NET
感谢你的指点,我好象有点懂了;
--使用sql的话,增加字段修改的地方会比较多,而且语句组装会带来非常不便,这Sql是个开发瓶颈吧,看看有多少程序员花多少时间在调Sql语句吧!
哦!用ORM的程序员不用花时间调SQL,他们可以不用SQL,建立一个SQL映射关系好象不会那么复杂吧!!
--ORM中的修改只要修改映射,然后赋个值就可以了。不是ORM不修改,而是清晰简单!
清晰简单的代价是N个类,N个单元!

--可是当你要把项目标准化,升级为产品,那么你就得考虑多数据库支持,或者一个项目要卖给第二家客户,人家就是要求换数据库,数据库移植已经不再是传说
--那么Sql语句带来的不兼容性会让你疯掉,!!
似乎只有ORM才可以移数据库,而且我们也不要用什么数据库厂商护展标准SQL的部分,不晓得他们搞些扩展来干什么,有标准的不就行了嘛!!

--ORM模式下数据库就可以无关,移植几乎不要修改任何代码。
ORM的数据层是万能适配层,换数据库不用写第二个!!!

#40楼  回复 引用   

2005-03-28 14:11 by hihin
基于表和基于行的编成方式在.net中,因为DataTable和DataRow的存在的确是最容易不过的实现方式。OR这种东东,如果不是系统太大,逻辑太复杂,还是不要用了的比较好。一不小心,就会同样的数据创建两份对象,要避免这个问题,还得去写一个identitymap出来。

#41楼  回复 引用 查看   

2005-03-28 17:39 by kknd      
@none
有没有blog?

#42楼  回复 引用   

2005-03-29 09:12 by none
kknd,我是一个懒散的人,没有写BLOG的习惯。。。BLOG有不少,到处乱申请,也包括这里,但都没有什么内容。。。如果有什么要交流的,跟这个帖子好了,我加了收藏了:D

#43楼  回复 引用 查看   

2005-03-29 15:18 by kknd      
@none
向你请教一个cache同步的问题。
在使用了负载均衡的系统中,怎样同步各个服务器的cache呢?
我自己想的是不是使用msmq,某个服务器处理了更新后,发送一条消息到队列,其他服务器接收到这条消息后,相应修改cache。这样好吗?

#44楼  回复 引用   

2005-03-29 16:59 by none
请教之词愧不敢当,关于负载均衡中使用,说句让您见笑的话,我只用过冗余和自建的均衡控制,至于系统级的负载均衡真是没怎么接触过,但我想,如果负载均衡的调度中心如果可以实现进程数据的维护,就没什么问题,如果实现不了,问题就麻烦了,可以考虑采用你说的办法,又或者建立一个独立的CACHE服务器,又或者建立一个CACHE调度服务器,所以对CACHE的更新都将以这里为进出口,剩下的就好解决了,但无论如何,这都不是一个好解决的问题,刚才另外一个朋友(newlee)提到无状态的负载均衡中使用,那看起来似乎更麻烦些,我所理解的无状态负载均衡,进程数据是绝对不可维护的,那就真的只能自己DIY了。。。

#45楼  回复 引用   

2005-03-30 11:14 by floe
首先向听棠的辛勤工作致敬!
然后谈谈我的个人看法:) 现在是OS时代,做这种中间件产品,大家都不可避免的会以整个软件界的标准来评价,而要做一个能实际有助于各种应用开发的ORMap工具,要考虑得完善的确非常难。
可以看出听棠是个热血青年,而且善于动脑勤于动手,但我觉得过于自信了,喜欢说这是我的看法,我不管别人怎么实现。我们能看到的产品,都是被选择出来的,作者都不简单。
你整体上的思路还不够成熟,这从不了解relation的重要性和本文的主题内存表这两点都可以看出来。建议多研究一些其它的ORMap工具,包括你提到的NH的发源H。
没有relation的支持,许多用简单的对象导航可以实现的操作就要改为手工实现,更重要的yok已经说了:无法实现oql。
而内存表,我觉得不值得专门花时间,重点应该放在缓存上。这里的关键是是否透明。大家都不实现内存表,不是因为它很难实现,而是因为它的实际意义很小,而没必要的功能多了绝对不是好事。有了完善的缓存机制后,如果需要内存表,应用程序只要用一小段代码将表读入就可以达到内存表的目的了。
你总结的“维护性数据、操作型数据、日志型数据”我们基本也这样分类,不过称呼是“只读类、更新类、追加类”这样比较省脑子一点,一般我们会设计一个表保存大部分代码类数据,这样实际上只读类数据表很少。
至于大家讨论的内存表的占用内存问题,我觉得不存在吧?只读数据一般都不会太多,代码性质的一个系统中最多几兆吧,如果是其它只读性质的数据就不要放进内存表啦。

#46楼[楼主]  回复 引用 查看   

2005-03-30 13:30 by 听棠.NET      
@floe :
你好,我想你也考虑过“只读类”数据的性能吧,所以你说的“没有必要”感觉和你说的自相矛盾啊。这些"只读类"的内存缓存有没有必要,这应该是每个人有每个人的看法,不同的项目有不同的要求,所以你说的“没有必要”那可能是你的看法。
而这个只读类的实现,大家可以有自己方式,我也只是说了一下我是如何来实现的。方法应该有很多种。

#47楼  回复 引用   

2005-03-30 13:48 by floe
说没有必要的原因是有完善的缓存机制就不需要内存表了,而完善的缓存机制又是一个工业级的ORM必须实现的

#48楼  回复 引用   

2005-03-30 14:24 by none
在我的理解中,内存表应当是不需要持续化的,譬如Online数据,只要是数据库中有的,在内存中所做的映射就该叫缓存,这是我的个人观点,关于orm中的relation我们就不过多讨论了,每个人的实际需求可能不一样,一个十分简单的应用将不会需要用的字母表之类的东西,至于复杂后,为了ORM而放弃了方便的relation改用自己手工做,我觉得不是很明智,也不用因此觉得自己这么做很强,其实做程序的目的就一个——简化工作而已。。。

floe:赞成你所说的工业级的orm需要支持缓存,但我仍旧认为,缓存在某些时候的实现,还是有很大的区别的,简单的把数据在内存中做个映射,并且在知道数据变化后重新载入,这种基本的功能,还不足以胜任,将数据整个在内存中镜象一个就更是一件十分简单的事,不足以纳入工业标准来说了,工业级的orm应该有更强大更先进的缓存技术。

#49楼[楼主]  回复 引用 查看   

2005-03-30 14:39 by 听棠.NET      
工业级的ORM中缓存是必需的,同意。
个人感觉内存镜像与缓存还是存在很多不同的,缓存的调度机制与内存镜像的机制是不同的,用内存镜像可以更好的解决这些"只读类"的数据,当然可能你觉得没有必要。
其实一个好的缓存机制也不是很简单的事。不知道哪儿有好的缓存机制介绍,我也想学习与研究一下。

#50楼  回复 引用   

2005-03-30 15:27 by kknd
@none
使用负载均衡的目的就是分散web服务器的压力,提高系统的负载能力,如果又单独使用一个cache服务器,会不会把压力又集中到了cache服务器身上?
个人觉得,cache还是应该由业务逻辑来决定,放在dal可能并不适合。

#51楼  回复 引用   

2005-03-31 09:26 by none
kknd:所以这个问题没那么好处理,要么原有的负载均衡设备支持CACHE,要么自己做CACHE的负载均衡,当然,做得简单一些,搞个调度设备来负责通知机群更新CACHE

业务逻辑也可以做,但那肯定会大大的加大代码编写的工作量,也可以采取合理的办法,项目不大的时候,在DAL做,很合理,项目大到需要N台以上的机器做负载均衡了,那也有足够的人力物力在逻辑层实现CACHE,或者自己实现CAHCE同步调度了。

#52楼  回复 引用   

2005-03-31 09:28 by none
听棠.NET:一个非常好的缓存机制,我也十分渴望了解,但就目前而言,我的方法和你的大同小异,无非我还能索引缓存与实体缓存分离的方式来支持大数据缓存而已,没有本质的区别。

至于这个东西到底是缓存还是镜象表我觉得就没有争论的意义了:D

#53楼  回复 引用   

2005-03-31 23:01 by 陆刚[未注册用户]
作者的原话:
这种非ORM模式开发的系统,系统扩展性非常差、数据库移植性差、开发效率低下

系统扩展性非常差:我勉强可以接受.
数据库移植性差:也还说的过去
开发效率低下:应该反过来说吧!
关键是运行效率,在企业开发中,多表的关联 7,8个是很多的.
ORM的速度根本无法接收. 至于你说的缓存,一次能缓存100万条记录合适吗!即使在中间层.

ORM我感觉很难在我的系统中实现,请多指教!

#54楼[楼主]  回复 引用 查看   

2005-04-01 09:04 by 听棠.NET      
@陆刚 :
ORM的开发效率高于非ORM,你如果对此有不同意见,可以开个主题,问问BLOG上的朋友。
至于我说的缓存,你为何不看清我说的前提呢。“维护型数据”,你看了我说的“维护型数据”的特点了吗?
数据量少!!!

我只是希望你在看清并能理解我的东西后再发表你的看法。

#55楼  回复 引用 查看   

2005-04-04 15:19 by Samuel      
这篇投稿已发表,请访问 http://www.alphatom.com/content/view/154/69/ 察看。
ps:好激烈的讨论

#56楼  回复 引用   

2005-04-11 14:51 by 笨笨
1
//USE relationship config:

Person.getAddress().getStreetName();

2
//dont use relation:

Long addressId = Person.getAddress();
Address address = AddressXXX.loadById(id);
address.getStreetName()


which one is better?

#57楼  回复 引用   

2005-04-21 10:48 by jez

哦,上次问你怎么存放常用数据到内存中,原来你说的就是这个IsSaveToMemory="true",我一直不知道,都是直接访问数据库的.
还好数据量小,再让用户加了内存,呵呵,感觉也蛮不错的.

对了,在使用中只要在xml里加上IsSaveToMemory="true这个就可以了吗?还是一定要用Retrieve()之类的东东?我在程序里用了很多的比如:BankEntity Bank = BankEntityAction.RetrieveABankEntity(e.Row.Cells.FromKey("Branch").Value.ToString().Trim());的代码,或者是根据其他条件查询rc的,是不是直接可以用的?

#58楼[楼主]  回复 引用 查看   

2005-04-21 12:14 by 听棠.NET      
@ jez :
只要在XML中设置一下就可以了,其他的所有的工作都是SPL给你完成的。你的代码不需要修改的。

#59楼  回复 引用   

2005-04-22 17:09 by jez
我觉的有这么一种情况:
有可能我对某些表的内存的使用只是在一段时间内比较频繁(比如说是页面打开的时候),但是用后我认为这些内存数据在另外的地方不常用了,为了减小内存占用量,要把它释放掉,这个怎么办?
最好是在实体类里面有一个开关做另外的控制

#60楼  回复 引用   

2006-05-05 13:26 by Mike Chan[未注册用户]
我发现一个BUG, 当已经实例化了一个实体,就无法以非主键字段为条件retire实体集合,情况发生在IsSaveToMemory为true的情况

#61楼[楼主]  回复 引用 查看   

2006-05-08 09:25 by 听棠.NET      
@Mike Chan :
不管是不是“保存到内存”了,Retrieve都是针对“主键”进行查询的,所以,如果是非主键的话,请采用RetrieveCriteria,因为Retrive出来的是唯一实体,这只有在主键下才可能,如果是非主键,则“可能会有”多条记录。

#62楼  回复 引用   

2007-02-03 17:37 by 单凯[未注册用户]
听棠兄,出错了。我在SPL中用isSavetoMemory=true,就出错了。


RetrieveCriteria rc = new RetrieveCriteria(typeof(SiteColumn));


Condition condition=rc.GetNewCondition();
condition.AddEqualTo(SiteColumn.__PARENTCOLUMNID, DefaultValue.DefaultTopParentColumnId);//如果把这两个句注释,又不出错了。

return rc.AsEntityContainer();这句出错。

#63楼  回复 引用   

2007-02-03 17:44 by 单凯[未注册用户]
我说的问题其实同2006-05-05 13:26 by Mike Chan ,好像真的有BUG

#64楼  回复 引用   

2007-07-05 14:24 by guest[未注册用户]
有个问题怎么解决:如果多个人都把同一个表的内容保存到内存中了,其中人一个人执行了删除操作,这样也删除了数据库,这样怎么才能维护数据库和其他人所维护的内存的同步,不然其他人如果恰好用了这些数据 那么就是用了不存在(删除)或者错误(更新)的数据了

#65楼  回复 引用   

2007-07-05 17:06 by guest[未注册用户]
听棠兄,出错了。我在SPL中用isSavetoMemory=true,就出错了。


RetrieveCriteria rc = new RetrieveCriteria(typeof(SiteColumn));


Condition condition=rc.GetNewCondition();
condition.AddEqualTo(SiteColumn.__PARENTCOLUMNID, DefaultValue.DefaultTopParentColumnId);//如果把这两个句注释,又不出错了。

return rc.AsEntityContainer();这句出错。

这里出错的原因好像是先是在内存中保存了一张表,然后用table.select形式从表中过虑,从持久层中的sql来看,好像是过虑的时候用table.select('[tablename.filed]')的形式,这样内层中的字段就不能识别这样的tablename.filed这样的字段名了

#66楼  回复 引用   

2007-08-18 22:40 by 广紫[未注册用户]
看了  各位 大虾的评论有写晕晕的 自己什么时候才能像各位大虾那样有水准 佩服佩服  !!!!!!!
出售蓝奇高级验证码识别引擎,可准确识别新浪动网淘宝CSDN等多种复杂验证码。

输出为一个标准DLL,可供VB,VC,Delphi,C#.NET,VB.NET,模拟精灵,按键精灵等多平台调用,调用方法简单,几行代码即可完成。独具特色的边缘检测字符分离、旋转倾斜纠正和通用字符匹配算法(无论字体和大小), 使得该引擎对于像新浪、动网、淘宝、CSDN等多种验证码均有不错的识别率,是一款效果较为理想的验证码识别引擎。附详细的调用实例和代码注释等相关技术文档。

官方网站 - http://***/yzm_advocr
识别效果怎么样一试就知道 - DEMO下载 http://***/yzm_advocr/advocr.rar