banq大哥说的有理,其实锁不锁的还是要看业务知识的,Thread添加message,也就是message数量发生了改变,如果我是一个普通jdon用户,我不会关心此时到底有多少message的,如果我是一名广告商,会为这个thread下每个message付一笔广告费,我就会关心此时的message数量
所到底其实还是一种锁与业务伸缩性之间的博弈

>锁与业务伸缩性之间的博弈

非常精辟,实际也就是锁粒度的粗细,也是决定程序员水平高低的关键衡量标准,粒度越细,不是粗粒度板块一个,那么
伸缩性就好。

所以,我现在开始不喜欢那种缺省为每个类提供缺省的事务支持的框架了,比如EJB/Spring+Hibernate,很多人误以为JTA
是EJB特有,实际不是,JTA是JavaEE一个组件,我们可以直接操作使用它,我喜欢缺省没有事务的真正POJO架构,因为事务
JTA实际是一段锁,那么就可能带来粗粒度,特别是用在Service上,粒度更粗,简直可怕。

事务或锁应该深入到具体领域模型中,降低锁或事务的粒度,提高软件的伸缩性。

锁的学问非常大,这一点可以参看数据库的锁一部分的设计,会对你的设计有很多启迪。

>>具体可以看看JiveJdon3.6 的代码,在messageKenerl中,数据库持久只是一个单独的动作,在某个时刻对于修改内容来说,是否持久保存到数据库,根本不影响我们业务,见 updateMessage方法,你可以屏蔽了messageTransactionPersitence这个动作,当你修改帖子之后,大家还是能够看到帖子修改后的内容,因为我们修改了内存缓存中这个帖子,但是没有保存。

>>如果这个时候服务器当机,当然我们的修改结果,下次重启就没有了,那么我们还是需要messageTransactionPersitence持久的,但是我们的业务不再依赖它,它可以放在一个异步机制或抛新的线程来执行,与我们业务无关,只是一个save保存动作,就像我们编辑文本打字,经常按ctrl- s保存一样。


今天认真的看了一下这个帖子,说一下我的不成熟的看法:

首先在程序里面什么是最重要的??是速度、健壮、负载、容错??我个人认为是准确性。请让我们站在使用者的角度上来思考这个问题。当你提供给对方一个接口,有一个使用者来使用它,当他使用的时候发现速度慢,他可以使用缓存或者异步的方式来解决;不是很健壮,最起码可以捕捉错误,最起码知道我的请求没有完成;负载,如果是黑盒的话使用者应该不用关心这个问题;容错,使用者可以通过自己的方式进行解决,但是准确性不行,当你使用一个接口的时候你是希望获得一个准确的答案那还是希望得到一个可能准确的答案那??

我们现在来说异步方式的进行持久化的问题,异步就要放到队列里面,我们假设来买一张飞机票,那么可以交钱,查一下缓存,没有被订就购买,然后出票,我们先不说系统崩溃的风险,只是说你如何保证在你队列前方没有跟你订同一位置的人那??那是不是在加入队列的时候要查询整个队列那??

使用新的线程也无所谓,本身异步就是需要新的线程。直接使用新的线程是必须与当前线程保持同步的,也就是说只有新的线程返回,当前线程才能返回,这对效率有什么提高那??白白浪费了系统资源(多创建了一个新的线程)。

所以我的经验通常是,只有是void,且不能有out返回的时候才可以考虑这种方式,不过你应用中有多少void的那??大家可以看看自己写过的代码,然后统计一下。

其次、缓存的位置,应该在那一层进行缓存。我的看法是如果你不能很好的控制缓存(所谓很好的控制缓存就是保证返回的数据一定是正确的),那就交给使用者。越上层越靠近业务,也就更加清楚业务对数据的要求是如何的。与其你为了速度而给与了不准确的数据,不如让使用者察觉速度很慢而自己确定使用方式。

ACoder讨论的可能是异步架构的一些使用场景,思路是对,异步其实是多线程的一个架构升华,可以集群和分布式负载。

我就从大家熟悉的多线程来谈异步:如果从当前的线程抛出新线程,当然这个新线程执行的业务就不能与当前线程有关系,也就是必须松耦合,否则就会发生ACoder说的需要两个线程控制同步的事情,这很困难,几乎做不到。

所以,两个线程执行的业务松耦合是前提,实际上,这就是伸缩性的一种设计追求,如果我们能够将一个业务划分为松耦合的两个部分,那么我们就说,伸缩性提高了,因为可以分开执行了。

同意banq所说的松耦合可以提高伸缩性。

但是缓存更新和数据持久化是否可以看作松耦合要根据不同情况。比如:论坛发帖子,可以忍受系统问题造成最后几个帖子丢失,所以持久化可以异步来做。对于银行业务,不能忍受数据丢失和不一致,所以必须同时持久化,因而不是松耦合关系。

因此,是依赖缓存提高性能,还是依靠持久化提高可靠性,要看具体问题。

我比较倾向于由应用管理缓存,而不是用不了解业务的纯缓存服务。理由是应用了解不同对象的对于性能和可靠性的优先级,可以更好的控制缓存和持久化。纯缓存服务不包含业务,很难平衡性能和可靠性。

to usejava
确实是这样啊。松耦合可以提高伸缩性非常到位的一句话哈。目前我也在想,能否将持久化专门由封装的executors框架来完成,但是这样同样面临系统资源问题,持久化的线程也会由占用很大的系统资源,而采用传统的方式直接在同一个线程中完成持久化,这方面我也还没做过具体的测试,只是在尝试。另外一种就是采用JMS来异步的完成持久化,这样的话伸缩性应该很不错,但是同样面临数据丢失的问题,这样就还是要靠冗余来减低风险,对于数据要求严格的领域就不太适用了。并且在缓存设计方面要求有深厚的并发编程功底,一不小心,也许锁粒度太大,造成了资源竞争非常严重,这就反而抵消了缓存给性能带来的提升。

缓存确实是好,但是还有很多问题,需要我们去解决,去思考,希望大家能将自己实践的结果都发出来,一起借鉴,一起进步。哈

to xmuzyu
我也有同样的考虑,所以在探索是否可以把DDD建模的分析方法和传统的数据库技术结合起来。简单的讲,就是用DDD分析问题,确定系统大的结构,进而实现业务逻辑。然后,采用传统的数据库访问技术来实现持久化。
毕竟数据库有很多提高性能的方法,比如JDBC的addBatch。如果完全靠框架,恐怕很难利用上。不能充分发挥系统各个部分的能力,造成整个系统性能不好。

松耦合与可靠性是没有矛盾的,只有松耦合才提高可靠性,所以,取决于你对可靠性的宏观了解,就象开饭店,你觉得把配菜做菜由一个信得过的厨师统一掌握比较可靠,还是分工比较可靠,当饭店客人很多时,一个厨师就忙不过来,你这个饭店就没有伸缩性,只能永远是小饭店。

异步方式不代表不可靠,反而是更可靠的表示,如果持久化失败,可以通过再加一个异步来反馈,可见在大数据量讨论中的那张多个JMS Queue组成的大图:http://www.jdon.com/jivejdon/imageShow.jsp?id=6933&oid=23122239

缓存使用中一定要结合OO设计,否则只是数据库的遮羞布,只有超越关系数据库,才能避开关系数据库的低天花板,例如:依靠DBC契约设计中对象不变性要求,来通过对象自己的方法加锁的方式维持自己的不变性。缓存使用得好,其实就是对象设计功底淳厚的象征:
http://www.jdon.com/jivejdon/thread/36303.html


[该贴被banq于2009-05-21 14:23修改过]

其实说到缓存,主要有三个切入点
1.事务相关缓存:可能是一个数据库事务,可能是一个对话,只有在当前工作单位有效,这样的缓存不会被并发访问
2.process相关缓存,被并发的工作单元或事务共享,缓存中的数据被并发线程访问
3.集群相关缓存,同一台机器的多个process之间,或者集群下的多个机器之间共享,主要关注网络通信

对于应用以上几种缓存的一点经验是对于多用户,可伸缩性要求很高的web或企业级应用,应该避免内存中保存锁(除一些内容管理类型的应用程序),而推荐大量应用事务范围缓存(类型1),但是也有限制,比如内存要求很大,因为事务范围内存消耗和并发数成正比

对于process相关缓存(类型2)或集群范围缓存(类型3)一个比较好的备选是
a.很少改变的对象
b.不重要的对象(如内容管理类型的应用)
c.应用程序固有(数量一定)的对象
d.被其他类的很多实例引用的实例(称为引用数据)

还有一点必须说明:缓存缓存,一个“存”,一个“缓”,“存”自然是存在内存,别忘记还有一个“缓”,“缓”的又是什么?缓的就是那个"比内存访问慢很多的"的那个访问方式(可以是数据库访问,也可以是文件访问,也可以是网络访问)。现在大量采用数据库,那么缓存就逃脱不了sql的大环境,也就是那个"缓"的对象
[该贴被yellowcat于2009-05-21 14:58修改过]

>>缓存使用中一定要结合OO设计,否则只是数据库的遮羞布,只有超越关系数据库,才能避开关系数据库的低天花板,例如:依靠DBC契约设计中对象不变性要求,来通过对象自己的方法加锁的方式维持自己的不变性。缓存使用得好,其实就是对象设计功底淳厚的象征:

同意banq老师的看法,在加锁的方面粒度要尽量的小,在设计到业务对象的集合状态的时候,一般采用java.util.concurrent包中的类,这样还可以通过委托来实现线程安全。其实我觉得面向对象编程,在一定的程度上是让我们面向内存编程,相信内存,运算由内存运算完成,不是传统的表面上用了对象,但是都是数据容器,真正的逻辑都跑到数据库了,逻辑跑到数据库水平伸缩性就变的很差。

to xmuzyu:
不使用缓存,不面向内存编程,并不代表逻辑就到了数据库里。

业务逻辑还是可以在对象里实现,只不过所有的读写操作都直接访问数据库。简单的讲,就是数据库访问只是单纯的数据读写,不涉及任何逻辑。

没有缓存,性能可能会下降,尤其是读取性能。

我们也可以相信内存,否则也别用计算机了。但是相信是有限度的。如果可以完全相信,就不需要备份了。举个例子:上百万美元的SUN UNIX服务器,内存带校验,也坏过,甚至CPU都出过问题。

没有哪个企业应用不备份的。为什么,除了可以追述历史,最大的原因就是基于系统一定会崩溃。重要系统甚至要求异地备份,因为可能火灾或地震,同一地点的服务器和备份一起毁掉。这种事情发生的概率非常小,但是还是要花几百万美元去做异地备份。原因就是数据可靠性重于一切。

所以我还是坚持是否使用缓存,应该看具体的需求。不能一概而论。

to banq:
我认为可靠性和伸缩性是不一样的概念。多个厨师可以同时服务多个客人,伸缩性很好。但是,所有厨师都要依赖煤气供应来开火炒菜。煤气断了,再多厨师也做不出菜来。从饭店角度讲,可靠性并不好。
做个不太恰当的比喻。可以把饭店看作整个系统,厨师看作缓存,煤气看作数据库。厨师和煤气是完全不同的两回事,再多厨师也代替不了煤气。厨师多了,只不过提高了厨师队伍的可靠性。同样,缓存多了,提高了缓存系统的可靠性,但是数据库当了,整个系统还是停机。
异步反馈并不能提高可靠性。比如:从取款机取钱,现金取走后,持久化失败。即使有反馈又如何,难道要取款人把钱退回来吗?而取款机采用持久化和吐现金是一个同步事务的方法,不能持久化根本就不出现金。我认为还是现在的方式比较好。

>>业务逻辑还是可以在对象里实现,只不过所有的读写操作都直接访问数据库。简单的讲,就是数据库访问只是单纯的数据读写,不涉及任何逻辑。

恩,这个我认同,但是不一定缓存都是只读的,有些缓存的聚合根对象是可以全局写的,内存中通过细粒度的锁的并发性不会低于数据库的锁(当然我是指悲观锁),所以缓存不一定都是只读的,对象设计好以后,可以让所有的线程并发写操作,这样并发性会很好。

还有可靠性的问题,其实数据可靠性和完整性在不用缓存的时候也会遇到,因为缓存本来就是暂时存的,并不是持久化存储,持久化存储还是要靠数据库的,持久化的操作可以由专门的异步组件来完成,当然了异步失败可能造成数据的丢失,所以对一些对安全性和完整性要求高的系统,采用全局写缓存的话,异步持久化也可能出现问题,并且如今也没有出现可靠地方案。所以缓存也不能100%的解决所有的问题,但是大多数情况下,我还是非常喜欢缓存的。

我个人认为只要系统采用了面向对象的设计设计了很好的聚合,封装的比较好,肯定会用到缓存,如果没有设计好对象模型,用缓存也只能缓存只读的数据,而只有真正面向对象的设计,才可以让缓存中的对象全局并发写操作,这样也才能真正的发挥通过内存业务对象本身,细粒度的控制并发的效果。
[该贴被xmuzyu于2009-05-23 14:20修改过]

大家讨论得很好,但是不能就缓存谈缓存,缓存的关键实际是为对象开辟了新的生存空间:计算空间,而数据库是对象的存储空间,因此缓存引入实际是分离了计算和存储,从而才有云计算和云存储的可能;如果没有缓存这个对象新的生存空间,那么无疑计算和存储是耦合在对象的唯一空间:存储空间。就象一个厨师又要做配菜 又要烹饪一样。

转帖一个基于内存的云计算产品Terracotta,可以拓展基于缓存ehcache的应用,而jivejdon则就是基于ehcache缓存的,所以,加上我们前面将持久化和内存操作分离(实际就是计算和存储的分离),存储持久化用JMS异步实现,内存计算借助于Terracotta拓展到云计算,这样,我们的JiveJdon论坛伸缩性scalable就可以发展到云计算平台,充分利用当今分布式计算的强大优势,一步到位发展为一个巨大吞吐量的论坛系统,足够应付任何巨量访问。

有些人总以为JiveJdon不就是一个论坛系统吗?小学生都会编写,其实忽视了它背后的伸缩性scalable等其他包含相当高度前沿的OO设计。关键看你是否会举一反三,所以,孔子喜欢子贡,因为子贡会举一反五,可是我们有些人举一反二的能力都没有,这些都是没有学习能力的人,好废话不说,转帖Terracotta文章如下:

以下全部是转帖文章:
根据国外媒体报道,Terracotta公司近日推出了Terracotta 3.0,这是一款开源的Java内存缓冲数据库平台。内存缓冲(In-Memory Caching)技术最近发展迅速,它给基于网络交易的数据库应用程序提供了一种新的方案。Terracotta公司是一家在这一领域里快速成长的企业。

Terracotta 3.0让基于Java的程序的处理能力翻了三番,同时将数据库的负荷同前一版本相比下降了60%。Terracotta 3.0还为基于云和网格的应用程序提供了新的API和管理开发界面。新版本除了增强了功能性之外,还提供了管理Dashboard和一个开发者控制台,可以用它来辅助调试过程中的内存浏览和可视化运行。

这款软件适合那些需要能处理瞬间峰值访问量数据库的用户。

与一般数据库处理软件需要频繁存取数据库中的数据所不同的是,Terracotta通过一个客户端接口来捕获一个请求,并在内存中处理数据,并在内存中将数据传递给应用程序。这就避免了对数据库本身的频繁访问,从而降低了数据库许可费用和基础设施管理费,同时因为数据存放在内存中,使得对象-关系映射这一过程也被省略了。数据库通过关系格式来存储数据,所以我们需要将对象映射为关系。,Terracotta的CEO Pandey说,而Terracotta不这样做它通过对象的形式来存储数据。

产品主页:http://www.terracotta.org/

小资料: In-Memory Cache技术,顾名思义,内存缓冲技术让缓存数据存储在内存中。这种缓存方法速度很快,因为它不需要初始化而且只保存数据源的部分备份。不过服务器重启时,缓存数据会丢失。

[该贴被banq于2009-05-24 12:53修改过]

>>其实数据可靠性和完整性在不用缓存的时候也会遇到,因为缓存本来就是暂时存的,并不是持久化存储

同意这个看法。即使数据库立即commit,也可能在备份之前丢失数据。当然这个问题可以通过磁带存储archive log或者热备份来解决。我认为缓存引入的问题是:
1)产生更多没有持久化的数据,造成数据丢失数量的增大。
2)全局或者分布的缓存服务器,给整个系统增加了数据库和应用服务器之外的又一个独立部分,从而降低了整个系统的可靠性。

缓存确实可以提高性能,并且对应用屏蔽了数据库的非对象结构。但是我希望缓存能更加可控,允许控制缓存的使用,甚至跳过缓存。当然这要对应用透明。

就像Oracle和SQL Server,SQL Server简单,可以自动配置,大多数时候也工作的很好。但是Oracle给了DBA更多控制能力,可以更好的根据需要调节。对于有经验的DBA来讲,Oracle远比SQL Server好。至少不会面对一个黑盒子无能为力。