Phalcon框架下如何锁定MySQL数据库 避免交易插入过程数据变更
不需要锁定整个数据库,采用MySQL事务+行级排他锁即可实现你要的并发控制效果,Phalcon框架原生支持对应操作,具体实现方式如下:
核心实现逻辑
开启数据库事务后,使用FOR UPDATE语法在查询用户余额时对对应行加排他锁,加锁后其他事务无法修改该用户记录,直到当前事务提交或回滚,彻底避免查询后到更新前余额被篡改。校验余额充足后执行余额扣减、交易记录插入操作,全部完成后提交事务自动释放锁;余额不足或执行异常直接回滚事务即可。
Phalcon 代码示例
// 开启数据库事务 $this->db->begin(); try { // 加排他锁查询用户余额,锁定当前用户记录 $user = $this->db->fetchOne( "SELECT sold FROM user WHERE id = ? FOR UPDATE", \Phalcon\Db\Enum::FETCH_ASSOC, ['Charlie'] ); // 查询企业交易成本,若cost字段存在中途修改风险也可加FOR UPDATE锁 $company = $this->db->fetchOne( "SELECT cost FROM company WHERE id = ?", \Phalcon\Db\Enum::FETCH_ASSOC, ['Tango'] ); if ($user['sold'] >= $company['cost']) { // 扣减用户余额 $this->db->execute( "UPDATE user SET sold = sold - ? WHERE id = ?", [$company['cost'], 'Charlie'] ); // 插入交易记录 $this->db->execute( "INSERT INTO transactions (id_company, id_user, cost) VALUES (?, ?, ?)", ['Tango', 'Charlie', $company['cost']] ); // 提交事务,自动释放所有行锁 $this->db->commit(); // 后续交易成功逻辑 } else { // 余额不足回滚事务,释放锁 $this->db->rollback(); // 后续余额不足提示逻辑 } } catch (\Exception $e) { // 执行异常强制回滚,释放锁 $this->db->rollback(); // 后续异常处理逻辑 }
注意事项
- 全库/全表锁会严重影响业务并发性能,非特殊场景不建议使用,行级锁只会锁定你操作的用户记录,不影响其他用户的正常交易。
- 所有涉及修改用户余额的业务逻辑,都需要统一使用
FOR UPDATE加锁查询,否则仍会出现并发不一致问题。 - 高并发场景下可以选择乐观锁方案:给
user表新增versionint类型字段,更新余额时增加version校验:UPDATE user SET sold = sold - :cost, version = version + 1 WHERE id = :userId AND version = :oldVersion,如果执行后影响行数为0则说明余额已被其他事务修改,直接返回失败或重试即可,无需显式加锁,并发性能更优。
内容的提问来源于stack exchange,提问作者Luca95
相关产品推荐
相关产品推荐

