在可重复读取隔离中使用 SELECT FOR UPDATE

本页面介绍了如何在可重复读隔离级别中使用 FOR UPDATE 子句。

对于可重复读和可序列化隔离,FOR UPDATE 子句的锁定机制有所不同。与可序列化隔离不同,FOR UPDATE 子句在可重复读隔离中不会获取锁定。如需详细了解 FOR UPDATE 中的锁定,请参阅在可序列化隔离中使用 SELECT FOR UPDATE。

如需了解如何使用 FOR UPDATE 子句,请参阅 GoogleSQL 和 PostgreSQL FOR UPDATE 参考指南。

为何使用 FOR UPDATE 子句

当事务以可重复读隔离级别运行时,SELECT 语句查询的数据始终以事务的已建立快照时间戳返回。如果事务随后根据查询的数据进行更新,那么如果并发事务也更新了查询的数据,则可能会出现正确性问题。如需了解详情,请参阅读写冲突和正确性。

为确保 SELECT 语句查询的数据在事务提交时仍然有效,您可以将 FOR UPDATE 子句与可重复读隔离级别搭配使用。使用 FOR UPDATE 可保证事务的正确性,即使在读写冲突的情况下,数据可能在读取和修改之间被另一个事务修改。

查询语法

本部分介绍了使用 FOR UPDATE 子句时的查询语法。

最常见的用法是在顶级 SELECT 语句中。例如:

SELECT SingerId, SingerInfo
FROM Singers WHERE SingerID = 5
FOR UPDATE;

FOR UPDATE 子句可确保 SELECT 语句和 SingerID = 5 查询的数据在事务提交时仍然有效,从而防止因并发事务更新查询的数据而可能出现的正确性问题。

在 WITH 语句中使用

当您在 WITH 语句的外层查询中指定 FOR UPDATE 时,FOR UPDATE 子句不会验证 WITH 语句中扫描的范围。

在以下查询中,由于 FOR UPDATE 未传播到通用表表达式 (CTE) 查询,因此未验证任何扫描范围。

WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;

如果在 CTE 查询中指定了 FOR UPDATE 子句,则会验证 CTE 查询的扫描范围。

在以下示例中,系统会验证 SingerId > 5 所在行的 SingerId 和 SingerInfo 单元格。

WITH s AS
  (SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;

在子查询中使用

您可以在包含一个或多个子查询的外部级查询中使用 FOR UPDATE 子句。系统会验证顶级查询和子查询中扫描的范围,但表达式子查询除外。

以下查询会针对 SingerId > 5. 的行验证 SingerId 和 SingerInfo 单元格

(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;

以下查询不会验证 Albums 表中的任何单元格,因为它位于表达式子查询中。系统会验证表达式子查询返回的行的 SingerId 和 SingerInfo 单元格。

SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;

用于查询视图

您可以使用 FOR UPDATE 子句查询视图,如以下示例所示:

CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;

SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;

在定义视图时,不能使用 FOR UPDATE 子句。

不受支持的应用场景

以下 FOR UPDATE 用例不受支持:

  • 作为在 Spanner 外部运行代码的互斥机制:请勿使用 Spanner 中的锁定来确保对 Spanner 外部资源的独占访问。Spanner 可能会中止事务,例如,如果重试事务(无论是通过应用代码明确进行,还是通过 Spanner JDBC 驱动程序等客户端代码隐式进行),则只能保证在已提交的尝试期间持有锁。
  • 与 LOCK_SCANNED_RANGES 提示搭配使用时:您无法在同一查询中同时使用 FOR UPDATE 子句和 LOCK_SCANNED_RANGES 提示,否则 Spanner 会返回错误。如需了解详情,请参阅与 LOCK_SCANNED_RANGES 提示的比较。
  • 在全文搜索查询中:您无法在使用全文搜索索引的查询中使用 FOR UPDATE 子句。
  • 在只读事务中:FOR UPDATE 子句仅在读写事务中执行的查询中有效。
  • 在 DDL 语句中:您无法在 DDL 语句中的查询中使用 FOR UPDATE 子句,这些语句会存储起来以供日后执行。例如,在定义视图时,您无法使用 FOR UPDATE 子句。

后续步骤