Rails lock_with 悲观锁
https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html
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
or FOR UPDATE NOWAIT
. 例如:
Account.transaction do
# select * from accounts where name = 'shugo' limit 1 for update
shugo = Account.where("name = 'shugo'").lock(true).first
yuko = Account.where("name = 'yuko'").lock(true).first
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
, 向其中传入一个 block , 会启动一个事务, 并获得锁. 整个 block 会被一个事务包装起来, 该对象获得了锁. 例如:
account = Account.first
account.with_lock do
# This block is called within a transaction,
# account is already locked.
account.balance -= 100
account.save!
end
数据库特定的锁可以参考:
MySQL: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
PostgreSQL: https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE