锁定老帖子 主题:关键字:查询,事务,粒度
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-08-22
*作者:张荣华 *日期:2008-08-22 **/ 在那遥远的过去,俺曾经写过一篇关于事务的文章,原文地址见: http://ahuaxuan.iteye.com/blog/95124.文章大意是这样的:在spring+hibernate的场景下,readonly的事务会有特别的优化.因为readonly的事务在提交的时候不会flush 一级缓存中的几个队列(包括,更新队列,插入队列等).看了那篇文章的同学会以为:ok,只读的时候我只要readonly就行了. 不过那篇文章中我并没有考虑到所有的场景,所有再写一篇文章,算是对整个概念(查询操作是否需要事务)的一个完善. 还是老路子,我先描述一下我遇到的一个项目的问题: 1 代码逻辑,下面是一段伪代码 /** * 该方法上加事务,传播途径为required * @param params * @return */ public List<object> getObject(Map<String, String> params) { //先从memcached中取得数据 List<object> o1 = memcachedClient.getFromCache(params); if (o1 == null) { ol = ObjectDao.getfromDB(params); memcachedClient.putToCache(params, o1); } return o1; } 这段代码逻辑非常简单,先从memcached中取数据,取不到就从db取,然后再放到memcached中,无可挑剔(不过要注意我的注释,这个方法上加了required的事务) 就是这段代码,放到tomcat中,我的同事william测出来的结果是,单线程请求(tomcat的thread pool中在同一时间只有一个处理请求的线程被调用):ab -c 1 –n 1000 http://xxxx.xxx.xxx/xxx 结果是每秒中只能处理20个请求, 我的天啊, 20个,改多线程呢,: ab -c 100 –n 1000 http://xxxx.xxx.xxx/xxx, 一百个线程请求1000次,晕倒,还是每秒只能处理20多个请求. 估计有些人会觉得很奇怪,这么简单的逻辑怎么会这么慢,我但是也很奇怪,不过突然大脑中一个概念闪过”连接池”(难道这就是传说中的灵感),打开配置一看,果然,连接池配置的连接数只有20. “每秒钟处理20个请求,20个connection,加了事务” “每秒钟处理20个请求,20个connection,加了事务” “每秒钟处理20个请求,20个connection,加了事务” 多想了两遍之后(快分裂了),答案出来了: 在getObject方法上加上事务之后,所有的调用都会新建事务对象,然后放到当前线程中,而新建该事务对象的基础是connection,同时这个connection也会被保存在当前线程中,这样造成的结果是只有等到拥有connection的请求退出事务之后,connection才能重新回到线程池,换句话说,getObject方法是依赖于connection的,getObject能够被调用的次数取决于线程池中线程的数量 于是把线程池开到100,同样运行ab,结果果然好了很多,现在每秒能够处理的请求达到了120+,connection的数量变成原来的5倍,每秒处理的请求数也变成了原来的5倍. 看上去为这个get方法配置事务导致了该方法依赖于db connection是真正的原因, 从我们的逻辑上看该方法确实是不需要事务,但是由于我们的习惯,就顺利成章的给配置了一个,但是这个小小的配置确带来了巨大的影响. 不过有时候,我们以为找到了所有的问题,但是往往有更深层次的问题隐藏其中,比如说多次查询不加事务可能产生幻读的情况(不过如果你的应用对幻读的要求不高的话也没有什么问题). 那么也许我们可以这样结论,只要查询的操作在高性能需求的场景下千万不要加事务,即使是readonly的也不行,其他场景加上吧,不加可能会有些问题,比如说前面提到的幻读(这次我的结论好像底气不足,因为我还没有对它有过特别彻底的研究). 结论下来了,我们看看如何优化,其实在我举的这个场景中,是既可以保证事务,又能提高效率的,就是缩小事务的粒度,如果我在ObjectDao.getfromDB(params);加上事务的话,依赖于connection的只有ObjectDao.getfromDB(params);这个方法了,而这个方法只有o1 == null的时候才会被调用,绝大多数情况下它是不会被调用的.这样既一定程度上保证了事务又提高的程序的速度. 那么我们还可以下一个结论,就是某些场景下缩小我们事务的粒度能够很大程度提高程序的性能. 注明:由于ahuaxuan水平有限,文中不妥之处还望不吝指正,谢谢。 [/size] 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-08-24
"某些场景下缩小我们事务的粒度能够很大程度提高程序的性能."非常有同感,我经常对我的同事们说,尽快的结束事务,减少不必要的数据库的访问。
|
|
返回顶楼 | |
发表时间:2008-08-25
哪怎么实现呢 吧访问数据库的这一小段代码独立出来给他加事务么?
|
|
返回顶楼 | |
发表时间:2008-08-25
阁下真有心呀,以后要向阁下学习呀。
|
|
返回顶楼 | |
发表时间:2008-08-25
嗯,前段时间也遇到了这个问题。明明从cache中读数据,可由于事务的原因,还是要获得一个connection,导致性能下降的厉害。
|
|
返回顶楼 | |
发表时间:2008-08-25
那么用hibernate自身的二级缓存要小心了,其不能分隔那些是cache,那些是事务代码,这个问题很严重.
|
|
返回顶楼 | |
发表时间:2008-08-25
这篇文章可以给我们更深刻的提示:OpenSessionInView模式的最大的问题在于线程处理能力。因为实际上,OpenSessionInView要求connection在View层就拿过来,直到整个请求结束,才归还连接池。
|
|
返回顶楼 | |
发表时间:2008-08-25
啊
看到这个 我终于明白为什么之前做的一个项目里面,把cache 层单独提出来了 cache 层调用service层的 |
|
返回顶楼 | |
发表时间:2008-08-25
ahuaxuan 写道 /**
*作者:张荣华 *日期:2008-08-22 **/ 就是这段代码,放到tomcat中,我的同事william测出来的结果是,单线程请求(tomcat的thread pool中在同一时间只有一个处理请求的线程被调用):ab -c 1 –n 1000 http://xxxx.xxx.xxx/xxx 结果是每秒中只能处理20个请求, 我的天啊, 20个,改多线程呢,: ab -c 100 –n 1000 http://xxxx.xxx.xxx/xxx, 一百个线程请求1000次,晕倒,还是每秒只能处理20多个请求. 尽量细化事务粒度,深有同感,想问一下,这个测试是怎么做的,用什么工具? |
|
返回顶楼 | |
发表时间:2008-08-25
NicholasBugs 写道 ahuaxuan 写道 /**
*作者:张荣华 *日期:2008-08-22 **/ 就是这段代码,放到tomcat中,我的同事william测出来的结果是,单线程请求(tomcat的thread pool中在同一时间只有一个处理请求的线程被调用):ab -c 1 –n 1000 http://xxxx.xxx.xxx/xxx 结果是每秒中只能处理20个请求, 我的天啊, 20个,改多线程呢,: ab -c 100 –n 1000 http://xxxx.xxx.xxx/xxx, 一百个线程请求1000次,晕倒,还是每秒只能处理20多个请求. 尽量细化事务粒度,深有同感,想问一下,这个测试是怎么做的,用什么工具? 估计是tomcat的启动参数吧 |
|
返回顶楼 | |