遇到一个多用户并发的有关问题,哪位高手说查询操作不用做事务处理或者不用加锁
遇到一个多用户并发的问题,谁说查询操作不用做事务处理或者不用加锁?
查询操作不用做事务处理或者不用加锁,以前一直比较同意,今天遇到一个实际的问题,原来这种想法是错误的.
项目中遇到一个用户资金扣除出错的问题:
系统采用了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的最终余额是错误的。
结论:查询操作不用做事务处理或者说不用加锁是错误的。
其实这是一个丢失更新的典型的场景,但在实际中真实地遇到它,还是让人很兴奋啊.
欢迎大家批评指正。
同意.
投入门贴的朋友们,你们都遇到过这种场景吗?表示怀疑
看来大家的看法比较一致啊.
我这个场景中,查询只是事务的一部分而已.
这个查询方法在dao层中,事务由service方法做处理
能解释下您说的本质吗
查询操作不用做事务处理或者不用加锁,以前一直比较同意,今天遇到一个实际的问题,原来这种想法是错误的.
项目中遇到一个用户资金扣除出错的问题:
系统采用了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);
}
事务的设置可以用声明式事务对该方法进行设定 或者 自己手动控制事务
如果是查询出来余额判断后执行更新操作,那么你这个逻辑的本身也是属于更新的一部分,方法类似
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);
}
事务的设置可以用声明式事务对该方法进行设定 或者 自己手动控制事务
如果是查询出来余额判断后执行更新操作,那么你这个逻辑的本身也是属于更新的一部分,方法类似
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去混吧
看来我水平有限啊,还是到serverside去混吧