数据库锁的问题 insert where 的问题

问题描述

插入并发控制工作中碰到的问题工作环境中ge_money_details是会员的金钱表判断其余额时使用selectsum(money)fromge_money_details获取增加金额时#{money}为正数减少金额时#{money}为负数用以下SQL进行并发插入出现金额被减少为负数的情况<selectid="InsertMoneyDetailsByAccumulate"resultType="java.lang.Long"parameterType="com.smart.gpo.model.po.GeMoneyDetails">INSERTINTOge_money_details(account_id,operate_type,money,create_user,create_time,update_user,update_time)select#{accountId},#{operateType},#{money},#{createUser},#{createTime},#{updateUser},#{updateTime}where(selectsum(money)fromge_money_detailswhereaccount_id=#{accountId})&gt;=abs(#{money})returningmoney_details_id</select>

使用的是mybetis进行入库数据库为postgresql判断结果是以下代码List<Long>resultCount=this.getMybatis().getListByObject("InsertMoneyDetailsByAccumulate",geMoneyDetails);if(resultCount==null||resultCount.isEmpty())thrownewDaoException("余额不足!");

自测时插入成功resultCount的结果是money_details_id的值插入失败余额不足时候结果是resultCount.lenght=0正确的抛出的异常就是并发时出现了一次错误但没有复现出来后来由于是生产环境就先把方法加了一个锁先解决问题但这段代码哪里有错误还是很迷惑

解决方案

解决方案二:
看不懂,什么被减少为负数?
解决方案三:
引用1楼zhangsf1982的回复:

看不懂,什么被减少为负数?

就是公司的金钱流水表会员的金钱流动会在金钱流水添加一条记录如:会员存款100元记录中的money字段就为100会员购买商品消耗10元
解决方案四:
引用1楼zhangsf1982的回复:

看不懂,什么被减少为负数?

上面那个回复手抖了还没写完就发出去了不知道为啥还不能编辑的就是公司的金钱流水表会员的金钱流动会在金钱流水添加一条记录如:会员存款100元记录中的money字段就为100会员购买商品消耗10元记录中的money字段就为-10判断会员余额时就sum(money)获得9090就是会员的余额我在insert的时候判断了下如果这次插入的数据是负数就判断会员的余额必须比本次插入的负数的绝对值大(就是余额要大于本次消费的金额)但出现了余额小于商品金额的情况下成功购买了商品的情况简单讲就是一个10元的商品会员只有11元的余额会员购买该商品时不知道怎么回事并发了2个请求结果两个请求判断会员的金额都是大于10然后会员购买了2个10元的商品余额变为-9
解决方案五:
不知道是不是由于2个SQL并发且在事务中并未提交延迟提交后产生的问题?事物管理器用的org.springframework.orm.hibernate3.HibernateTransactionManager如果是有什么解决办法不?(纯SQL修改的方式)有没有高人来解答下疑惑....
解决方案六:
这个并发问题,我感觉啊,你在方法名加上synchronized,防止出现负数问题
解决方案七:
引用5楼tt2x77的回复:

这个并发问题,我感觉啊,你在方法名加上synchronized,防止出现负数问题

上面有说明的后来由于是生产环境就先把方法加了一个锁先解决问题主要是没搞明白为啥insertwhere没有效果加synchronized会非常影响性能锁粒度太大了我用了个ConcurrentHashMap作为锁对单个会员进行锁操作booleanflag=false;//用会员ID进行加锁StringmoneyLockkey=geMoneyDetails.getAccountId().toString();synchronized(LockUtil.MoneyLock){flag=LockUtil.MoneyLock.get(moneyLockkey,true);if(flag){LockUtil.MoneyLock.put(moneyLockkey,false);}}if(flag){try{//是扣除金额if(geMoneyDetails.getMoney().compareTo(BigDecimal.ZERO)==-1&&//因为回滚操作会导致负数回避掉回滚代码!geMoneyDetails.getOperateType().equals(OperateType.CancelBalance.getType())&&!geMoneyDetails.getOperateType().equals(OperateType.ReconstructionBet.getType())){//InsertMoneyDetailsByAccumulate就是insertwhere的那段代码List<Long>resultCount=this.getMybatis().getListByObject("InsertMoneyDetailsByAccumulate",geMoneyDetails);if(resultCount==null||resultCount.isEmpty())thrownewDaoException("余额不足!");}elsethis.getHibernate().save(geMoneyDetails);}finally{//释放锁信息LockUtil.MoneyLock.put(moneyLockkey,true);}}else{thrownewDaoException("您当前有金钱操作未完成请稍后尝试!");}

解决方案八:
引用6楼qqArz的回复:

Quote: 引用5楼tt2x77的回复:
这个并发问题,我感觉啊,你在方法名加上synchronized,防止出现负数问题

上面有说明的后来由于是生产环境就先把方法加了一个锁先解决问题主要是没搞明白为啥insertwhere没有效果加synchronized会非常影响性能锁粒度太大了我用了个ConcurrentHashMap作为锁对单个会员进行锁操作booleanflag=false;//用会员ID进行加锁StringmoneyLockkey=geMoneyDetails.getAccountId().toString();synchronized(LockUtil.MoneyLock){flag=LockUtil.MoneyLock.get(moneyLockkey,true);if(flag){LockUtil.MoneyLock.put(moneyLockkey,false);}}if(flag){try{//是扣除金额if(geMoneyDetails.getMoney().compareTo(BigDecimal.ZERO)==-1&&//因为回滚操作会导致负数回避掉回滚代码!geMoneyDetails.getOperateType().equals(OperateType.CancelBalance.getType())&&!geMoneyDetails.getOperateType().equals(OperateType.ReconstructionBet.getType())){//InsertMoneyDetailsByAccumulate就是insertwhere的那段代码List<Long>resultCount=this.getMybatis().getListByObject("InsertMoneyDetailsByAccumulate",geMoneyDetails);if(resultCount==null||resultCount.isEmpty())thrownewDaoException("余额不足!");}elsethis.getHibernate().save(geMoneyDetails);}finally{//释放锁信息LockUtil.MoneyLock.put(moneyLockkey,true);}}else{thrownewDaoException("您当前有金钱操作未完成请稍后尝试!");}

比如你余额50,在19行,你执行一次取款50,这条数据插入了,可是数据库没有提交,你下次执行余额还是显示50你又插入了,我觉得你的insertwhere没问题
解决方案九:
synchronized可以锁具体对象,比如synchronized(geMoneyDetails.getMoney()),这样不会太影响性能
解决方案十:
1、有负数是正常的,电信运营商都能欠费,太严格的校验会增加负担2、应该实时计算当前最最新余额,而不是每次都sum
解决方案十一:
建议加一个UserBalance表来记录用户的余额消费时按UserId判断余额读取数据是加排他锁select×××forupdatewithRS

时间: 2025-01-27 03:05:49

数据库锁的问题 insert where 的问题的相关文章

介绍了Oracle数据库锁的种类及研究

本文通过对Oracle数据库锁机制的研究,首先介绍了Oracle数据库锁的种类,并描述了实际应用中遇到的与锁相关的异常情况,特别对经常遇到的由于等待锁而使事务被挂起的问题进行了定位及解决,并对死锁这一比较严重的现象,提出了相应的解决方法和具体的分析过程. 数据库是一个多用户使用的共享资源.当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性. 加锁是实现数据库并发控制的一个非常重要的技术.当事务在对某

数据库锁的基本原理

前言:为了保证数据的完整性和一致性,数据库系统采用锁来实现事务的隔离性.各种大型数据库采用的锁基本理论是一致的,但在具体实现上各有差别.从并发事务锁定的关系上看,可以分为共享锁定和独占锁定.从锁定的对象不同,一般可以分为表锁定和行锁定. 1.锁的分类 锁分为悲观锁和乐观锁: ● 悲观锁:悲观的思想,认为并发问题总会出现,每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁.悲观锁的性能低,其基本种类有: ● 乐观锁:乐观的思想,认为并发问题

数据库锁和数据库隔离级别

最近突然发现忘了数据库锁和数据库隔离级别,时常弄混它们之间的关系.为此特此写下此博客,以方便自己复习,同时也可以帮助博友. 数据库锁 数据库锁就是事务T在对某个数据对象(例如表.记录等)操作之前,先向系统发出请求,对其加锁.加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象. 数据库锁是实现并发控制的重要技术,但是"锁"会带来系统额外的开销.所以需要注意选择封锁粒度时必须同时考虑开销和并发度两个因素,进行权衡,以求得最优的效果. 锁的类型主要

oracle 数据库锁类型 TM锁和TX锁

问题描述 oracle 数据库锁类型 TM锁和TX锁 oracle 9i 10g 编程艺术书中对oracle 的锁类型有三种分类:DML锁,DDL 锁 以及 内部锁和闩 针对DML 锁并没有详细的介绍,只说明有TM(表)锁 和TX(行)锁 网上搜索了很多资料,在资料中有具体的分类 问题是:表头说这些锁都是TM锁... 但是我怎么感觉2,3 都属于TX锁呢?2,3 到底是TM 锁还是 TX 锁呢? 解决方案 TX锁和TM锁不是互斥的,DML 锁 先由TM锁获取表级锁,然后TX 在对具体行修改. 这

轻松掌握MySQL数据库锁机制的相关原理

在MySQL中对于使用表级锁定的存储引擎,表锁定时不会死锁的.这通过总是在一个查询开始时立即请求所有必要的锁定并且总是以同样的顺序锁定表来管理. 在一个update和insert操作频繁的表中,少量数据测试的时候运行良好,在实际运营中,因数据量比较大(21万条记录),会出现死锁现象,用show processlist查看,可以看到一个update语句状态是Locked,一个delete语句状态是Sending data.查看了一下参考手册,把锁定相关的资料整理下来,以便自己记录和追踪该问题的解决

4.数据库锁及表分区

1.数据库权限授权的语句:     grant select,update,insert,delete on test(表名) to xiaoshan(用户名);2.被授权用户访问表:注意,一定要加上授权用户的用户名     select * from scott.test;     update test set ename='xiaoshan'     where empno=9000;3.行级锁:    ①行被排他锁定:    ②在某行的锁被释放之前,其他用户不能修改此行:    ③使用c

Informix数据库锁技术

INFORMIX使用锁技术解决在多用户访问数据库情况下,对同一对象访问的并发控制问题.INFORMIX 支持复杂的.可伸缩性的锁技术. 锁的类型 INFORMIX有三种不同类型的锁.它们在不同的情况下使用. 1. SHARED锁 SHARED锁只保留对象的可读性.当锁存在时,对象不能改变.多个程序可对同个对象加SHARED锁. 2. EXCLUSIVE锁 只能使单个程序使用.在程序要改变对象时使用.当其他锁存在时,EXCLUSIVE锁不能使用.当使用了E XCLUSIVE 锁后,其他锁不能用于同

MySQL数据库锁机制的相关原理

在一个update和insert操作频繁的表中,少量数据测试的时候运行良好,在实际运营中,因数据量比 较大(21万条记录),会出现死锁现象,用show processlist查看,可以看到一个update语句状态是 Locked,一个delete语句状态是Sending data.查看了一下参考手册,把锁定相关的资料整理下来,以便 自己记录和追踪该问题的解决情况: MySQL 5.1支持对MyISAM和MEMORY表进行表级锁定,对BDB表进行页级锁定,对InnoDB 表进行行级锁定 .在许多情

Access数据库多条数据Insert

当下Access数据库可能要被遗忘了!比较这个轻量级数据库被精简了许多功能,个人感觉用起来还是挺麻烦的!当前Access在企业项目中就是一块鸡肋.数据量小使用SQL Server是浪费,既然选择了Access就别抱怨了!如果用好了还是一块宝!废话不多说了,今天就来探讨下如何向Access中插入多条数据把!       由于在此之前我没有用过Access数据库的,当需要想数据库中插入多条数据时,我们不妨先按照sql server的做法:"insert into tablename(column1,