锁定老帖子 主题:关键字:查询,事务,粒度
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-09-02
貌似他是这个意思:正确的逻辑应该是无论如何怎么并发,都只需要从连接池获取一个connection.只用一个connection更新你的cache,然后所有的并发调用都是从cache里获取数据。
但是你的代码实际上在并发的时候获取了多个connection.这就是不对的。不知道我解释清楚了没有 他的意思就是,你实际上逻辑没有考虑清楚,才会犯后面的连接被耗尽的错误。 反正我就是这么理解的,错了请拍砖 我确实是看过了你们的帖子,要不我也不会来吹水,这不是我的风格。 |
|
返回顶楼 | |
发表时间:2008-09-02
呵呵,怪我多嘴,得罪之处还请谅解。至于你说的解决方法,其实在JDK5刚出来那时已经变成一种比较大众的方法了。
下面是不严谨的,简单的代码,只为了说明一下避免多次查询数据库的逻辑,没有考虑数据库更新后的缓存同步问题,这个问题的解决方法比较多,就不在代码里写了,免得班门弄斧。 ConcurrentHashMap<Map<String, String>, FutureTask<List<Object>>> map = new ConcurrentHashMap<Map<String, String>, FutureTask<List<Object>>>(); public List<Object> get(final Map<String, String> params) throws InterruptedException, ExecutionException { FutureTask<List<Object>> result = map.get(params); if(result == null){ FutureTask<List<Object>> task = new FutureTask<List<Object>>(new Callable<List<Object>>(){ @Override public List<Object> call() throws Exception { return ObjectDao.getfromDB(params); }}); result = map.putIfAbsent(params, task); if(result == null){ System.out.println("result = null"); result = task; } result.run(); } return result.get(); } 在数据库数据更新后,考虑到缓存数据要重新从数据库里获得,可以利用这个任务的重置方法,使此任务可以重新执行。例如runAndReset()方法,在数据更新时,可以从任务MAP中获取此任务,使用此方法使任务重置。 |
|
返回顶楼 | |
发表时间:2008-09-02
public List<object> getObject(Map<String, String> params) {
//先从memcached中取得数据 //假设A线程走到这里 List<object> o1 = memcachedClient.getFromCache(params); //假设B线程走到这里 if (o1 == null) { // 假设C线程走到这里 ol = ObjectDao.getfromDB(params); // 假设D线程走到这里 memcachedClient.putToCache(params, o1); } return o1; } 并发的情况下,如果实际情况A,B,C,D线程取同样的数据,多次读取数据库,不可避免 :) |
|
返回顶楼 | |
发表时间:2008-09-02
terranhao 写道 貌似他是这个意思:正确的逻辑应该是无论如何怎么并发,都只需要从连接池获取一个connection.只用一个connection更新你的cache,然后所有的并发调用都是从cache里获取数据。
但是你的代码实际上在并发的时候获取了多个connection.这就是不对的。不知道我解释清楚了没有 他的意思就是,你实际上逻辑没有考虑清楚,才会犯后面的连接被耗尽的错误。 反正我就是这么理解的,错了请拍砖 我确实是看过了你们的帖子,要不我也不会来吹水,这不是我的风格。 这就是我们理解不一样的地方了,我不认为代码有错(当然并发的问题我没有写在代码里),我认为是事务的粒度不对才导致connection耗尽的,逻辑上唯一不严谨的就是并发问题没有阐述出来,不过我已经在前面的回帖中追加了这部分阐述 terranhao 写道 但是你的代码实际上在并发的时候获取了多个connection.这就是不对的。 这个并不是我们的java代码控制的,这个是写在配置文件中的,其实这就是事务粒度的问题,跟代码没有什么关系,我的代码在并发的时候并没有获取多个connection,你想想啊,我getFromDb又不是每次都查,我的代码怎么会每次都获取connection呢,我觉得你还是没有真正的理解问题的原因,这不是代码造成的 ---------------------------------------------------- wym0291 写道 呵呵,怪我多嘴,得罪之处还请谅解。至于你说的解决方法,其实在JDK5刚出来那时已经变成一种比较大众的方法了。
下面是不严谨的,简单的代码,只为了说明一下避免多次查询数据库的逻辑,没有考虑数据库更新后的缓存同步问题,这个问题的解决方法比较多,就不在代码里写了,免得班门弄斧。 ConcurrentHashMap<Map<String, String>, FutureTask<List<Object>>> map = new ConcurrentHashMap<Map<String, String>, FutureTask<List<Object>>>(); public List<Object> get(final Map<String, String> params) throws InterruptedException, ExecutionException { FutureTask<List<Object>> result = map.get(params); if(result == null){ FutureTask<List<Object>> task = new FutureTask<List<Object>>(new Callable<List<Object>>(){ @Override public List<Object> call() throws Exception { return ObjectDao.getfromDB(params); }}); result = map.putIfAbsent(params, task); if(result == null){ System.out.println("result = null"); result = task; } result.run(); } return result.get(); } 在数据库数据更新后,考虑到缓存数据要重新从数据库里获得,可以利用这个任务的重置方法,使此任务可以重新执行。例 如runAndReset()方法,在数据更新时,可以从任务MAP中获取此任务,使用此方法使任务重置。 好,我喜欢这样的讨论,我们假设使用你的代码,然后其他都不用改,getObject方法上继续加使用required事务 如果我的getObject方法里面采用了你的代码connection还是会耗尽,因为我的getObject方法是放在事务里面的啊,这一点你考虑了吗? 你给出的只是同步的方案而已,不能解决connection耗尽的问题,我前面已经说过了,同步的话只要在getObject方法上加锁就可以了,不用这么麻烦 ------------------------------------------------------ 好好的一个阐述事务粒度的文章居然被偏到并发问题上去了,呵呵,希望大家看到这个回帖之后不要转移重点了。 |
|
返回顶楼 | |
发表时间:2008-09-02
另外,楼主说自己是8个TOMCAT实例,并发很大,运行良好。。。。。
呵呵,其实,在RUP精化阶段,做性能模型分析时,你代码的这种问题肯定会被作为重要的性能问题被提出。 不过话题真是扯远了,我开始的意思只是觉得应该换一个更直观,没有明显问题的代码做演示会更好一点,不然问题代码会给很多新人留下印象,之后在开发时会直接使用。可能是表达能力不行,让楼主误会了,抱歉抱歉。 |
|
返回顶楼 | |
发表时间:2008-09-02
wym0291 写道 另外,楼主说自己是8个TOMCAT实例,并发很大,运行良好。。。。。
呵呵,其实,在RUP精化阶段,做性能模型分析时,你代码的这种问题肯定会被作为重要的性能问题被提出。 不过话题真是扯远了,我开始的意思只是觉得应该换一个更直观,没有明显问题的代码做演示会更好一点,不然问题代码会给很多新人留下印象,之后在开发时会直接使用。可能是表达能力不行,让楼主误会了,抱歉抱歉。 那我在说明一下吧,事实上这个代码不是我写的,因为出了问题原因是我找到的,但是原因不是在你说的并发问题上,确实是在事务粒度上,后来缩小了事务的粒度,问题立刻解决了。 我刚才说了,使用你的并发代码,并不能解决这个问题,因为主要问题不在并发上。 |
|
返回顶楼 | |
发表时间:2008-09-02
呵呵,确实是我偏离了你的论题,sorry
|
|
返回顶楼 | |
发表时间:2008-09-02
wym0291 写道 呵呵,确实是我偏离了你的论题,sorry
不过我想了想,这样的讨论对这篇文章来说未必是坏事,至少在文章开头描述的场景在互连网应用中是非常常见的,讨论得全面一点对所有人来说都是一件好事,参与讨论得人对这样得场景更加了如指掌,而看贴得人对这样得场景要注意什么问题也心里有数了 谢谢大家参与讨论 |
|
返回顶楼 | |
发表时间:2008-09-02
把方法拆分好了,将从缓存取数据的方法和从数据库取数据的方法分离
|
|
返回顶楼 | |
发表时间:2008-09-03
ahuaxuan 写道 wym0291 写道 楼主,你没发现你这个getObject方法本身就是一个错误么?而你却把这个方法代码级的错误延伸到事务上去了,佩服佩服。。。。。
你从什么地方看出来这个getObject方法本身是错误的???? 错在哪里,我也挺佩服你的,大家都没有看出来,就你看出来了. wym0291 写道 首先,你这个方法的正确逻辑应该是第一次执行时,缓存中没有数据,然后从数据库中去查数据并放入缓存。之后的方法执行都会直接从缓存中获得数据。也就是说,应该只有一个connection对象会被使用一次。而你得出的结论确实有多个connection被使用。你不觉得这首先就是你方法的代码级别出现了错误,造成了这个后果么?
从你这段话里可以看出来你还不明白我在说什么,我很少用"建议"这个词,因为这让人有种据高凌下的感觉,但是我真的建议你再把文章看两遍 wym0291 写道 再看代码。因为没考虑到并发问题,你这个方法代码在多线程情况下,明显会造成多次重复的查询数据库,而这个查询数据库的动作原本只应该执行仅一次而已。 这里即使有产生并发,最多就是多查两遍数据库, 查完之后就不会再查了,显然这个是小问题,因为这并发的几次查询都是一样的数据.如果加同步,那么不管是查数据库还是缓存都有锁的开销,显然我的选择是在真正并发的时候拿掉锁,宁可多查两遍数据库 0291是对的,楼主仔细想想吧 0291分析的很对很到位 缓存的更新在没失效确实只有一次 锁还是比查数据库快的 但是我觉得楼主是在无视这个并发问题下讨论事务,就是说楼主有新的关注点--事务,我现在读第3页,先发个帖子,接着读 |
|
返回顶楼 | |