PHP Mysql - 创建自己的银行系统

Posted

技术标签:

【中文标题】PHP Mysql - 创建自己的银行系统【英文标题】:PHP Mysql - creating own banking system 【发布时间】:2018-01-07 17:28:11 【问题描述】:

我正在开展一个项目,用户将用真钱购买“硬币”。他们还可以在交易系统中以真钱出售这些代币。他们甚至可以将硬币发送给其他用户。这将用 php 编写。我了解 mysql 事务,但我不是 100% 确定这需要 100% 来自并发错误。我创建了伪代码,我认为其中存在缺陷。

    function withdraw($id, $amount_to_withdraw) 
       $ret = false;
       $balance = $db->getBalanceById($id);

       $this->makeSureAmountIsNotNegative($amount_to_withdraw);

       $new_balance = $balance - $amount_to_withdraw;

       if ($new_balance >= 0.00) 
        try 
             $db->startTransaction();

            // THIS IS WHERE THE FLAW IS!!!
            $db->do("UPDATE account SET balance = ? WHERE id = ?", array($new_balance, $id));

            $db->commit();

            $ret = true;
         catch (Exception $e) 
            $db->rollback();
        
    

    return $ret;

据我了解,在 PHP 中可能会在此完全完成之前执行另一个请求,并且可能会将余额设置为低于零和其他错误的并发错误。 我如何编写此代码以使其免受这些错误的影响。 我需要行级mysql锁定吗:

// ROW LEVEL LOCKING FOR UPDATE
$balance = $db->select("SELECT balance FROM account WHERE id = ? FOR UPDATE;", array($id));

谢谢 布赖恩

【问题讨论】:

你的意思是写SET balance = ? 是的,谢谢..已编辑 【参考方案1】:

您需要将获取旧余额的查询和设置新余额的查询放在同一个事务中。所以你需要把$db->startTransaction()放在$db->getBalance($id);之前,然后把这些都放在try块里面。

function withdraw($id, $amount_to_withdraw) 
    $ret = false;

    try 
        $db->startTransaction();

        $balance = $db->getBalanceById($id);

        $this->makeSureAmountIsNotNegative($amount_to_withdraw);

        $new_balance = $balance - $amount_to_withdraw;

        if ($new_balance >= 0.00) 
            $db->do("UPDATE account SET balance = ? WHERE id = ?", array($new_balance, $id));

            $ret = true;
        
        $db->commit();

     catch (Exception $e) 
        $db->rollback();
    

    return $ret;

但更简单的方法是在 UPDATE 查询中进行减法运算,而不是进行两次查询。

$db->do("UPDATE account SET balance = balance - ? 
        WHERE id = ? AND balance >= ?", array($amount_to_withdraw, $id, $amount_to_withdraw));

您不需要为此显式创建事务,因为语句始终是它自己的事务。

【讨论】:

好的,如果我把减法放在查询中,什么会保证新余额> 0.00?...假设余额为 100,mysql 不会高兴地执行此操作:余额 = 100 - 101,这会导致负余额? 我在您的原始代码中没有看到任何阻止这种情况的内容。您只需检查提款金额是否为负数,而不是 $new_balance 是否为负数。 $balance = $db->getBalanceById($id); $this->makeSureAmountIsNotNegative($amount_to_withdraw); $new_balance = $balance - $amount_to_withdraw; if ($new_balance >= 0.00) 糟糕,错过了。我已经更新了答案以在 WHERE 子句中显示如何做到这一点。 唯一的问题是你不能这样设置$ret。您的 DB 类是否提供了一种调用 PDOStatement::rowCount() 的方法?这将告诉您是否找到并更新了任何行。

以上是关于PHP Mysql - 创建自己的银行系统的主要内容,如果未能解决你的问题,请参考以下文章

在 php/mysql 应用程序中收集和存储银行帐户详细信息时的安全注意事项

Java swing+mysql 银行管理系统(1016)

使用SQL-Server创建一个银行数据管理系统Ⅰ

为啥支付宝绑定银行卡显示银行系统繁忙

Java连接Oracle数据库开发银行管理系统设计篇

银行 Rehat7无网部署Mysql.5.7