【发布时间】:2011-12-30 04:20:24
【问题描述】:
我正在使用 Rails 3.1 和 MySQL 5.1 设计一个类似拍卖的 Web 应用程序。用户将有帐户余额,因此重要的是,如果有人没有足够的资金就不要竞标拍卖物品。
显然,我会将拍卖的“获胜”打包到交易中,如下所示:
交易1:
ActiveRecord::Base.transaction do
a = Account.where(:id=>session[:user_id]).first
# now comes a long part of code with various calculations and other table updates, i.e. time pases
a.balance -= the_price_of_the_item
a.save!
end
顺便说一句,我目前正在使用乐观锁定,因此我所有的表都有 lock_version 列。
在执行此类交易时,用户可以通过另一个输入进行其他出价,因此每当他们出价时,一段代码会检查当前可用余额是否足够
这里还是一样:
事务 2:
ActiveRecord::Base.transaction do
a = Account.where(:id=>session[:user_id]).first
raise ActiveRecord::Rollback if a.balance < the_price_of_the_bid + Bids.get_total_bid_value_for_user(session[:user_id])
# now process the bid saving
end
显然,我需要确保这两个交易不重叠,否则交易 2 可能正在读取余额,而交易 1 正在处理中,我最终得到一个负账户余额(出价被保存,然后交易 1 提交,则用户可能用他不再拥有的资金出价)。
需要注意的一点是,事务 2 并没有对 Account 进行任何更改,它只是读取了 account。我想这归结为一个问题:How to prevent any reads for selected SELECT statements while running transaction 1.
如何让事务 2 等待事务 1 完成?是否可以使用乐观锁定和可用的 MySQL 事务隔离级别之一,还是我需要在这里使用悲观锁定?如果悲观锁定是唯一的答案将添加 a.锁! 读完两笔交易中每一笔的账户记录就够了吗?
设计标准当然是
- 我正在寻找性能最高的解决方案,即使它意味着更多 编码。
- 数据一致性至关重要
【问题讨论】:
-
只是想了解您的数据。假设某人的余额为 10。他们可以创建 2 个每个 10 的出价(此时我们不知道是否会获胜)?如果他们不能,您如何跟踪?
-
不,他们不能,否则我冒着他们赢得两个投标的风险,这将导致他们的账户余额为 -10。跟踪在事务 2 中完成,它检查可用余额并且只有用户的账户上有足够的钱,然后他的出价将被接受并保存。
-
所以该代码也会检查当前是否有任何未结出价? (或者账户余额是否反映了这一点)
-
余额仅跟踪实际余额。在行之后:
raise ActiveRecord::Rollback if a.balance < the_price_of_the_bid将有进一步的代码,通过读取投标表(作为回报可能包括正在交易 1 中处理的投标)来检查现有的公开投标。
标签: mysql ruby-on-rails ruby-on-rails-3.1