悲观锁定
Locking::Pessimistic
提供对使用 SELECT … FOR UPDATE 和其他锁定类型的行级锁定的支持。
将 ActiveRecord::Base#find
与 ActiveRecord::QueryMethods#lock
链接,以获取所选行的独占锁定
# select * from accounts where id=1 for update
Account.lock.find(1)
调用 lock('some locking clause')
以使用你自己的特定于数据库的锁定子句,例如 ‘LOCK IN SHARE MODE’ 或 ‘FOR UPDATE NOWAIT’。示例
Account.transaction do
# select * from accounts where name = 'shugo' limit 1 for update nowait
shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
shugo.balance -= 100
shugo.save!
yuko.balance += 100
yuko.save!
end
你还可以使用 ActiveRecord::Base#lock!
方法通过 ID 锁定一条记录。如果你不需要锁定每一行,这可能会更好。示例
Account.transaction do
# select * from accounts where ...
accounts = Account.where(...)
account1 = accounts.detect { |account| ... }
account2 = accounts.detect { |account| ... }
# select * from accounts where id=? for update
account1.lock!
account2.lock!
account1.balance -= 100
account1.save!
account2.balance += 100
account2.save!
end
你可以通过使用一个块调用 with_lock
来启动一个事务并一次获取锁定。该块在事务中被调用,对象已经被锁定。示例
account = Account.first
account.with_lock do
# This block is called within a transaction,
# account is already locked.
account.balance -= 100
account.save!
end
有关行锁定的特定于数据库的信息
实例公共方法
lock!(lock = true) 链接
获取此记录的行锁定。重新加载记录以获取请求的锁定。传递 SQL 锁定子句以追加到 SELECT 语句的末尾,或传递 true 以获取“FOR UPDATE”(默认值,独占行锁定)。返回锁定的记录。
来源:显示 | 在 GitHub 上
# File activerecord/lib/active_record/locking/pessimistic.rb, line 69 def lock!(lock = true) if persisted? if has_changes_to_save? raise(<<-MSG.squish) Locking a record with unpersisted changes is not supported. Use `save` to persist the changes, or `reload` to discard them explicitly. Changed attributes: #{changed.map(&:inspect).join(', ')}. MSG end reload(lock: lock) end self end
with_lock(*args) 链接
将传递的块包装在一个事务中,在返回之前使用锁定重新加载对象。你可以将 SQL 锁定子句作为可选参数传递(参见 lock!
)。
你还可以将选项(如 requires_new:
、isolation:
和 joinable:
)传递到包装事务(参见 ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction
)。
来源:显示 | 在 GitHub 上
# File activerecord/lib/active_record/locking/pessimistic.rb, line 92 def with_lock(*args) transaction_opts = args.extract_options! lock = args.present? ? args.first : true transaction(**transaction_opts) do lock!(lock) yield end end