比較 DML 和變異

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

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

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

Spanner 提供下列兩種 DML 執行實作方式,各有不同的屬性。

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

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

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

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

    • 對具有預設值的新資料欄進行補充作業。例如使用 UPDATE 陳述式,將值為 NULL 的新資料欄設為 False。

    如需更多資訊 (包括程式碼範例),請參閱「使用分區 DML」。

    您可以對大量寫入作業使用批次寫入,不必執行不需要不可分割交易的讀取作業。詳情請參閱「使用批次寫入作業修改資料」。

什麼是突變?

修改作業代表一系列的插入、更新和刪除作業,Spanner 會以不可分割的形式,將這些作業套用到資料庫中不同的資料列和資料表。您可以在修改作業中加入適用於不同資料列或資料表的作業。定義一或多個包含一或多個寫入作業的修改作業後,您必須套用修改作業來提交寫入作業。系統會按照變更新增至變異的順序套用變更。

如需更多資訊 (包括程式碼範例),請參閱「使用變異插入、更新與刪除資料」。

DML 和變異的功能比較

下表列出常見資料庫作業和功能的 DML 和修改作業支援。

作業 DML 異動
插入資料 支援 支援
刪除資料 支援 支援
更新資料 支援 支援
插入或忽略資料 支援 不支援
讀取您的寫入內容 (RYW) 支援 不支援
插入或更新資料 (Upsert) 支援 支援
SQL 語法 支援 不支援
限制條件檢查 每份對帳單 在提交時間
強制執行異動限制 (含索引) 每個陳述式 (執行後重設) 每個修訂版本 (累計)

DML 和突變在支援下列功能時有所不同:

  • 讀取寫入內容:在有效交易中讀取未提交的結果。使用 DML 陳述式所做的變更,對於相同交易中的後續陳述式而言是可見的。這與使用變異不同,變更必須在交易修訂後,才會顯示在任何讀取作業中 (包括在相同交易中完成的讀取作業)。這是因為交易中的變異會緩衝處理用戶端 (本機),並在修訂作業中傳送至伺服器。因此,相同交易中的 SQL 或 DML 陳述式無法看到修訂要求中的變異。

  • 限制檢查:Spanner 會檢查每個 DML 陳述式之後的限制。相較之下,使用變異時,Spanner 會緩衝用戶端的變異直到修訂為止,並在修訂時檢查限制。評估每個 DML 陳述式之後的限制,可讓 Spanner 確保同一交易中後續查詢傳回的資料與結構定義一致。

  • 強制執行異動限制:對於 DML,每個陳述式會套用 80,000 的異動限制 (包括索引),並在陳述式執行後重設。這樣一來,只要單一陳述式不超過上限,單一交易就能執行多個 DML 陳述式,總共超過 80,000 個變異。Mutation API 只能做為提交作業的一部分使用,因此提交作業中所有異動 (包括索引) 的累計上限為 80,000 項。這兩種方法都受限於 100 MiB 的交易大小上限。

  • SQL 語法:DML 提供傳統的資料操縱方式。您可以使用 DML API,運用 SQL 技能變更資料。

最佳做法:避免在相同交易中混用 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 records updated」(已更新 0 筆記錄) 。這是因為這是因為我們使用 Mutations 所做的變更,在交易修訂前,後續陳述式都無法看到。理想情況下,我們應等到交易結束時,才將寫入內容放在緩衝區。

後續步驟