论坛首页 Java企业应用论坛

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

浏览 23115 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-08-22  
[size=medium]/**

*作者:张荣华

*日期: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]
   发表时间:2008-08-24  
"某些场景下缩小我们事务的粒度能够很大程度提高程序的性能."非常有同感,我经常对我的同事们说,尽快的结束事务,减少不必要的数据库的访问。
0 请登录后投票
   发表时间:2008-08-25  
哪怎么实现呢   吧访问数据库的这一小段代码独立出来给他加事务么?
0 请登录后投票
   发表时间:2008-08-25  
阁下真有心呀,以后要向阁下学习呀。
0 请登录后投票
   发表时间:2008-08-25  
嗯,前段时间也遇到了这个问题。明明从cache中读数据,可由于事务的原因,还是要获得一个connection,导致性能下降的厉害。
0 请登录后投票
   发表时间:2008-08-25  
那么用hibernate自身的二级缓存要小心了,其不能分隔那些是cache,那些是事务代码,这个问题很严重.
0 请登录后投票
   发表时间:2008-08-25  
这篇文章可以给我们更深刻的提示:OpenSessionInView模式的最大的问题在于线程处理能力。因为实际上,OpenSessionInView要求connection在View层就拿过来,直到整个请求结束,才归还连接池。
0 请登录后投票
   发表时间:2008-08-25  

看到这个 我终于明白为什么之前做的一个项目里面,把cache 层单独提出来了
cache 层调用service层的
0 请登录后投票
   发表时间: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多个请求.


尽量细化事务粒度,深有同感,想问一下,这个测试是怎么做的,用什么工具?
0 请登录后投票
   发表时间: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的启动参数吧
0 请登录后投票
论坛首页 Java企业应用版

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