论坛首页 Java企业应用论坛

关键字:查询,事务,粒度

浏览 23117 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-09-02  
貌似他是这个意思:正确的逻辑应该是无论如何怎么并发,都只需要从连接池获取一个connection.只用一个connection更新你的cache,然后所有的并发调用都是从cache里获取数据。
但是你的代码实际上在并发的时候获取了多个connection.这就是不对的。不知道我解释清楚了没有
他的意思就是,你实际上逻辑没有考虑清楚,才会犯后面的连接被耗尽的错误。
反正我就是这么理解的,错了请拍砖
我确实是看过了你们的帖子,要不我也不会来吹水,这不是我的风格。
0 请登录后投票
   发表时间: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中获取此任务,使用此方法使任务重置。
0 请登录后投票
   发表时间: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线程取同样的数据,多次读取数据库,不可避免

 

:)
0 请登录后投票
   发表时间: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方法上加锁就可以了,不用这么麻烦

------------------------------------------------------

好好的一个阐述事务粒度的文章居然被偏到并发问题上去了,呵呵,希望大家看到这个回帖之后不要转移重点了。
0 请登录后投票
   发表时间:2008-09-02  
另外,楼主说自己是8个TOMCAT实例,并发很大,运行良好。。。。。

呵呵,其实,在RUP精化阶段,做性能模型分析时,你代码的这种问题肯定会被作为重要的性能问题被提出。

不过话题真是扯远了,我开始的意思只是觉得应该换一个更直观,没有明显问题的代码做演示会更好一点,不然问题代码会给很多新人留下印象,之后在开发时会直接使用。可能是表达能力不行,让楼主误会了,抱歉抱歉。
0 请登录后投票
   发表时间:2008-09-02  
wym0291 写道
另外,楼主说自己是8个TOMCAT实例,并发很大,运行良好。。。。。

呵呵,其实,在RUP精化阶段,做性能模型分析时,你代码的这种问题肯定会被作为重要的性能问题被提出。

不过话题真是扯远了,我开始的意思只是觉得应该换一个更直观,没有明显问题的代码做演示会更好一点,不然问题代码会给很多新人留下印象,之后在开发时会直接使用。可能是表达能力不行,让楼主误会了,抱歉抱歉。


那我在说明一下吧,事实上这个代码不是我写的,因为出了问题原因是我找到的,但是原因不是在你说的并发问题上,确实是在事务粒度上,后来缩小了事务的粒度,问题立刻解决了。

我刚才说了,使用你的并发代码,并不能解决这个问题,因为主要问题不在并发上。
0 请登录后投票
   发表时间:2008-09-02  
呵呵,确实是我偏离了你的论题,sorry
0 请登录后投票
   发表时间:2008-09-02  
wym0291 写道
呵呵,确实是我偏离了你的论题,sorry


不过我想了想,这样的讨论对这篇文章来说未必是坏事,至少在文章开头描述的场景在互连网应用中是非常常见的,讨论得全面一点对所有人来说都是一件好事,参与讨论得人对这样得场景更加了如指掌,而看贴得人对这样得场景要注意什么问题也心里有数了

谢谢大家参与讨论
0 请登录后投票
   发表时间:2008-09-02  
把方法拆分好了,将从缓存取数据的方法和从数据库取数据的方法分离
0 请登录后投票
   发表时间:2008-09-03  
ahuaxuan 写道
wym0291 写道
楼主,你没发现你这个getObject方法本身就是一个错误么?而你却把这个方法代码级的错误延伸到事务上去了,佩服佩服。。。。。

你从什么地方看出来这个getObject方法本身是错误的????
错在哪里,我也挺佩服你的,大家都没有看出来,就你看出来了.

wym0291 写道
首先,你这个方法的正确逻辑应该是第一次执行时,缓存中没有数据,然后从数据库中去查数据并放入缓存。之后的方法执行都会直接从缓存中获得数据。也就是说,应该只有一个connection对象会被使用一次。而你得出的结论确实有多个connection被使用。你不觉得这首先就是你方法的代码级别出现了错误,造成了这个后果么?

从你这段话里可以看出来你还不明白我在说什么,我很少用"建议"这个词,因为这让人有种据高凌下的感觉,但是我真的建议你再把文章看两遍



wym0291 写道

再看代码。因为没考虑到并发问题,你这个方法代码在多线程情况下,明显会造成多次重复的查询数据库,而这个查询数据库的动作原本只应该执行仅一次而已。

这里即使有产生并发,最多就是多查两遍数据库, 查完之后就不会再查了,显然这个是小问题,因为这并发的几次查询都是一样的数据.如果加同步,那么不管是查数据库还是缓存都有锁的开销,显然我的选择是在真正并发的时候拿掉锁,宁可多查两遍数据库



0291是对的,楼主仔细想想吧
0291分析的很对很到位
缓存的更新在没失效确实只有一次
锁还是比查数据库快的

但是我觉得楼主是在无视这个并发问题下讨论事务,就是说楼主有新的关注点--事务,我现在读第3页,先发个帖子,接着读
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics