遇到一个多用户并发的问题,谁说查询操作不用做事务处理或者不用加锁?
查询操作不用做事务处理或者不用加锁,以前一直比较同意,今天遇到一个实际的问题,原来这种想法是错误的.
项目中遇到一个用户资金扣除出错的问题:
系统采用了quartz来做定时任务的处理,在某个时间点,自动对用户购买方案进行扣款,通过对日志的跟踪发现:当用户购买了多个方案时,有时用户可用资金会出错。
要实现的功能:
查询用户的可用资金,然后扣款。
假设用户w当前可用资金为1000元,应该执行两次扣款,第一次100,第二次50,用户w正确的余额应该是850。
出错原因:
扣款前要先查询用户当前的可用金额,然后再扣款.
通过日志发现,线程a查询完用户w的可用金额为1000元后,暂停了执行扣款的操作。
然后线程b开始执行,线程b查询用户w的可用金额为1000元,然后扣款50,更新DB,这时用户w余额是950。
然后,线程a在1000的基础上扣款100,更新DB,这时用户w的最终余额是900元。显而易见,用户w的最终余额是错误的。
结论:查询操作不用做事务处理或者说不用加锁是错误的。
其实这是一个丢失更新的典型的场景,但在实际中真实地遇到它,还是让人很兴奋啊.
欢迎大家批评指正。 1 楼 daquan198163 2007-12-25 乐观锁是用来解决这个问题的 2 楼 sorphi 2007-12-25 你这里的场景:查询操作只是一个完整事务的一部分
可别从一个极端到另外一个极端 3 楼 WorkingHard?! 2007-12-25 关键是你更新的策略问题,你在进行更新的时候逻辑是怎么写的~
如果是查询出来余额判断后执行更新操作,那么你这个逻辑的本身也是属于更新的一部分,方法类似
public void updateAccount(***){
double account = queryAccout();
XXDAO.update(XXX);
}
事务的设置可以用声明式事务对该方法进行设定 或者 自己手动控制事务 4 楼 抛出异常的爱 2007-12-25 脏读...锁机制存在的主要原因之一 5 楼 charles751 2007-12-25 WorkingHard?! 写道关键是你更新的策略问题,你在进行更新的时候逻辑是怎么写的~
如果是查询出来余额判断后执行更新操作,那么你这个逻辑的本身也是属于更新的一部分,方法类似
public void updateAccount(***){
double account = queryAccout();
XXDAO.update(XXX);
}
事务的设置可以用声明式事务对该方法进行设定 或者 自己手动控制事务
同意.
投入门贴的朋友们,你们都遇到过这种场景吗?表示怀疑 6 楼 ddandyy 2007-12-25 所谓的查询 是指整个事务里只有一个查询 7 楼 charles751 2007-12-25 ddandyy 写道所谓的查询 是指整个事务里只有一个查询
看来大家的看法比较一致啊.
我这个场景中,查询只是事务的一部分而已.
这个查询方法在dao层中,事务由service方法做处理
8 楼 ideafrog 2007-12-25 你的数据库是oracle吗?
不同的数据库有不同的锁机制。你这样说,根本就是在乱扯。虽然有那么点道理,但是与问题本质没有关系 9 楼 charles751 2007-12-25 ideafrog 写道你的数据库是oracle吗?
不同的数据库有不同的锁机制。你这样说,根本就是在乱扯。虽然有那么点道理,但是与问题本质没有关系
能解释下您说的本质吗 10 楼 movingboy 2007-12-25 最基本的事务处理问题啊~~~
“查询操作不用做事务处理或者不用加锁”是指不需要对查询出来的数据进行更新(包括删除),自然不需要进行事务管理了。楼主的情况很明显不同,当然需要事务管理了。建议楼主再读点数据库基础方面的书 11 楼 hocus 2007-12-25 查询和更新应该做到一个事务控制的方法里。
该方法会被多个线程调用,所以该方法需要同步控制。
12 楼 soci 2007-12-25 多线程共享资源啊 和数据库没啥关系 和俩线程访问同一个实例是一回事 13 楼 xiangzhouwang 2007-12-26 控制下加锁级别 14 楼 charles751 2007-12-26 第一贴就成新手贴,可悲啊.
看来我水平有限啊,还是到serverside去混吧