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

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

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

如需了解如何使用 FOR UPDATE 子句,请参阅 GoogleSQLPostgreSQL 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 所在行的 SingerIdSingerInfo 单元格。

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

在子查询中使用

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

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

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

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

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 子句。

后续步骤