我正在使用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

顺便说一下,我目前正在使用乐观锁,因此我的所有表都有列锁版本。
在执行这样的事务时,用户可以通过另一个输入放置其他出价,因此每当他们放置出价时,一段代码检查当前可用余额是否足够
这里也一样:
交易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不会对帐户进行任何更改,它只是读取帐户。我想这可以归结为一个问题:如何在运行事务1时防止对所选select语句的任何读取。
如何使事务2等待事务1完成?乐观锁和一个可用的mysql事务隔离级别是否可能,或者我需要在这里使用悲观锁?如果悲观的锁定是唯一的答案
锁!
在阅读完两个交易记录后是否足够?
设计标准当然是
我正在寻找最有效的解决方案,即使它意味着更多
编码。
数据一致性至关重要

最佳答案

现在,我花了将近10个小时不停地阅读各种文章和文档,并使用rails控制台进行尝试和出错,我想总结一下我的发现:
乐观锁定:对于满足我的需求毫无用处,锁定只在我实际保存帐户余额记录时生效。但是,出价不会更新帐户记录,因此不会触发乐观锁定,除非我在帐户记录中维护一个字段,该字段跟踪所有公开出价的当前承诺资金,因此将在出价时更新帐户记录(我不想这样做,因为它将重新每次保存出价时都需要另一个数据库更新)。
所以那只会给我留下讨厌的锁。为了简单起见,我决定锁定用户记录,因此事务1的代码更改为:
交易1:

ActiveRecord::Base.transaction do
    u = User.find(session[:user_id],:lock=>true)
    a = Account.where(:id=>session[:user_id]).first
    a.balance -= the_price_of_the_item
    ... some more code here ...
    a.save!
end

以及交易2:
ActiveRecord::Base.transaction do
    u = User.find(session[:user_id],:lock=>true)
    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

此外,我决定将mysql事务隔离级别设置为可序列化。

关于mysql - 拍卖/银行类应用中的乐观或悲观锁定(Rails/MySQL),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8676128/

10-16 18:25