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

简介: [size=medium]/** *作者:张荣华 *日期:2008-08-22 **/ 在那遥远的过去,俺曾经写过一篇关于事务的文章,原文地址见: http://ahuaxuan.iteye.com/blog/95124.文章大意是这样的:在spring+hibernate的场景下,r
[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水平有限,文中不妥之处还望不吝指正,谢谢。
目录
相关文章
|
6月前
|
缓存 数据库
并发修改同一记录时需要加锁
并发修改同一记录时需要加锁
|
6月前
|
调度 数据库 数据库管理
数据库事务中调度串行化、冲突可串行化、前趋图(优先图)
数据库事务中调度串行化、冲突可串行化、前趋图(优先图)
346 0
|
SQL 存储 算法
MySQL的锁机制,包括锁分类、锁级别、锁粒度、锁冲突等方面
MySQL的锁机制,包括锁分类、锁级别、锁粒度、锁冲突等方面
146 0
|
6月前
|
SQL 关系型数据库 MySQL
MySQL 并发更新冗余索引字段导致的死锁
一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 案例分析2.1 业务场景业务开发同学要做业务逻辑迁移,由A表迁移到B表,B表承担所有的类型的业务,他们的业务逻辑是:...
196 0
MySQL 并发更新冗余索引字段导致的死锁
|
Java Spring
代码如何实现事务查询
代码如何实现
126 0
|
SQL 关系型数据库 MySQL
跨表查询经常有,何为跨表更新?
跨表查询经常有,何为跨表更新?
跨表查询经常有,何为跨表更新?
|
关系型数据库 数据库 程序员
|
关系型数据库 数据库 程序员