简体   繁体   English

避免交易中的竞争条件

[英]Avoid Race Condition in Transaction

I am developing a mobile based shopping app. 我正在开发基于移动的购物应用程序。 What the app does is, user deposits money in his account and spend it later. 该应用程序的作用是,用户将钱存入他的帐户中,以后再用。 Race condition is one of the problem I am trying to avoid. 种族状况是我要避免的问题之一。 such that user account balance won't be miscalculated. 这样就不会错误地计算用户帐户余额。

I am using mysql 5.5, php. 我正在使用mysql 5.5,php。

Here is what I have come up. 这是我提出的。

create table orders ( order_id int, user_id int, title varchar, item_price decimal, is_active int default null, constraint primary key (order_id), constraint unq unique (user_id, is_active) )

The idea is to set unique constraint on user_id and is_active so that only one active order(deposit money or use balance) can be processed. 想法是对user_id和is_active设置唯一的约束,以便只能处理一个有效订单(存款或使用余额)。 active order will have is_active set to 1. is_active is updated to a timestamp so the unique constraint will be satisfied once the order is completed. 有效订单的is_active设置为is_active更新为时间戳,因此一旦订单完成,唯一约束将得到满足。 Deposit money is similar logic. 存钱是类似的逻辑。

Here is the pseudo code for purchase item with account balance: 这是带有帐户余额的购买商品的伪代码:

if  user has enough balance,
  start transaction
  insert into order with user_id, order_id, is_active=1
  update user balance = balance - item_price where balance >= item_price
  commit

if transaction success,
  update order set is_active= current_timestamp where user_id=, order_id=

Is there any problem with this logic? 这个逻辑有什么问题吗?

Or the race condition can be avoided without the unique constraint with this line: update user balance = balance - item_price where balance >= item_price 或者使用此行,可以在没有唯一约束的情况下避免竞争条件: update user balance = balance - item_price where balance >= item_price

UPDATE 1 更新1

I have missed a case that will make things more complicated. 我错过了一个使情况变得更加复杂的案例。 Here is the detail: 这是详细信息:

User can choose to pay the remaining via an external payment service when an item price is greater than his account balance. 当物料价格大于其帐户余额时,用户可以选择通过外部支付服务来支付剩余款项。

// first http request
try to cancel any previous active external payment by the same user
if user has enough balance,
    get a secure token from external payment service
    insert into order with user_id, order_id, is_active=1

// second http request
user paid and external payment service notifies my backend about the success payment. Then 
    start transaction
    update user balance = balance - balance_pay_amount where balance >= balance_pay_amount
    update order set is_active= current_timestamp where user_id=, order_id=
    commit

Since the payment and account balance update happen in a span of requests. 由于付款和帐户余额更新是在多个请求中发生的。 transaction along won't work here. 交易在这里不起作用。

So I choose to cancel any previous active order paid via external service by the same user before creating another active order. 因此,我选择在创建另一个有效订单之前取消同一用户通过外部服务支付的任何先前的有效订单。 This will have a side effect of slowing down user who submit many orders without pay in a short period of time. 这将产生副作用,使用户在短时间内无偿提交许多订单的用户放慢速度。 This serves as additional cleanup in case any existing abandoned active order prevent user making new order. 如果任何现有的已放弃活动订单阻止用户下达新订单,这可以作为额外的清理工作。

is_active is the safeguard to prevent race condition from happening. is_active是防止争用情况发生的保障措施。

The is_active flag is not necessary. is_active标志不是必需的。 You need to make sure that you lock the user's balance before you do the check. 在执行检查之前,您需要确保锁定用户的余额。

start transaction 
if  user has enough balance (lock the rows using a select query with FOR UPDATE)
  insert into order with user_id, order_id, is_active=1
  update user balance = balance - item_price where balance >= item_price
  commit
else 
  rollback
  show some error or something

This guarantees that the user balance can't be changed by another thread while the transaction is active. 这样可以确保在事务处于活动状态时,其他线程无法更改用户余额。 It also guarantees that the if user has enough balance will only be evaluated for a user which does not have a transaction active at the moment. 它还保证仅针对当前没有活跃交易的用户评估if user has enough balance

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM