• WBB ThinkPhp高并发提交如何保障数据正常


    高并发导致的数据错误

    通常出现于多连接,假设当前用户余额为100。

    A逻辑从数据库取值得到100,扣取50,得到50,进行数据库保存。最终数据库=50。

    在同一瞬间内,A逻辑未保存数据库前,B逻辑从数据库取得余额100,扣去30,得到70,进行数据库保存。最终数据库=70。

    这样问题就发生了。 本来应该是100-50-30=20;  现在变成了100-50=50,而后被替换成100-30=70。

    实测这样的情况是可以发生的。

    public function testMoney(){

    for($i=0;$i<50;$i++){

    $mMoney = new MoneyModel();

    $result = $mMoney->find(['uid'=>1]);

    $result['money'] = $result['money'] - 0.1;

    $save = $mMoney->save($result);

    }

    for($i=0;$i<50;$i++){

    $mMoney = new MoneyModel();

    $result = $mMoney->find(['uid'=>1]);

    $result['money'] = $result['money'] + 0.1;

    $save = $mMoney->save($result);

    }

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    publicfunctiontestMoney(){

    for($i=0;$i<50;$i++){

    $mMoney=newMoneyModel();

    $result=$mMoney->find(['uid'=>1]);

    $result['money']=$result['money']-0.1;

    $save=$mMoney->save($result);

    }

    for($i=0;$i<50;$i++){

    $mMoney=newMoneyModel();

    $result=$mMoney->find(['uid'=>1]);

    $result['money']=$result['money']+0.1;

    $save=$mMoney->save($result);

    }

    }

    这样的代码,频繁刷几次,数据库的金额发生了错误。

    解决方案

    1、Sql语句内做加减法

    Thinkphp有现成的函数可以用:setInc或setDec。仅对数值改变有效。如果是复杂的数据变化不可用。

    本质是产生【字段 = 字段 +/- 数值】这样的语句嵌套到update里,可以解决数据入库的问题,即:

    public function testMoney(){

    for($i=0;$i<50;$i++){

    $mMoney = new MoneyModel();

    $mMoney->where(['uid'=>1])->setDec('money',0.1);

    }

    for($i=0;$i<50;$i++){

    $mMoney = new MoneyModel();

    $mMoney->where(['uid'=>1])->setInc('money',0.1);

    }

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    publicfunctiontestMoney(){

    for($i=0;$i<50;$i++){

    $mMoney=newMoneyModel();

    $mMoney->where(['uid'=>1])->setDec('money',0.1);

    }

    for($i=0;$i<50;$i++){

    $mMoney=newMoneyModel();

    $mMoney->where(['uid'=>1])->setInc('money',0.1);

    }

    }

    这样的逻辑,无论你怎么循环,除非数据库崩溃或Mysql连接本身出现问题,否则值最后不会有变化。

    值的注意的是:这种方法只能保证数据的写入不出现问题,数据的读取依然无法避免高并发受到的影响,即有可能出现用户余额100,消费50元后,高并发期间瞬间再做查询依然是100元余额的情况。这种情况恐怕只能使用本地缓存来解决了。

    2、乐观锁

    乐观锁的原理是在数据行级别上做一个版本记录,并在每次插入时对比版本字段是否有+1,没有则说明重复操作了,原理很巧妙,其实就是去掉了select出来之后逻辑造成的时间差,避免了数据的重复调出并做出多次修改。而且比起悲观锁,宽松很多,个人认为适用于方法1无法使用的场景。

    注意在使用乐观锁时不要使用setInc和setDec了,否则setInc和setDec内部的update的动作在出现lock_version重复时还是会被打回,回退逻辑没有做好的情况下容易出错。

  • 相关阅读:
    关于 a 标签 jquery的trigger("click"),无法触发问题。
    浏览器上传文件,存到oracle数据库示例。
    关于java的Long 类型到js丢失精度的问题
    java 自定义注解,并使用示例
    关于重置功能(type="reset")的相关问题
    校验键盘上中英文状态下所有的特殊字符(排除下划线所在的按键)
    VS Code 设置取消打开文件目录的自动定位跟踪功能。
    $.extend(x,y); 函数用法介绍。
    用jquery的.val() 给具有style="display:none;" 属性的标签写值的问题。
    10.我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。 请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
  • 原文地址:https://www.cnblogs.com/behindman/p/16170061.html
Copyright © 2020-2023  润新知