我正在使用Rails 3.1和MySQL 5.1设计一个类似拍卖的Web应用程序。用户将有账户余额,因此很重要的一点是,如果他没有足够的资金,他不应该竞标拍卖品。
显然,我将赢得拍卖的内容封装到一个交易中,类似于以下内容:
交易1:
顺便说一下,我目前正在使用乐观锁定,因此我的所有表都有 lock_version 列。
在执行此类事务时,用户可以通过另一个输入放置其他出价,因此每当他们放置出价时,一段代码会检查当前可用余额是否足够。
这里再次相同:
事务2:
显然,我需要确保两个事务不会重叠,否则在第一个事务正在处理过程中,第二个事务可能会读取余额,导致我的账户余额变为负数(出价已保存,然后第一个事务提交,那么用户可能已经用他不再拥有的资金进行出价)。
需要注意的一点是,第二个事务不对Account做任何更改,仅仅是读取账户。我想问题归结为一个问题:如何在运行事务1时防止选择性SELECT语句的任何读取?
如何让事务2等待事务1完成?这是否可能使用乐观锁和可用的MySQL事务隔离级别之一来实现,还是我需要在这里使用悲观锁?如果悲观锁是唯一的答案,在每个事务中读取账户记录后添加a.lock!是否足够?
当然,设计标准是:
显然,我将赢得拍卖的内容封装到一个交易中,类似于以下内容:
交易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
显然,我需要确保两个事务不会重叠,否则在第一个事务正在处理过程中,第二个事务可能会读取余额,导致我的账户余额变为负数(出价已保存,然后第一个事务提交,那么用户可能已经用他不再拥有的资金进行出价)。
需要注意的一点是,第二个事务不对Account做任何更改,仅仅是读取账户。我想问题归结为一个问题:如何在运行事务1时防止选择性SELECT语句的任何读取?
如何让事务2等待事务1完成?这是否可能使用乐观锁和可用的MySQL事务隔离级别之一来实现,还是我需要在这里使用悲观锁?如果悲观锁是唯一的答案,在每个事务中读取账户记录后添加a.lock!是否足够?
当然,设计标准是:
- 即使意味着编写更多代码,我也要寻找最高效的解决方案。
- 数据一致性至关重要
raise ActiveRecord::Rollback if a.balance < the_price_of_the_bid
还会有进一步的代码,通过读取Bids表来检查现有的未决竞标(这可能包括正在处理事务1的竞标)。 - KKK