Posted on 2005-03-23 21:51
听棠.NET 阅读(10495)
评论(69) 编辑 收藏
系统性能提升之道--内存镜像表
提出问题
对于一个系统,我们在设计开发时,不得不考虑系统的性能问题,硬件的提速可以缓减系统日益增长的消耗,但我们也不能肆无忌惮的扩展系统而不考虑性能的提高,我们应该重视资源的有限性。
为了说明问题,我先举个例子,有两个表如下:
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后,在显示时要求显示物料组名的话,就需要CatalogEnity去Retrieveg一个物料组对象aCatalog,使用此aCatalog的Name属性才能显示出来。
我们也发现了,在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中定义实体类的IsSaveToMemory为true即可。例:
<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
ASP.NET 1.1中的缓存机制也类似于“内存镜像表”, 只不过在数据库更新时, 缓存中的数据不能及时更新。ASP.NET 2.0已经解决这个问题。
但这也不是我们想要的效果,他的这种机制还是无法保持内存的同步的。而且我想我也不会去使用Sql编程。
大哥寫的太好﹑希望能繼續看到大哥的新作。
也要注意休息啊﹗
去年夏天,我搞过一个东西,可以自动的管理缓存,包括读取,更新等等,或许能达到你想要的效果的90%
有兴趣的话,可以MSN我发给你
cnlamar(at)hotmail.com
同时,数据不要一次性全部放到内存里,这样并不好,可以考虑一条一条,分别放内存里,需要的时候连续起来使用,再建立一个索引来维持连贯性,把缓存的粒度尽量的细化后,效果更佳
一条一条是不现实的,我这个不是缓存,是一个数据表的镜像,因为随时都可能要求返回DataTable,因此在内存镜像中必须有一个完全的镜像,要不然分不清哪些是去数据库,哪些不去,反正不方便,增加压力。
貌似访问多个异种数据库,因此内存还是数据层通过DAL访问,是吗
内存镜像表的使用,跟缓存的使用,有相似之处。
以前我也是使用楼主提的类似内存镜像表的结构,
在服务器内存中维护一个DataSet,将稳定型数据(多读少写)
置于DataSet中,然后用特定策略维护数据一致性。
但这种方式对内存的要求比较高,而且对服务器的稳定性有
不少影响。
之后,就采用了类型微软Application Block架构的缓存处理
(也是内存中维护数据,hashtable结构),
而缓存数据以记录为单元,第一次使用时才置入缓存中,然后用特定策略(完全避免脏数据)维护数据一致性。这种方式减少内存中的数据量。但也有缺点,对于返回DataTable记录集,该种方式有明显缺陷。
这两种方式各有其应用范围吧。
企业应用中经常使用Farm的形式
如何处理Scale out?
然后采用非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>
@ Yok :
哦,原来可以直接绑定啊。不过,竟然我对象的基类的IsPersistent属性也给带出来了。
象要是有十个关联的,要配置这么多关系,而且效率上也没有提高,我觉得这种利小于弊,增加这么多复杂度,就只为了那几个值。
我知道没有Relation就不是完整的ORM了,但我想理论归理论,有很多理论没有实际价值,那是美好的憧憬而已,ORM中还有些理论都没有实现的,就像共产主义就是理论,那是永远也到不了的理想社会,没有实际意义。我还是喜欢实践才是检验真理的唯一标准。
就算是typed dataset,也支持relation的,我无法理解为什么relationship会成为orm的一个解决难题,内存镜像表还是一个缓存的实现,这在xpo、nhibernate中都已经实现,然而这一点恰恰是我不欣赏的地方,为什么?因为cache这一层完全可以在整个系统构架中独立于orm,我会在系统中来处理entity object的缓存和同步问题,而不是交给orm这一层来完成。
在我的persistore中,entitylist对数据库是在需要时一次读取,延迟加载所有entity object的,也就是说如果我不访问这个entitylist,那么永远都不会去查询数据,如果访问了,只需要查询一次,而具体的对象是在需要时才创建的,而且支持可更新的数据绑定,可是我觉得无论是性能、方便性还是其它,我宁可选择dataset,用entitylist的唯一好处就是我内部是数据库平台无关的sql语句,但我现在已经倾向于通过xml定义data view,从而完成绑定、更新等操作,这样的data view做为一个object,其功能、灵活性、性能都远好于entitylist。
估计你没有认真使用过任何一个真正的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
Yok所说的问题我也曾经考虑过,所以到最后没有把这个实现为一个o/r mapping因为我觉得如果要实现一个标准意义,且能适应很多应用,在理论和现实都达到某种可用度的O/R MAPPING不是一件简单的事情
to 楼主:
一条一条存的道理很简单,你如果数据量小,你需要用这些吗?几乎是没任何必要,一般的机器就足够去支撑,做缓存没有任何意义,在数据量大的时候,缓存整个数据,仍旧没有任何意义(如果非要这样,建议购买使用数据中心版的操作系统与数据库,配以巨大的内存,整个SQL SERVER都可以镜象到内存之中),所以,我们需要一个东西,能够根据我们的需要,写一些类似于普通SQL的东西,自动去检查内存中是否有缓存,缓存了的直接取出,没缓存的从数据库中读出来,并缓存,再将这些数据如你所愿,结合在一起,成为一个DataTable,最终输出,在你更新或者插入数据的时候,就象操作DataTable那样,透明的操作,然后将数据同步给缓存与数据库,一切操作都透明化,对使用者来说,应该意识不到缓存做了什么工作,只知道有个开关,开启后,缓存对性能的提升也就来了~~~
我想你区别缓存与镜象的区别,大约是缓存对数据的保存不完整,并且无法自动同步,如果是这样,那我想我所说的缓存和你所说的镜象没有实际的区别。
没有Relation的OR/MAPPING在一些稍大的项目中会让你用起来异常苦恼,你会恨不得把他丢掉,继续写SQL或者存储过程之类的原始办法。
@none:
===你如果数据量小,你需要用这些吗?几乎是没任何必要,一般的机器就足够去支撑,做缓存没有任何意义,在数据量大的时候,缓存整个数据,仍旧没有任何意义==
这个我非常不同意,我针对的就是数据量小,数据量虽小,但你知道影响系统性能的瓶颈是什么吗?就是宝贵的数据访问资源啊!!!就是因为数据量小,访问频率高,才真正有必要放到内存啊。数据量大我也不推荐这样实现的。
要提高性能就应该在数据访问上找原因,数据库的缓存也是在数据库服务器上进行缓存,而且没有减少访问,没有减少应用服务器与数据库服务器之间的数据传输,而内存镜像呢,是在应用服务器上,根本不用去访问数据库服务器。而且优点就在于数据量小,但访问频繁啊,镜像表的话,只要第一个载入内存,以后N个人都不再去数据库服务器读取了,这性能难道你看不出来吗?
至于程序要看到效果的话,我觉得很简单,在SQL里监测访问好了,我测试过的,在内存中的数据访问在SQL监测中不会出现了。可以证明数据是从内存读取的。
至于ORM的Relation我也不想多谈了,我不反对Relation,只是从目前来说,利小于弊而已,你说的什么异常苦恼,恨不得丢掉,只是你一厢情愿说的吧。难道ORM没有了Relation就没有处理方式了,那也太弱了吧,我只是觉得Relation的东西,我用SPL的其他方式处理更简单,虽然在理论上好象“没有Relation”就不是完整的ORM。我想实用就可以了。
不用那么激动,将一个话题演变为争吵可不是什么明智的选择。
小数据表的访问,你也许太过于担忧了,仔细想想,真的有那么多吗?你的CPU负载超过80%了吗?当然前提是你花50%以上的软件成本来购买硬件,性能是相对的,这是个永恒的话题,没必要说得更多~~
而且不用强调不用访问数据库服务器这一点,关于这种东西,我去年已经实现过,也在应用,并且在不段的改进,我相信比我做得更早更好的人太多了,顺便再说一句,如果缓存的管理不能自动化,那将会让你非常的疲劳,如果不做一个适合的数据库连接管理器,也将无法最大发挥性能,因为如果数据都有缓存了,那么我还连接数据库服务器做什么呢?
说一个我个人认为比较理想的方法:
1、数据库连接从一个请求到结束,保持住,不用担心这个时间过长,如果你的一个页面消耗太多时间的话,那么无论如何都会占用一个连接,如果仅仅是程序的占用,那可以提前释放连接,看需要而进行,这样可以更好的在多层构架中实现事务,并且可以降低数据库消耗。
2、读取需要数据的时候,如果这些数据全部有缓存,那么不要连接数据库,如果全部或者部分没有缓存,那么把读取没有缓存的,并结合在一起,做最终的数据供应。
3、如果数据量大的时候把数据单条缓存,按需缓存,需要多少缓存多少,没人使用的数据不要缓存,节约资源,如果数据量大,那么建立全表的缓存,如果数据甚至不需要持久化,那么建立所谓的内存表,比如Online表,当然如果你觉得Online表不是那么重要的话
4、数据的插入、更新等操作都应该可以自动的同步缓存与数据库。
5、以上所描述,都必须可以自动完成,不需要手工做更多的干涉,这一点很重要。
再说Relation吧,首先我是一个无名小卒,很弱也是应当,但我想如果同一件事,大家想出来一个办法,为的是让他更简单更方便,却起了反效果,那么这个办法真的不是那么的理想,既然要用or那目的很显然是为了让数据库开发简单方便,如果因为用了OR反而要我每次做联表子表等操作的时候比以前更烦琐,那么对于一个商业程序来说,真的没有任何意义,因为一个商业程序中会充斥着各种Relation,不是吗?不要为了OR而OR
就实用主义而言,我谈谈我是怎么做的,为了省心,我没有自己做代码生成器,没有自己做对象基件,仅仅是用DataTable和DataRow来映射数据,因为如果大型项目,我们会用存储过程,小型项目,DataTable足也,更何况他内部实现了包括find在类的太多东西,我没必要自己再做一套,映射的时候,我也没有自己搞出一套什么对象查询语言,也就是把各个查询语句分离开作为条件,使用的时候ADD进去,然后组合一下,很简单的操作,对于数据自动更新,我有同样能够实现,这就足够了,更复杂的应用呢,我会用存储过程。
或许我是天生长有反骨;虽然看了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真的要取代传统还有长长的路要走;
我的看法或许是愚蠢的,真是这样的话请大家让列清醒;
3、如果数据量大的时候把数据单条缓存,按需缓存,需要多少缓存多少,没人使用的数据不要缓存,节约资源,如果数据量大,那么建立全表的缓存,如果数据甚至不需要持久化,那么建立所谓的内存表,比如Online表,当然如果你觉得Online表不是那么重要的话
写错了,应该是:
3、如果数据量大的时候把数据单条缓存,按需缓存,需要多少缓存多少,没人使用的数据不要缓存,节约资源(不敢想象把硬盘的东西都反到内存中的结果),如果数据量小,那么建立全表的缓存,如果数据甚至不需要持久化,那么建立所谓的内存表,比如Online表,当然如果你觉得Online表不是那么重要的话
其实做缓存这东西,早在一年以前,lostinet大哥就曾经提出来了,也这么做了,但由于他太忙,没有把他的自动化继续做下去而已。
有兴趣你可以到www.lostinet.com看看,他的论坛和他提供下载的东西,仔细看看吧。
@none :
你说的缓存机制我不知道是具体什么样的,最好能把你的缓存机制原理讲一下,或许我的机制确实有问题。你的缓存也是在应用服务器上的?那整个机制跟我的应该是差不多的,只是缓存什么内容不同而已,你说的缓存管理机制是不错的,但我想每次都要经常的判断是否值得,还有你说的以上都是自动的,我现在说的内存镜像也是自动的啊。所以我搞不清,你现在跟我意义反歧就在于缓存什么多少东西吗?如果是,你推荐一下原理,把实现机制也讲一下,因为有些道理归道理,实现的代价还是很大的。
至于Relation,我觉得是好的思想,我以后也可能会采用的,只是目前觉得它的存在增加的复杂性胜过了它的功能优点而已。这可能是我个人的想法。
@飞飞 :
至于ORM的话题,要讲的话,你可以开个专题,我在这里就简单回答一下你的问题。
使用sql的话,增加字段修改的地方会比较多,而且语句组装会带来非常不便,这Sql是个开发瓶颈吧,看看有多少程序员花多少时间在调Sql语句吧。ORM中的修改只要修改映射,然后赋个值就可以了。不是ORM不修改,而是清晰简单。
项目移植的可能性不会很大,可是当你要把项目标准化,升级为产品,那么你就得考虑多数据库支持,或者一个项目要卖给第二家客户,人家就是要求换数据库,数据库移植已经不再是传说。那么Sql语句带来的不兼容性会让你疯掉,ORM模式下数据库就可以无关,移植几乎不要修改任何代码。
@none :
我根本不知道那位大哥,我也根本不知道他的东西,他早就提出来,和我现在提出来有什么关系吗?
再说了,他都没有实现,就算实现了,跟我也没有关系啊。我提出我的,我自己也实现了。
我又不是抄袭。
首先呢,大家平静一些,我不是来吵架或者挑衅的:D
现在这样就不错,其实缓存的内容不是重要,关键在于自动化处理会麻烦一些,你说得没错,大家都是缓存在.net组件所运行的进程内的,而我说我的那一套,也没什么先进性可言,区别可能在于我更注重对大数据量的处理而已,关于关系呢,其实真的很好,用过之后才知好啊,呵呵,先接受他吧,接受之后再来给他一个合适的评价也许更合适。
关于LOSTINET呢,个人觉得他算一个技术比较不错的人,你可以到博客堂看看他的专栏,我记得博客园这边他也应该有个,但这边人太多,也不知道是否有按人名搜索的。。。
关于每次判断是否值得,这是一个比较头疼的问题,如果不判断,你怎么能确保缓存没有失效?判断一个对象是否为NULL似乎是最直接也最省效的方法了吧?或者你有什么更好的办法?如果有且愿意分享,不妨说一下?
谢谢dudu,不过LOSTINET好象没写太多东西,呵。
@none :
说真的,你说的那种把一条条记录缓存起来的方式,我个人感觉没有我的方式好,我的方式强调的前提你注意到没有:是数据量小,使用频率很高的数据,我强调的是数据分类对待,因此,对于这些数据,我个人认为这种方式不好,不管是从内存还是效率上来说。
而你说的缓存,可能适合于所有的数据,你的记录缓存方式,应该是没有考虑数据分类对待。所以在判断方式等势必会带来不方便,而且感觉有些事倍功半。
我感觉把数据分类对待,这对每个设计者来是应该考虑的一个问题,从这些数据中我们可以发现一些共同点,并可以思考一种方式来对待,我的内存镜存表也就是这种方式。
内存同步方面我的思考....
http://www.cnblogs.com/william_fire/articles/126665.html
缓存同步的问题,我认为还是需要缓存自己来维护的,不过,目前的数据库多得让人想杀人,实现的特性也各自不一,有些性能比较高的方案,只适合于一些功能上考虑得比较完善的关系型数据库。
以前看过听棠另一篇有关分页的讨论,结合这篇文章,感觉听棠是个实用主义者,开发思想上有自己的原则,非常强调在特定环境下讨论某种方案的优劣性,这一点我非常赞同。
当然,cnblog里有太多大师级的人物,不论哪种方案,大师们都能一眼看出在其他环境中,你的方案存在着这样的不足、那样的缺陷。
其实没必要这么较真,世界上没有万能的工具和解决方案,生活中大家都知道杀鸡没必要用牛刀,牛刀杀鸡也不一定好使,可为什么在讨论软件时就忘了这一点呢?
@Addie :
你说的蛮对的,其实我在思考理论的同时,会考虑其可行性与操作性,当然有些东西在不同的环境里是不适合的,但我想,我的理论还是有其存在价值的,可能是因为大家没有对文中提到的“维护型数据”注意吧,我的思想也只是为了解决这部分数据,对于其他的部分,我想大家谈的缓存方式可能更佳,就像NH可提的一样。
其实说真的,不管是什么理论,最好是一些可行的,我最烦的就是说了一堆的理论,但没有人在实现,有些理论是非常好的,但在实现过程中会发现事倍功半。
纯属个人意见!
呵,也许我不是很了解你所说的差异化,我目前的缓存策略提供了三个选项:
1、IndexCache
2、EntityCache
3、FullEntityCache
PS:我不会在大量数据的时候还把所有数据都缓存的,那是一种很愚蠢的行为,我曾经在我的1800+/512ddr266的机器上测试过2000万数据的分页读取(不做任何过滤,完全读取),占用系统内存大约160M,读取其中任何一页数据大约15ms-60ms之间,当然这是没有做一些优化措施的情况下的数据,仅仅是简单的用了IndexCache与EntityCache,或许你会认为这没有任何实际的意义,呵呵,我也这么认为,这就是一个测试而已,我没有想过我做任何事情的时候会需要去看2000W的数据,呵呵。。。会把我看死的。。。缓存的同步也解决了,可以实时同步,写的性能大约下降了70%左右,但我认为值得,毕竟写的频率会远低于读。
一些小表数据,如系统配置等,没有超过1000条数据的,我会使用FullEntityCache,看起来似乎有些类似于你所说的镜象表。。。
听棠.NET
感谢你的指点,我好象有点懂了;
--使用sql的话,增加字段修改的地方会比较多,而且语句组装会带来非常不便,这Sql是个开发瓶颈吧,看看有多少程序员花多少时间在调Sql语句吧!
哦!用ORM的程序员不用花时间调SQL,他们可以不用SQL,建立一个SQL映射关系好象不会那么复杂吧!!
--ORM中的修改只要修改映射,然后赋个值就可以了。不是ORM不修改,而是清晰简单!
清晰简单的代价是N个类,N个单元!
--可是当你要把项目标准化,升级为产品,那么你就得考虑多数据库支持,或者一个项目要卖给第二家客户,人家就是要求换数据库,数据库移植已经不再是传说
--那么Sql语句带来的不兼容性会让你疯掉,!!
似乎只有ORM才可以移数据库,而且我们也不要用什么数据库厂商护展标准SQL的部分,不晓得他们搞些扩展来干什么,有标准的不就行了嘛!!
--ORM模式下数据库就可以无关,移植几乎不要修改任何代码。
ORM的数据层是万能适配层,换数据库不用写第二个!!!
基于表和基于行的编成方式在.net中,因为DataTable和DataRow的存在的确是最容易不过的实现方式。OR这种东东,如果不是系统太大,逻辑太复杂,还是不要用了的比较好。一不小心,就会同样的数据创建两份对象,要避免这个问题,还得去写一个identitymap出来。
kknd,我是一个懒散的人,没有写BLOG的习惯。。。BLOG有不少,到处乱申请,也包括这里,但都没有什么内容。。。如果有什么要交流的,跟这个帖子好了,我加了收藏了:D
@none
向你请教一个cache同步的问题。
在使用了负载均衡的系统中,怎样同步各个服务器的cache呢?
我自己想的是不是使用msmq,某个服务器处理了更新后,发送一条消息到队列,其他服务器接收到这条消息后,相应修改cache。这样好吗?
请教之词愧不敢当,关于负载均衡中使用,说句让您见笑的话,我只用过冗余和自建的均衡控制,至于系统级的负载均衡真是没怎么接触过,但我想,如果负载均衡的调度中心如果可以实现进程数据的维护,就没什么问题,如果实现不了,问题就麻烦了,可以考虑采用你说的办法,又或者建立一个独立的CACHE服务器,又或者建立一个CACHE调度服务器,所以对CACHE的更新都将以这里为进出口,剩下的就好解决了,但无论如何,这都不是一个好解决的问题,刚才另外一个朋友(newlee)提到无状态的负载均衡中使用,那看起来似乎更麻烦些,我所理解的无状态负载均衡,进程数据是绝对不可维护的,那就真的只能自己DIY了。。。
首先向听棠的辛勤工作致敬!
然后谈谈我的个人看法:) 现在是OS时代,做这种中间件产品,大家都不可避免的会以整个软件界的标准来评价,而要做一个能实际有助于各种应用开发的ORMap工具,要考虑得完善的确非常难。
可以看出听棠是个热血青年,而且善于动脑勤于动手,但我觉得过于自信了,喜欢说这是我的看法,我不管别人怎么实现。我们能看到的产品,都是被选择出来的,作者都不简单。
你整体上的思路还不够成熟,这从不了解relation的重要性和本文的主题内存表这两点都可以看出来。建议多研究一些其它的ORMap工具,包括你提到的NH的发源H。
没有relation的支持,许多用简单的对象导航可以实现的操作就要改为手工实现,更重要的yok已经说了:无法实现oql。
而内存表,我觉得不值得专门花时间,重点应该放在缓存上。这里的关键是是否透明。大家都不实现内存表,不是因为它很难实现,而是因为它的实际意义很小,而没必要的功能多了绝对不是好事。有了完善的缓存机制后,如果需要内存表,应用程序只要用一小段代码将表读入就可以达到内存表的目的了。
你总结的“维护性数据、操作型数据、日志型数据”我们基本也这样分类,不过称呼是“只读类、更新类、追加类”这样比较省脑子一点,一般我们会设计一个表保存大部分代码类数据,这样实际上只读类数据表很少。
至于大家讨论的内存表的占用内存问题,我觉得不存在吧?只读数据一般都不会太多,代码性质的一个系统中最多几兆吧,如果是其它只读性质的数据就不要放进内存表啦。
@floe :
你好,我想你也考虑过“只读类”数据的性能吧,所以你说的“没有必要”感觉和你说的自相矛盾啊。这些"只读类"的内存缓存有没有必要,这应该是每个人有每个人的看法,不同的项目有不同的要求,所以你说的“没有必要”那可能是你的看法。
而这个只读类的实现,大家可以有自己方式,我也只是说了一下我是如何来实现的。方法应该有很多种。
说没有必要的原因是有完善的缓存机制就不需要内存表了,而完善的缓存机制又是一个工业级的ORM必须实现的
在我的理解中,内存表应当是不需要持续化的,譬如Online数据,只要是数据库中有的,在内存中所做的映射就该叫缓存,这是我的个人观点,关于orm中的relation我们就不过多讨论了,每个人的实际需求可能不一样,一个十分简单的应用将不会需要用的字母表之类的东西,至于复杂后,为了ORM而放弃了方便的relation改用自己手工做,我觉得不是很明智,也不用因此觉得自己这么做很强,其实做程序的目的就一个——简化工作而已。。。
floe:赞成你所说的工业级的orm需要支持缓存,但我仍旧认为,缓存在某些时候的实现,还是有很大的区别的,简单的把数据在内存中做个映射,并且在知道数据变化后重新载入,这种基本的功能,还不足以胜任,将数据整个在内存中镜象一个就更是一件十分简单的事,不足以纳入工业标准来说了,工业级的orm应该有更强大更先进的缓存技术。
工业级的ORM中缓存是必需的,同意。
个人感觉内存镜像与缓存还是存在很多不同的,缓存的调度机制与内存镜像的机制是不同的,用内存镜像可以更好的解决这些"只读类"的数据,当然可能你觉得没有必要。
其实一个好的缓存机制也不是很简单的事。不知道哪儿有好的缓存机制介绍,我也想学习与研究一下。
@none
使用负载均衡的目的就是分散web服务器的压力,提高系统的负载能力,如果又单独使用一个cache服务器,会不会把压力又集中到了cache服务器身上?
个人觉得,cache还是应该由业务逻辑来决定,放在dal可能并不适合。
kknd:所以这个问题没那么好处理,要么原有的负载均衡设备支持CACHE,要么自己做CACHE的负载均衡,当然,做得简单一些,搞个调度设备来负责通知机群更新CACHE
业务逻辑也可以做,但那肯定会大大的加大代码编写的工作量,也可以采取合理的办法,项目不大的时候,在DAL做,很合理,项目大到需要N台以上的机器做负载均衡了,那也有足够的人力物力在逻辑层实现CACHE,或者自己实现CAHCE同步调度了。
听棠.NET:一个非常好的缓存机制,我也十分渴望了解,但就目前而言,我的方法和你的大同小异,无非我还能索引缓存与实体缓存分离的方式来支持大数据缓存而已,没有本质的区别。
至于这个东西到底是缓存还是镜象表我觉得就没有争论的意义了:D
作者的原话:
这种非ORM模式开发的系统,系统扩展性非常差、数据库移植性差、开发效率低下
系统扩展性非常差:我勉强可以接受.
数据库移植性差:也还说的过去
开发效率低下:应该反过来说吧!
关键是运行效率,在企业开发中,多表的关联 7,8个是很多的.
ORM的速度根本无法接收. 至于你说的缓存,一次能缓存100万条记录合适吗!即使在中间层.
ORM我感觉很难在我的系统中实现,请多指教!
@陆刚 :
ORM的开发效率高于非ORM,你如果对此有不同意见,可以开个主题,问问BLOG上的朋友。
至于我说的缓存,你为何不看清我说的前提呢。“维护型数据”,你看了我说的“维护型数据”的特点了吗?
数据量少!!!
我只是希望你在看清并能理解我的东西后再发表你的看法。
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?
哦,上次问你怎么存放常用数据到内存中,原来你说的就是这个IsSaveToMemory="true",我一直不知道,都是直接访问数据库的.
还好数据量小,再让用户加了内存,呵呵,感觉也蛮不错的.
对了,在使用中只要在xml里加上IsSaveToMemory="true这个就可以了吗?还是一定要用Retrieve()之类的东东?我在程序里用了很多的比如:BankEntity Bank = BankEntityAction.RetrieveABankEntity(e.Row.Cells.FromKey("Branch").Value.ToString().Trim());的代码,或者是根据其他条件查询rc的,是不是直接可以用的?
@ jez :
只要在XML中设置一下就可以了,其他的所有的工作都是SPL给你完成的。你的代码不需要修改的。
我觉的有这么一种情况:
有可能我对某些表的内存的使用只是在一段时间内比较频繁(比如说是页面打开的时候),但是用后我认为这些内存数据在另外的地方不常用了,为了减小内存占用量,要把它释放掉,这个怎么办?
最好是在实体类里面有一个开关做另外的控制
我发现一个BUG, 当已经实例化了一个实体,就无法以非主键字段为条件retire实体集合,情况发生在IsSaveToMemory为true的情况
@Mike Chan :
不管是不是“保存到内存”了,Retrieve都是针对“主键”进行查询的,所以,如果是非主键的话,请采用RetrieveCriteria,因为Retrive出来的是唯一实体,这只有在主键下才可能,如果是非主键,则“可能会有”多条记录。
听棠兄,出错了。我在SPL中用isSavetoMemory=true,就出错了。
RetrieveCriteria rc = new RetrieveCriteria(typeof(SiteColumn));
Condition condition=rc.GetNewCondition();
condition.AddEqualTo(SiteColumn.__PARENTCOLUMNID, DefaultValue.DefaultTopParentColumnId);//如果把这两个句注释,又不出错了。
return rc.AsEntityContainer();这句出错。
我说的问题其实同2006-05-05 13:26 by Mike Chan ,好像真的有BUG
有个问题怎么解决:如果多个人都把同一个表的内容保存到内存中了,其中人一个人执行了删除操作,这样也删除了数据库,这样怎么才能维护数据库和其他人所维护的内存的同步,不然其他人如果恰好用了这些数据 那么就是用了不存在(删除)或者错误(更新)的数据了
听棠兄,出错了。我在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这样的字段名了
看了 各位 大虾的评论有写晕晕的 自己什么时候才能像各位大虾那样有水准 佩服佩服 !!!!!!!
出售蓝奇高级验证码识别引擎,可准确识别新浪动网淘宝CSDN等多种复杂验证码。
输出为一个标准DLL,可供VB,VC,Delphi,C#.NET,VB.NET,模拟精灵,按键精灵等多平台调用,调用方法简单,几行代码即可完成。独具特色的边缘检测字符分离、旋转倾斜纠正和通用字符匹配算法(无论字体和大小), 使得该引擎对于像新浪、动网、淘宝、CSDN等多种验证码均有不错的识别率,是一款效果较为理想的验证码识别引擎。附详细的调用实例和代码注释等相关技术文档。
官方网站 - http://***/yzm_advocr
识别效果怎么样一试就知道 - DEMO下载 http://***/yzm_advocr/advocr.rar