博客
关于我
PHP 并发扣款,保证数据一致性(悲观锁和乐观锁)
阅读量:793 次
发布时间:2023-02-28

本文共 1700 字,大约阅读时间需要 5 分钟。

PHP 并发扣款中的数据一致性问题

在用户购买商品的逻辑中,需要对用户钱包的余额进行查询和扣款。然而,在并发处理的情况下,可能会出现数据不一致的情况。这种情况可能会导致用户购买多个商品时,系统显示余额不一致,影响用户体验。

扣款场景

扣款过程分为以下几个步骤:

  • 查询余额:从数据库中查询用户的钱包余额。

    SELECT balance FROM user_wallet WHERE uid = $uid;

    例如,查询结果为余额100元。

  • 业务逻辑

  • 查询商品价格,例如70元。
  • 比较商品价格和用户余额,判断是否足够扣款。
  • if (goodsPrice <= userBalance) {    $newUserBalance = userBalance - goodsPrice;} else {    throw new UserWalletException(['msg' => '用户余额不足']);}
  • 扣款提交:将数据库中的余额进行修改。

    UPDATE user_wallet SET balance=$newUserBalance WHERE uid = $uid;

    例如,扣款后余额变为30元。

  • 异常场景

    在并发处理中,可能会出现以下情况:

  • 并发查询:用户并发购买业务A和业务B,查询余额时可能显示100元。
  • 扣款处理:业务A扣款后余额变为30元,业务B扣款后余额变为20元。
  • 数据不一致:由于并发处理,系统可能会显示余额更新失败或不一致。
  • 解决方案

    为了解决并发扣款中的数据一致性问题,可以采用以下两种方法:

    悲观锁

    使用Redis的悲观锁,确保在并发处理中只允许一台服务器对数据进行操作。

    use Ar414\RedisLock;$redis = new \Redis();$redis->connect('127.0.0.1', '6379');$lockTimeOut = 5;$redisLock = new RedisLock($redis, $lockTimeOut);$lockKey = 'lock:user:wallet:uid:1001';$lockExpire = $redisLock->getLock($lockKey);if ($lockExpire) {    try {        // 查询用户钱包余额        $userBalance = 100;        // 查询商品价格        $goodsPrice = 80;        if ($userBalance >= $goodsPrice) {            $newUserBalance = $userBalance - $goodsPrice;            // 更新数据库            $redisLock->releaseLock($lockKey, $lockExpire);        } else {            throw new Exception('用户余额不足');        }    } catch (\Throwable $throwable) {        $redisLock->releaseLock($lockKey, $lockExpire);        throw new Exception('网络问题');    }

    乐观锁

    使用Redis的乐观锁(CAS,Compare And Set),在写入数据时进行版本检查,确保数据一致性。

    UPDATE user_wallet SET balance=$newUserBalance WHERE uid = $uid AND balance = $oldUserBalance

    这样可以确保并发处理中只有一台服务器能够成功写入数据。

    结语

    在并发处理中,数据一致性是一个关键问题。选择适合的锁机制可以有效避免数据不一致的情况。然而,使用Redis的悲观锁可能会带来较高的吞吐量压力。因此,需要根据实际情况选择合适的解决方案。

    转载地址:http://dwtfk.baihongyu.com/

    你可能感兴趣的文章
    PAT 2-07. 素因子分解(20)
    查看>>
    SparkSQL学习03-数据读取与存储
    查看>>
    PAT L2-012. 关于堆的判断
    查看>>
    PAT Spell It Right [非常简单]
    查看>>
    PAT-1044. Shopping in Mars (25)
    查看>>
    PAT-乙级-1040 有几个PAT
    查看>>
    PAT1093 Count PAT's (25)(逻辑题)
    查看>>
    PATA1038题解(需复习)
    查看>>
    Patching Array
    查看>>
    Spring源码学习(二):Spring容器之prepareContext和BeanFactoryPostProcessor的介绍
    查看>>
    PatchMatchStereo可能会需要的Rectification
    查看>>
    Path does not chain with any of the trust anchors
    查看>>
    Path形状获取字符串型变量数据
    查看>>
    PAT甲级——1001 A+B Format (20分)
    查看>>
    Skywalking原理
    查看>>
    PAT甲级——1006 Sign In and Sign Out (25分)
    查看>>
    PAT甲级——1007 Maximum Subsequence Sum (25分)
    查看>>
    PAT甲级——1009 Product of Polynomials (25分)(最后一个测试点段错误)
    查看>>
    Spring对jdbc的支持
    查看>>
    PayPal网站付款标准版(for PHP)
    查看>>