【发布时间】:2015-09-10 10:57:11
【问题描述】:
我正在开发一个基于移动的购物应用程序。该应用程序的作用是,用户将钱存入他的帐户,然后再使用。 竞争条件是我试图避免的问题之一。这样用户帐户余额就不会被计算错误。
我使用的是mysql 5.5,php。
这是我想出的。
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)
)
这个想法是对 user_id 和 is_active 设置唯一的约束,以便只能处理一个活动订单(存款或使用余额)。活动订单将 is_active 设置为 1。is_active 更新为时间戳,因此一旦订单完成,唯一约束将得到满足。存钱也是类似的逻辑。
这是购买项目的账户余额伪代码:
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=
这个逻辑有问题吗?
或者在没有唯一约束的情况下可以避免竞争条件:
update user balance = balance - item_price where balance >= item_price
更新 1
我错过了一个会使事情变得更复杂的案例。详情如下:
当商品价格高于账户余额时,用户可以选择通过外部支付服务支付余额。
// 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
由于付款和帐户余额更新发生在一系列请求中。交易在这里行不通。
因此,我选择取消同一用户之前通过外部服务支付的任何有效订单,然后再创建另一个有效订单。这将产生一个副作用,即会减慢在短时间内提交许多无偿订单的用户。这可以作为额外的清理,以防任何现有的已放弃的活动订单阻止用户下新订单。
is_active 是防止竞争条件发生的保障措施。
【问题讨论】:
标签: mysql race-condition