banq
2009-04-28 14:18
你的意思是仓库控制了缓存访问?应该是工厂吧,呵呵,我大概知道你一点意思,其实在service里也可以直接从缓存中获得某个聚合根对象。

我的意思是:Repository重点是将折叠的椅子还原成可以坐的椅子,而与缓存控制关系不大。

>那么当对象被清除的时候,将其持久化,这样我们甚至不需要每次更新的时候都持久对象到数据库,

你说对了,我们现在就需要这样的新式框架,通览当前Java开源领域,没有看到这样机制的持久层框架。如果有兴趣者,不妨做个这样框架,可以在Hibernate上加工,很先进哦。

[该贴被banq于2009-04-28 14:19修改过]

xmuzyu
2009-04-28 17:18
>>我的意思是:Repository重点是将折叠的椅子还原成可以坐的椅子,而与缓存控制关系不大。

恩,我理解banq老师的意思了,因为我在一个项目中采用了通过仓库控制了缓存的访问,service层获得聚合也要从仓库中取,banq 老师的意思是通过工厂来控制呵呵,只要DDD的精髓掌握了,至于控制方面,可以灵活一点呵呵。

ps:我这个用DDD开发的项目是一个和同学创业的项目,用了自己的想法来开发,用了ehcache做为缓存系统。并且我们系统session里面几乎没有数据,很多的数据我们都是放到了全局的缓存里。

至于公司,悲哀的是国内用的少啊,比如最近我公司的建行项目组,全部都是采用过去传统的开发方式,业务对象是由数据库表导出的,不过这也有原因,毕竟建行已经积累了很多年,要想把人家积累N年的平台换掉不切实际呵呵。再者来说,建行有钱啊,服务器,应用服务器,数据库都用几乎世界上最好的,没办法,性能不好,垂直伸缩。

banq
2009-04-28 18:21
>,建行有钱啊,服务器,应用服务器,数据库都用几乎世界上最好的,没办法,性能不好,垂直伸缩

其实软件不具有可伸缩性,就是花再多的钱也是无济于事,这个道理已经被国外很多年的发展证明,所以,才有Java在世界五百强大型企业中应用和普及。因为这些企业IT主管已经认识到:硬件可以用钱买的,如果软件架构设计做得不好,造成的损失将是金钱无法弥补的。

特别是业务模型,DDD谈到核心领域模型是一个公司的核心资产,甚至是核心竞争力,软件也是资产意思就是体现在核心领域模型上,这个是你公司通过时间迭代对业务深入认识升华后的结果,是别人无法通过钱买到的。

我们有的人分不清楚核心领域和通用子领域,以为所有软件都是资产,其实通用领域的东西可以通过购买 使用开源或者外包形式实现,但是核心领域是自己的。

所以,对于一个像建行这样的大公司大项目,性能不是重要的,就象你说可以垂直伸缩,甚至可以用银河计算机,国家的钱,但这些都不是重点,重点是软件核心领域。

如果我们建立了核心领域模型,核心领域的核心就是一个个聚合根和边界,那么就自然能够使用内存缓存计算,就自然有好的水平伸缩性,这是一个自成体系的,为什么大道至简的路不走,要走阳关小道呢?这其实就是知不知道的问题,知不知道软件世界有这么一个好体系,现在很多人都不知道,如果知道,他肯定会选择。

我看到有Repository中进行缓存和DB同步方法,如下,Repository应该更侧重DB,而缓存是无处不在的,这是想表达的意思,可能和你表达意思不是在讲同一件事情,角度不同:

public interface Repository

{

/** Syncronizes the database.

* <p> Can be used, for example, to save the current memory image of the DB. */

public void sync();

/** Returns the numbers of users in the database.

* @return the numbers of user entries */

public int size();

/** Returns an enumeration of the users in this database.

* @return the list of user names as an Enumeration of String */

public Enumeration getUsers();

/** Whether a user is present in the database and can be used as key.

* @param user the user name

* @return true if the user name is present as key */

public boolean hasUser(String user);

/** Adds a new user at the database.

* @param user the user name

* @return this object */

public Repository addUser(String user);

/** Removes the user from the database.

* @param user the user name

* @return this object */

public Repository removeUser(String user);

/** Removes all users from the database.

* @return this object */

public Repository removeAllUsers();

/** Gets the String value of this Object.

* @return the String value */

public String toString();

}

[该贴被banq于2009-04-28 20:37修改过]

xmuzyu
2009-04-28 22:16
呵呵,多谢板桥老师。我以前的思路是缓存管理器封装了缓存,仓库引用缓存管理器,然后service里面取数据还是要通过仓库去取数据,因为仓库要判断缓存中是否存在所请求的对象,如果存在就直接返回,如果不存在就从DB里面取出后,放回cache里,并且仓库还要保证放到cache里的对象是一个完整的聚合根。经过您提醒,我发现其实可以将判断对象是否在缓冲的职责从仓库剥离出来,分配给另外一个代理类,这个代理类判断对象是否在缓存中,如果在那么就直接返回,如果不在的话就通过仓库从DB取出来。此时仓库主要负责从DB拿出来的对象是完整的,而这个代理类要保证缓存中的对象是完整的,比如一个对象的局部状态发生改变,那么需要刷新状态的时候,就通过这个代理类来完成,这样仓库保证了从DB拿来的对象是完整的,而代理类保证了已经在缓存中的对象是完整的呵呵。

呵呵,关键以前我那个项目是用仓库代替了dao,所以将判断对象是否在缓存的功能也给了仓库,仓库还要保证从DB拿来的对象的完整性,而现在可以细分一下呵呵。

[该贴被xmuzyu于2009-04-28 22:38修改过]

banq
2009-04-29 09:27
缓存控制处理还是一个细节,我的经验过去是将缓存和数据库作为一个来源,就象另外一个帖子讨论,将数据库读写分开,分成两个数据库,只不过我们用缓存替代另外一个读数据库,保留写数据库事务,这样打破垂直伸缩的局限,将水平伸缩和垂直伸缩有机结合在一起。

原来是把缓存简单当作一个数据源,后来发现这个思路又受了数据库的影响,缓存不是数据库,没有一个定型边界的限制,缓存是对象形影不离的,是对象的空间 水和氧气。

对象生存在缓存内存中,就象我们当前时空一样,有时我们会将空间位置作为分辨一个实体的标识,比如两个一模一样的桌子在我们面前,但是我们可以分辨它们,因为我们已经将空间位置作为桌子的标识。

同样道理,两个对象在内存中,之所以不同,从技术角度讲它们的内存地址不一样,但从我上面时空角度来讲,就更加本质,就和实体标识这些知识融会贯通,自成一体。

所以,从将读写数据库分成两个数据库的性能优化概念出发,将读的数据库用缓存内存的对象来替代,不但解决性能问题,而且无缝和需求分析,领域建模对接,多么美妙的事情。

大数据性能优化:

http://www.jdon.com/jivejdon/thread/36068.html

freeren
2009-04-29 10:22
哈哈,又见很精典的讨论贴,学习了,真的学习了!

不过小弟在此也有个问题和各位请教一下:

在楼主与banq老师的讨论中除了谈到仓库和工厂、缓存,但没谈到池这东西,想知道池应该是做为工厂呢还是做为仓库或者是其他的?

xmuzyu
2009-04-29 10:24
>>所以,从将读写数据库分成两个数据库的性能优化概念出发,将读的数据库用缓存内存的对象来替代,不但解决性能问题,而且无缝和需求分析,领域建模对接,多么美妙的事情。

同意。并且缓存还有一个优点就是:它可以更具系统运行的情况动态的来适应系统的要求,缓存主动迎合系统的要求。比如:缓存的置换算法一般情况下都采用LRU,这样以来,缓存中的对象其实是经常被用到得,而那些不经常使用的,缓存系统已经控制将其移除了,所以从这个角度来说,缓存就好像系统的母亲一样,能根据系统这个孩子的运行时状态,动态的满足孩子的要求。

不过根据我在缓存实践来说,我发现要想实现缓存,面向对象的设计是一个挑战,如果对象的状态没有封装好,这样系统中到处都充斥着改变对象状态的代码,根本没有办法控制缓存中对象状态的并发访问控制。所以要想用好缓存,我们首先要设计好对象模型,能用private的地方尽量用private,这样的目的是封装。能用final的地方尽量用final,因为final类型的变量,JVM可以保证初始化安全性,这样更加容易做到安全的发布。

ps:最近越来越发现<<java 并发编程实践>>经典了。

xmuzyu
2009-04-29 10:32
>>在楼主与banq老师的讨论中除了谈到仓库和工厂、缓存,但没谈到池这东西,想知道池应该是做为工厂呢还是做为仓库或者是其他的?

呵呵,个人观点是:池不需要我们手的的实现,因为容器帮我实现了,这也是Java发展到今天的格局。比如目前的轻量级的IOC容器或者EJB容器其实底层都有池的概念,它的目的就是通过对象的复用来提升性能。并且池里面的对象有个特点:那就是没有标识,它不区分谁是谁,采用池的好处就是可以限制并发的线程数量,从而避免因为一时线程数的突然增加导致系统崩掉。

上面是有关的池的,至于池,它里面主要保存的是无状态的功能性的,不需要考虑线程安全问题的组件,而我们的业务模型,通过什么来提升性能呢?那就要靠缓存了。

所以池和缓存都是提升系统性能的手段,只不过池的工作有容器帮我做了,而我们只要集中精力做缓存就好了。

ps:更多关于池和缓存的讨论,请参考<<面向模式的软件体系结构>>卷3,资源管理。如果大家对服务器底层设计感兴趣,也可以看看卷2.

banq
2009-04-29 10:42
>池应该是做为工厂呢还是做为仓库或者是其他的

Pool是处理无状态的,我们现在讨论的是需求分析设计,聚合根实体代表一个现实时空的事物,都是有数据状态在其中。

讨论到这里,我们要明确什么是缓存?缓存=对象唯一性+内存。

缓存就是保持聚合根和边界内对象的唯一性,因为内存有限,而且也没有必要为每个类在不同的客户端事件下,创建一个对象。

这里要谈到经典架构SSH: Struts+Spring+Hibernate中有一个OSIV也就是Open Session In View模式,Hibernate缺省的缓存是Session内的,也就是每次请求处理中缓存唯一的,而不是application级别,全局唯一的,这其实就是Hibernate的一级缓存,是内置,要达到我们这个主题讨论的缓存目标,必须使用Hibernate的二级缓存。

换句话说:也就是使用SSH,就会发生我前面讲的,每次请求都重建一次聚合根和其边界内子对象,这个很耗费性能,也就是根本无法实现聚合根和缓存的唯一性,我们这里讲的美妙设计无法在缺省的SSH架构中实现。所以,很多人就是使用了SSH,还是走到了数据库为中心的编程模型,因为SSH没有缺省提供一个application级别的缓存,这样,如果要实现我前面讲的JiveJdon中updateMessage中功能,就必须自己有意识去做,没人点拨指导是想不到这条路的,这也是我们以前指责SSH容易导致数据库编程问题关键所在。(说句笑话:大家应该明白为什么Jdon框架推出虽然落后于Spring,但是早就达到5.0中级稳定版,而Spring正在向3.0买进呢)

所以,在SSH架构中必须使用二级缓存,手工显式配置缓存,然后,做好自己的工厂,保证缓存中聚合边界内的对象们都是引用指向的是这个边界内的对象,不能指错了,如果存在两个聚合体对象群,其实它们是反映需求中一类聚合设计,那么变成多个对象群,边界内对象互相引用,陷入失败地步。

有了以上前提保证唯一性以后,java 并发编程实践就能派上用场了,我们甚至可以利用Java并发锁在内存中来优雅高性能解决并发写问题,这样避免数据库锁的低性能和粗粒度的排他性。

相关并发锁帖子:

http://www.jdon.com/article/34773.html

[该贴被banq于2009-04-29 10:43修改过]

freeren
2009-04-29 11:31
呵呵,看了banq老师与楼主的回答,明白了很多,谢谢!

>>至于池,它里面主要保存的是无状态的功能性的,不需要考虑线程安全问题的组件,而我们的业务模型,通过什么来提升性能呢?那就要靠缓存了。

茅塞顿开啊!

至于老师提的SSH中的hibernate的缓存(不管一级--session级还是二级--全局),我们公司目前都不用,本人一直推荐,可惜公司怕所谓的风险(本人觉得风险在于一开始的需求分析设计,而不是在于有没有用过),所以公司目前所谓的缓存基本是用web 容器的application--在容器启动,把一些基础数据加载进来的形式,还有就是死命往httpsession中加数据的形式!看来这样的风险更大!

yuer0
2009-05-13 13:40
受教了,非常感谢各位!

分析领域核心模型,使之凌驾于缓存之上,把曾经的数据库操作就交给后台的Runner。

非常激动人心的世界,值得思索。

banq
2009-05-13 18:04
感谢yuer0肯定,与yuer0英雄所见略同,总算有同行者。

[该贴被banq于2009-05-13 18:05修改过]

xmuzyu
2009-05-13 18:31
呵呵,终于有个志同道合的人了,不容易啊。

yellowcat
2009-05-16 14:39
引用banq大哥的话:

“缓存=对象唯一性+内存”

(这句话真是经典的话!!!)

我们这里讲的美妙设计无法在缺省的SSH架构中实现

(其实这种大哥说的这种美妙的设计在GAvin_king在他的著作中早有以下评论:

“对于典型的web或者企业应用程序,持久化上下文范围(这里也就是session的范围)的同一性是首选(也就是session per request),跨多个工作单元(这里的工作单元其实也可以理解为request)的实例重用,在高速缓存利用和编程模型方面提供了一些潜在的优势,但是在一个遍布多线程的应用程序中,始终在全局的同一性映射中(也就是session per application)同步对持久化对象的共享访问成本太高,难以支付。更简单并且更可伸缩些的办法是,让每个线程在每个持久化上下文中使用一组独特的持久化实例”

)

gavin_king引入了一个概念就是利用hibernate提供的这一层持久化层来尽量减少数据库命中率(说白了就是提供缓存),并可通过2个方法来实现:

1.利用托管对象(说白了就是一个持久化对象在request结束则session关掉变为托管,这个托管的对象形成一层缓存,第二次requst来就不用命中数据库load了,直接reattach,又变成持久对象)(session per request with detached object)(同时要解决好同一性问题也就是banq大哥所讲的唯一性),因为提供缓存必须要保证聚合根的唯一性

2.扩展持久化上下文的扩展(说白了就是在两个request之间session不关)(session per conversation)这里就不用考虑唯一性问题,因为在一个session中(这个很重要)只要数据库记录相同,hibernate自动会保证java对象的同一性。

xmuzyu
2009-05-16 23:34
缓存用的地方非常多,比如处理器缓存(这就导致了并发编程的内存可见性),JVM缓存,缓存范围也很多,request,session,application或者cluster,根据缓存内容也可以划分好多种,比如静态内容的缓存,model缓存。yellowcat兄谈到的是session级别的缓存,不过它也是一种model的缓存。

而这个帖子的主题意思其实就是全局的(application,cluster)缓存,这是一种model缓存,针对业务对象的缓存。这种缓存的意义不仅在于减少了数据库的访问,也很大程度的减少了数据库事务给性能造成的影响,因为很多的操作,我们都可以通过内存锁实现,而不是依靠数据库事务的隔离级别实现并发控制,这个时候的并发有应用程序来控制,有点像悲观离线锁模式,而事务只需要保证数据完整性和持久性。

猜你喜欢
7Go 上一页 1 2 3 4 ... 7 下一页