数据库update时切忌这样干

前言:数据库update时切忌这样干,这样干是什么?请随我来看看。

早些时候,客户发来这样一段文字“XX的,XXXXXXXX有个10万没到账,帮加上去。”我马上动手查看数据库,寻找原因,随后回复了这样一句话“开玩笑吧”。虽然之前发生过类似这样的问题,但都是小额资金,就手动在数据库里加了,遇到这么大的资金还是头一次,于是我加紧翻看数据库记录的节奏。



我发现用户的入金记录的确已经是审核通过状态,而用户的可用资金却没有加上,这已经让人意外了。

接着我查看代码,并没有发现逻辑上的问题,这又让我感到迷茫。

再翻看日志,发现没有任何异常,好吧,感觉被设了局一样。

回头想想,这种情况并不多见,程序在多数情况下并没有这种情况,一切正常。这糟了,没有错误日志就是最大的错误。

静下心来,我想有可能是事务出现的问题,因为对于资金表,同一个用户的资金在同一个时间点是可能同时被更新的,我猜想,是入金记录先插入了出入金记录表,等到更新资金表的时候被锁住,事务超时后插入的记录没有回滚,于是我通过这样的方式来证明一下。

START TRANSACTION;
INSERT INTO record VALUES(1,123);
UPDATE money SET money = money + 10;
COMMIT;

START TRANSACTION;
UPDATE money SET money = money + 100;
COMMIT;

通过手动控制事务锁,发现不是我想的那样。然后我又试了试下面这种方式:

START TRANSACTION;
UPDATE money SET money = money + 10;
INSERT INTO record VALUES(1,123);
COMMIT;

START TRANSACTION;
UPDATE money SET money = money + 100;
COMMIT;

发现两者的效果是一样的,事务都会回滚。

这个时候,我的思路被禁锢了,我和同事讨论起来,看看他有什么想法,经过一番思想斗争后,果然同事还是给出了他的想法,我深感赞同。

MoneyUser moneyUser = moneyUserMapper.selectByPrimaryKey(members.getUid());
   // 冻结资金-出金金额
   moneyUser.setFrozenl(moneyUser.getFrozen().subtract(moneyTransfer.getount()));
   // 可用资金+出金金额
   moneyUser.setTotaaymoney(moneyUser.getTotalpayey().add(moneyTransfer.getount()));
this.moneyUserMapper.updateByPrimaryKey(moneyUser);

这样的代码在出现并发的时候,获取moneyUser对象的时候,其中的冻结资金和可用资金加入都是0,那么两个并发在update的时候,两个都执行了,但是起初的冻结资金和可用资金相同,就会导致有一笔资金没有加上。

调试的时候更容易发现这个问题。OH,my 噶,真是个大问题,还出在资金的问题时,我真庆幸这个问题发生的频率不够高。不过我到底是应该庆幸还是不庆幸呢,也许次数发生的多起来的时候,我就更容易来反思来找出问题的症结所在。

那么问题怎么解决呢?

UPDATE moneser
  SET
  frozapital = frozpital - #{amount,jdbcType=DECIMAL},
  totaloney = totaloney + #{amount,jdbcType=DECIMAL}
  WHERE uid = #{uid,jdbcType=INTEGER}

通过在sql语句中进行字段的更新操作,而不是在Java类中,因为mysql本身是会有这方面的处理方式的。

这个问题,之前领导有建议我在update数据的时候尽量在sql语句中加入,而不是类字段中做处理。

总结:这个问题,如果你不经意的话,很容易发生这个问题,我当初就没有这样的意识,真是可悲可叹!

时间: 2024-12-20 05:54:00

数据库update时切忌这样干的相关文章

mysql数据库update时只更新部分数据方法

需求:更新url中最一个字符的'-1'改为'-5',前面的内容保持不变 url列的内容如下:http://h5game.ecs.cedarmg.com/a/captal/dispther.do?deviceid=b-01-1 SELECT * from qrmsg WHERE url LIKE '%1' http://h5game.ecs.cedarmg.com/c/captal/dispther.do?deviceid=b-01-1 需要把最后这个-1改成-5 操作方式如下: UPDATE q

数据库设计时不得不违背三范式的情景

1.在进销存系统中,订单信息中关联到好多其他的基本信息,比如:客户,付款方式,货运方式等,这些信息是有专门表进行维护的,在下订单时也是用下拉框选择的,但在保存订单信息时,不能只记录所谓的外键ID,而是应该同时记录名称等其他的信息. 这是因为订单不能因为没有了客户ID或是付款方式ID而不知道客户与付款方式了.对于订单这种客观存在的事物,是具有一定的历史性质的,因此在设计时应该与其他的关联信息可以“断开”,这也就是保证了订单的独立性. 摘自:http://www.cnblogs.com/tongtk

还原数据库备份文件时,关于“System.Data.SqlClient.SqlError:媒体集有2个媒体簇,但只提供了1个。必须提供所有成员”的处理方式

好久没写博客了,最近在做毕设的权限管理模块,今天在还原数据库文件时,遇到了“System.Data.SqlClient.SqlError:媒体集有2个媒体簇,但只提供了1个.必须提供所有成员”这个错误,百度了一下,找到了原因和解决方式. 原因分析:1.备份的时候,旧路径没有删除,我添加了一个新路径,就无意中设置成了两个备份路径,SQL就会根据两个备份路径把备份文件分成两个.2.还原的时候源数据必须把两个备份文件都加载上.3.还原的新路径要手工修改成实际SQL的data文件夹路径.4.如果有同名数

UPDATE 时主键冲突引发的思考【转】

假设有一个表,结构如下: root@localhost : yayun 22:59:43> create table t1 ( -> id int unsigned not null auto_increment, -> id2 int unsigned not null default '0', -> primary key (id) -> )engine=myisam; Query OK, 0 rows affected (0.00 sec) root@localhost

恢复数据库备份时提示日志错误

可以打开了恢复的时候增加了这个参数 WITHOUT ROLLING FORWARD 恢复数据库备份时提示日志错误,码迷,mamicode.com

关于各种数据库 Insert时同时取到Id的操作

对数据库的操作,在Insert的同时获取到Id,保证原子性操作.而不是,先取Id,然后再插入到数据库等操作. Oracle: 使用Oracle自带的 Returning into 语句,具体代码示例如下: 1 sql = @"INSERT INTO KEYWORD(KEYWORD_ID,SITE_ID,KEYWORD_NAME,STATUS,SOURCE,CREATE_TIME,LAST_CHANGED) 2 VALUES 3 ( 4 KEYWORD_SEQ.nextVal, 5 :SITE_I

数据库查询时,查询数字或者英文可以,查询中文时出错

数据库查询时,查询数字或者英文可以,查询中文时出错:提示????????????:select * from szdbdb_userdata where        truename='????' Illegal mix of collations (gb2312_chinese_ci,IMPLICIT) and (latin1_swedish_ci,COERCIBLE) for operation '='12670 解决办法:一般网上会说是编码问题,在配置文件my.ini修改数据库编码.当把

数据库update死锁

比较常见的死锁场景,并发批量update时的一个场景: update cross_marketing set gmtModified = NOW(), pageview = pageview+ #extpageview# WHERE marketingId=#marketingId# 第一次调用时,marketingId传入值顺序: 1,3,5,12 第二次调用时,marketingId传入值顺序:1,2,5,3 每次update时,会锁行. 那么第一次调用时,顺序锁行,当更新完3,准备跟新5的

redhat+11g+rac 安装数据库软件时只有一个节点可选

在安装数据库软件时,只能检测到一个节点 解决办法:重启rac1节点