比較 DML 和變異

資料操縱語言 (DML) 和變異是 Spanner 中的兩個 API,可用於修改資料。每個工具都提供類似的資料操作功能。本頁面將比較這兩種方法。

什麼是資料操縱語言 (DML)?

您可以使用 Spanner 中的資料操縱語言 (DML),透過 INSERTUPDATEDELETE 陳述式操縱資料庫資料表中的資料。您可以使用用戶端程式庫Google Cloud 主控台gcloud spanner 執行 DML 陳述式。

Spanner 提供以下兩種 DML 執行作業的實作方式,每種方式都有不同的屬性。

  • 標準 DML:適用於標準線上交易處理 (OLTP) 工作負載。

    如需詳細資訊 (包括程式碼範例),請參閱「使用 DML」一文。

  • 分區 DML:專為大量更新和刪除而設計,如以下範例所示。

    • 定期清理和垃圾收集。例如刪除舊資料列或將資料欄設為空值。

    • 對具有預設值的新資料欄進行補充作業。例如使用 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() 來讀取已更新的專輯。

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 筆記錄。這是因為這是因為使用變異所做的變更,在交易修訂前不會顯示在後續陳述式中。在理想情況下,我們應等到交易結束時,才將寫入內容放在緩衝區。

後續步驟