为什么我在锁定的 ActiveRecord 查询生成的 SQL 中看不到 "FOR UPDATE"?
Why don't I see "FOR UPDATE" in the SQL generated by a locked ActiveRecord query?
我正在阅读 "The Rails 5 Way",在第 191 页我看到以下内容:
Pessimistic locking takes place at the database level. The SELECT
statement generated by Active Record will have a FOR UPDATE (or
similar) clause added to it...
Rails docs 似乎包含相同的信息:
Locking::Pessimistic provides support for row-level locking using
SELECT … FOR UPDATE and other lock types.
Chain ActiveRecord::Base#find to ActiveRecord::QueryMethods#lock to
obtain an exclusive lock on the selected rows:
Account.lock.find(1)
# SELECT * FROM accounts WHERE id=1 FOR UPDATE
作为实验,我想在我的本地机器上重现这个 FOR UPDATE
语句。我知道用悲观锁发起t运行saction的方法是调用.lock
class方法(书上给出了例子t = Timesheet.lock.first
)。所以我 运行 玩具 Rails 应用程序 (v 5.1.6) 的 REPL 中的以下代码包含订单 class:
irb(main):015:0> Order.transaction do
irb(main):016:1* o1 = Order.lock.first
irb(main):017:1> o1.update_attributes(name: 'Foo Bar')
irb(main):018:1> end
这产生了以下输出:
(0.3ms) begin transaction
Order Load (0.2ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ? [["LIMIT", 1]]
SQL (1.1ms) UPDATE "orders" SET "name" = ?, "updated_at" = ? WHERE "orders"."id" = ? [["name", "Foo Bar"], ["updated_at", "2018-11-04 03:01:35.593868"], ["id", 1]]
(0.4ms) commit transaction
=> true
我在 SELECT
或 UPDATE
语句中都没有看到 FOR UPDATE
。尝试指定悲观锁定时我做错了什么吗?或者我对应该输出什么 SQL 有错误的期望?
我发现我的玩具应用程序正在使用默认的 Rails sqlite
数据库。我创建了一个新的玩具应用程序 (rails new newbie --database=postgresql
),创建了一个包含多个实例的新用户模型,然后 运行 User.lock.first
,我看到了以下内容:
irb(main):004:0> User.lock.first
User Load (1.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT FOR UPDATE [["LIMIT", 1]]
=> #<User id: 1, name: nil, phone: nil, created_at: "2018-11-05 01:28:23", updated_at: "2018-11-05 01:28:23">
如您所见,FOR UPDATE
出现在 SQL 查询中。从 this Stack Overflow answer,我看到默认情况下 SQLite 不支持悲观锁定:
SELECT ... FOR UPDATE OF ... is not supported. This is understandable
considering the mechanics of SQLite in that row locking is redundant
as the entire database is locked when updating any bit of it.
我正在阅读 "The Rails 5 Way",在第 191 页我看到以下内容:
Pessimistic locking takes place at the database level. The SELECT statement generated by Active Record will have a FOR UPDATE (or similar) clause added to it...
Rails docs 似乎包含相同的信息:
Locking::Pessimistic provides support for row-level locking using SELECT … FOR UPDATE and other lock types.
Chain ActiveRecord::Base#find to ActiveRecord::QueryMethods#lock to obtain an exclusive lock on the selected rows:
Account.lock.find(1)
# SELECT * FROM accounts WHERE id=1 FOR UPDATE
作为实验,我想在我的本地机器上重现这个 FOR UPDATE
语句。我知道用悲观锁发起t运行saction的方法是调用.lock
class方法(书上给出了例子t = Timesheet.lock.first
)。所以我 运行 玩具 Rails 应用程序 (v 5.1.6) 的 REPL 中的以下代码包含订单 class:
irb(main):015:0> Order.transaction do
irb(main):016:1* o1 = Order.lock.first
irb(main):017:1> o1.update_attributes(name: 'Foo Bar')
irb(main):018:1> end
这产生了以下输出:
(0.3ms) begin transaction
Order Load (0.2ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ? [["LIMIT", 1]]
SQL (1.1ms) UPDATE "orders" SET "name" = ?, "updated_at" = ? WHERE "orders"."id" = ? [["name", "Foo Bar"], ["updated_at", "2018-11-04 03:01:35.593868"], ["id", 1]]
(0.4ms) commit transaction
=> true
我在 SELECT
或 UPDATE
语句中都没有看到 FOR UPDATE
。尝试指定悲观锁定时我做错了什么吗?或者我对应该输出什么 SQL 有错误的期望?
我发现我的玩具应用程序正在使用默认的 Rails sqlite
数据库。我创建了一个新的玩具应用程序 (rails new newbie --database=postgresql
),创建了一个包含多个实例的新用户模型,然后 运行 User.lock.first
,我看到了以下内容:
irb(main):004:0> User.lock.first
User Load (1.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT FOR UPDATE [["LIMIT", 1]]
=> #<User id: 1, name: nil, phone: nil, created_at: "2018-11-05 01:28:23", updated_at: "2018-11-05 01:28:23">
如您所见,FOR UPDATE
出现在 SQL 查询中。从 this Stack Overflow answer,我看到默认情况下 SQLite 不支持悲观锁定:
SELECT ... FOR UPDATE OF ... is not supported. This is understandable considering the mechanics of SQLite in that row locking is redundant as the entire database is locked when updating any bit of it.