比較 DML 和變異

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

什麼是 data manipulation language (DML)?

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

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

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

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

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

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

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

    如需更多資訊 (包括程式碼範例),請參閱「使用分區 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」。這是因為這是因為使用 Mutations 進行的變更,在交易提交前,後續陳述式都無法看到。理想情況下,我們應該只在交易結束時才進行緩衝寫入。

後續步驟