数据操纵语言最佳做法

本页面介绍使用数据操纵语言 (DML) 和分区 DML 的最佳做法。

使用 WHERE 子句缩小锁定范围

在读写事务中执行 DML 语句。当 Spanner 读取数据时 对您读取的行范围的有限部分获取共享读取锁定。具体而言,它仅会对您访问的列获取这些锁定。锁定可以包括 满足 WHERE 子句的过滤条件。

当 Spanner 使用 DML 语句修改数据时,它会获取 您正在修改的特定数据此外,它还会采用与读取数据时相同的方式获取共享锁定。如果您的请求包含大型行范围或整个表,则共享的 锁定可能会阻止其他事务并行进行。

如需尽可能高效地修改数据,请使用 WHERE 子句, Spanner 以仅读取必要的行。您可以通过按主键进行过滤或按二级索引的键进行过滤来实现此目标。WHERE 子句会限制 共享锁,让 Spanner 能够更高效地处理更新。

例如,假设 Singers 表中的某位音乐人更改了其名字,则您需要在您的数据库中更新该名字。您可以执行以下 DML 语句,但它会强制 Spanner 扫描整个表并获取共享锁, 覆盖整个表。因此,Spanner 必须读取不必要的数据,并且 并发事务无法并行修改数据:

-- ANTI-PATTERN: SENDING AN UPDATE WITHOUT THE PRIMARY KEY COLUMN
-- IN THE WHERE CLAUSE

UPDATE Singers SET FirstName = "Marcel"
WHERE FirstName = "Marc" AND LastName = "Richards";

为使该更新更加高效,请在 WHERE 子句中添加 SingerId 列。SingerId 列是 Singers 表的唯一主键列:

-- ANTI-PATTERN: SENDING AN UPDATE THAT MUST SCAN THE ENTIRE TABLE

UPDATE Singers SET FirstName = "Marcel"
WHERE FirstName = "Marc" AND LastName = "Richards"

如果 FirstNameLastName 上没有索引,您需要 扫描整个表以找到目标歌手。如果您不想添加辅助电子邮件地址 索引以使更新更加高效,然后添加 SingerId 列 。WHERE

SingerId 列是 Singers 表。如需查找它,请单独运行 SELECT, 更新事务之前的只读事务:


  SELECT SingerId
  FROM Singers
  WHERE FirstName = "Marc" AND LastName = "Richards"
  
  -- Recommended: Including a seekable filter in the where clause
  
  UPDATE Singers SET FirstName = "Marcel"
  WHERE SingerId = 1;

避免在同一事务中使用 DML 语句和变更

Spanner 缓冲使用 DML 执行的插入、更新和删除 语句,并且结果对后续 SQL 和 同一事务中的 DML 语句。此行为不同于 Mutation API,其中 Spanner 会缓冲 在客户端执行更改并将更改作为 提交操作因此,提交请求中的变更对同一事务中的 SQL 或 DML 语句不可见。

避免在同一事务中同时使用 DML 语句和变更。如果您 在同一笔交易中同时使用这两种工具,但您需要考虑 客户端库代码中如果事务同时包含 DML 多个语句和变更,Spanner 会执行 更改前的 DML 语句。

对于仅支持使用变更的操作,您可能需要 在同一事务中合并 DML 语句和变更,例如, insert_or_update.

如果同时使用两者,则缓冲区仅在事务结束时写入。

使用 PENDING_COMMIT_TIMESTAMP 函数写入提交时间戳

GoogleSQL

使用 PENDING_COMMIT_TIMESTAMP 函数在 DML 语句中写入提交时间戳。Spanner 在事务时选择提交时间戳 提交。

PostgreSQL

使用 SPANNER.PENDING_COMMIT_TIMESTAMP() 函数在 DML 语句中写入提交时间戳。Spanner 在事务时选择提交时间戳 提交。

分区 DML 以及日期和时间戳函数

分区 DML 使用可能在不同时间运行和提交的一个或多个事务。如果使用日期时间戳函数,则修改的行可能包含不同的值。

使用批量 DML 缩短延迟时间

如需缩短延迟时间,请使用批量 DML 将 在单个客户端-服务器轮中向 Spanner 插入多个 DML 语句 。

批量 DML 可以将优化应用于 批量进行,以实现更快、更高效的数据更新。

  • 通过单个请求执行写入操作

    Spanner 会自动优化由类似 INSERT、 UPDATE 或 DELETE 具有不同参数值的批量语句, 前提是它们没有违反数据依赖关系。

    例如,设想一个场景,您希望将大量新的 导出到名为 Albums 的表中。要让 Spanner 优化所有 所需的 INSERT 语句合并为一个高效的服务器端操作, 首先编写使用 SQL 查询的适当 DML 语句 参数:

    INSERT INTO Albums (SingerId, AlbumId, AlbumTitle) VALUES (@Singer, @Album, @Title);
    

    然后,向 Spanner 发送一个调用此语句的 DML 批次 重复且连续的,不同的只有 绑定到语句的三个查询参数的值。Spanner 它会优化这些结构相同的 语句转换为单个服务器端操作,然后再执行该操作。

  • 执行并行写入

    Spanner 会自动优化连续的 DML 语句组 在不违反数据依赖关系的情况下并行执行。 此优化为更多批量 DML 带来了性能优势 语句,因为它可以应用于多种 DML 语句类型(INSERT、 UPDATE 和 DELETE)以及参数化或非参数化 DML 语句。

    例如,我们的示例架构包含表 SingersAlbumsAccounts.AlbumsSingers 内交错存储信息 关于Singers的专辑。以下连续的语句组 将新行写入多个表,并且不包含复杂数据 依赖项

    INSERT INTO Singers (SingerId, Name) VALUES(1, "John Doe");
    INSERT INTO Singers (SingerId, Name) VALUES(2, "Marcel Richards");
    INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10001, "Album 1");
    INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10002, "Album 2");
    INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (2, 10001, "Album 1");
    UPDATE Accounts SET Balance = 100 WHERE AccountId = @AccountId;
    

    Spanner 通过执行 语句。系统会按照以下列中的语句顺序应用写入: 当语句运行失败时,批量处理并保留批量 DML 语义 执行。