Trước khi đến với Pessimistic Locking, hãy cùng nhìn lại database locking và optimistic locking được đề cập ở bài viết lần trước.
Khác với optimistic locking là kiểm tra lock_version
khi cập nhật dữ liệu, pessimistic locking sẽ khóa record ngay khi người dùng đầu tiên
truy cập vào dữ liệu đó, tất cả những người dùng khác sẽ bị loại cho đến khi tiến trình cập nhật của dữ liệu của người dùng lock đầu tiên
hoàn thành.
1
2
3
4
5
account = Account.find_by_user_id(5)
account.lock!
#no other users can read this account, they have to wait until the lock is released
account.save!
#lock is released, other users can read this account
Rails thực hiện Pessimistic Locking bằng cách sử dụng những truy vấn câu lệnh khóa trong cơ sở dữ liệu.
1
2
3
4
5
6
7
8
9
10
11
12
13
Account.transaction do
# In mysql: `FOR UPDATE`, `LOCK IN SHARE MODE`
# SELECT * FROM accounts WHERE id = 1 LIMIT 1 FOR UPDATE
account = Account.lock("FOR UPDATE").find_by(id: 1)
# SELECT * FROM accounts WHERE id = 1 LIMIT 1 LOCK IN SHARE MODE
account = Account.lock("LOCK IN SHARE MODE").find_by(id: 1)
# In postgresql: `FOR UPDATE`, `FOR UPDATE NOWAIT`, `FOR NO KEY UPDATE`, `FOR SHARE`, `FOR KEY SHARE`
# SELECT * FROM accounts WHERE id = 1 LIMIT 1 FOR UPDATE NOWAIT
account = Account.lock("FOR UPDATE NOWAIT").find_by(id: 1)
# SELECT * FROM accounts WHERE id = 1 LIMIT 1 FOR NO KEY UPDATE
account = Account.lock("FOR NO KEY UPDATE").find_by(id: 1)
end
Chúng ta có thể sử dụng phương thức ActiveRecord::Base#lock!
để để khóa 1 record bằng id. Đây là cách tốt nhất nếu không
cần thiết phải khóa mọi row trong cơ sở dữ liệu, mặt định sẽ dùng kiểu khóa FOR UPDATE
1
2
3
4
5
6
7
8
Account.transaction do
account = Account.find(1)
# SELECT * FROM accounts WHERE id = 1 LIMIT 1 FOR UPDATE
account.lock!
account.balance -= 1000000
account.save!
end
Comments