数据操纵 语言 (DML) 和变更是 Spanner 中的两个 API, 修改数据。它们各自提供类似的数据操纵特性。本页面对两种方法进行了比较。
什么是数据操纵语言 (DML)?
借助 Spanner 中的数据操纵语言 (DML),您可以使用 INSERT
、UPDATE
和 DELETE
语句来操纵数据库表中的数据。您可以使用
客户端库、
Google Cloud 控制台和 gcloud spanner。
Spanner 提供以下两种 DML 执行实现,每种实现分别具有不同的 属性。
标准 DML - 适用于标准联机事务处理 (OLTP) 工作负载。
如需了解详情(包括代码示例),请参阅使用 DML
分区 DML - 专为批量更新和删除而设计,如以下示例所示。
定期清理和垃圾回收。例如,删除旧行或将列设置为 NULL。
使用默认值回填新列。示例使用 UPDATE 语句将新列的值设置为 False。
如需了解详情(包括代码示例),请参阅使用分区 DML
您可以使用批量写入执行大量无需读取的写入操作 不需要原子化事务的操作如需更多信息 请参阅使用批量写入修改数据。
什么是变更?
变更表示一系列插入、更新和删除,Spanner 以原子方式将其应用于数据库中的不同行和表。 您可以在变更中添加应用于不同行或不同表的操作。在定义一个或多个变更(其中包含一个或多个写入)之后,必须应用该更改来提交写入。每项更改均按照它们添加到更改的顺序进行应用。
如需了解详情(包括代码示例),请参阅使用变更插入、更新和删除数据。
DML 与变更之间的特性比较
下表汇总了常见数据库操作和特性的 DML 和变更支持。
操作 | DML | 变更 |
---|---|---|
插入数据 | 支持 | 支持 |
删除数据 | 支持 | 支持 |
更新数据 | 支持 | 支持 |
插入或忽略数据 | 支持 | 不支持 |
读己所写 (RYW) | 支持 | 不支持 |
插入或更新数据 (Upsert) | 支持 | 支持 |
SQL 语法 | 支持 | 不支持 |
限制条件检查 | 在每个语句之后 | 提交时 |
DML 和变更在对以下功能的支持方面有所不同:
读取您的写入:读取活跃实例中未提交的结果 交易。使用 DML 语句所做的更改对同一事务中的后续语句可见。这与使用变更不同,使用变更所做的更改在事务提交之前在任何读取(包括在同一事务中完成的读取)中都不可见。这是因为事务中的变更会在客户端(本地)缓冲,并在执行提交操作的过程中发送到服务器。因此,提交请求中的变更对同一事务中的 SQL 或 DML 语句不可见。
约束检查:Spanner 会在每次后检查约束条件 DML 语句。这与使用变更不同,使用变更时,Spanner 会在提交之前缓冲客户端中的变更,并在提交时检查约束条件。在每个 DML 语句之后评估约束条件可使 Spanner 保证由同一事务中的后续查询返回的数据与架构一致。
SQL 语法:DML 提供了一种用于操作数据的传统方法。您可以重复使用 SQL 技能,以便使用 DML API 更改数据。
最佳做法 - 避免在同一事务中混用 DML 和变更
如果事务在提交中同时包含 DML 语句和变更 Spanner 会在更改之前执行 DML 语句。为了避免在客户端库代码中考虑执行顺序,您应该在单个事务中使用 DML 语句或变更,但不要同时使用两者。
以下 Java 示例说明了可能会出现的意外行为。代码使用 Mutation API 将两行插入 Albums。然后,代码段调用 executeUpdate()
来更新新插入的行,并调用 executeQuery()
来读取更新后的 Albums。
static void updateMarketingBudget(DatabaseClient dbClient) {
dbClient
.readWriteTransaction()
.run(
new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) throws Exception {
transaction.buffer(
Mutation.newInsertBuilder("Albums")
.set("SingerId")
.to(1)
.set("AlbumId")
.to(1)
.set("AlbumTitle")
.to("Total Junk")
.set("MarketingBudget")
.to(800)
.build());
transaction.buffer(
Mutation.newInsertBuilder("Albums")
.set("SingerId")
.to(1)
.set("AlbumId")
.to(2)
.set("AlbumTitle")
.to("Go Go Go")
.set("MarketingBudget")
.to(200)
.build());
// This UPDATE will not include the Albums inserted above.
String sql =
"UPDATE Albums SET MarketingBudget = MarketingBudget * 2"
+ " WHERE SingerId = 1";
long rowCount = transaction.executeUpdate(Statement.of(sql));
System.out.printf("%d records updated.\n", rowCount);
// Read a newly updated record.
sql =
"SELECT SingerId, AlbumId, AlbumTitle FROM Albums"
+ " WHERE SingerId = 1 AND MarketingBudget < 1000";
ResultSet resultSet =
transaction.executeQuery(Statement.of(sql));
while (resultSet.next()) {
System.out.printf(
"%s %s\n",
resultSet.getString("FirstName"),
resultSet.getString("LastName"));
}
return null;
}
});
}
如果执行此代码,则会看到 0 条记录已更新。为什么?之所以发生这种情况,是因为我们使用变更所做的更改在事务提交之前对后续语句不可见。理想情况下,应仅在事务的最后缓冲写入。
后续步骤
了解如何使用 DML 来修改数据。
了解如何使用变更来修改数据
要查找事务的变更计数,请参阅检索事务的提交统计信息。