悲觀鎖定
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