存取歷來資料

BigQuery 可讓您查詢及還原儲存在 BigQuery 資料儲存庫中,且在時光旅行期間內變更或刪除的資料。存取這類歷來資料,即可從意外刪除事件中復原,或分析過去特定時間點的資料確切狀態,不必維護個別備份。

查詢特定時間點的資料

您可以使用 FOR SYSTEM_TIME AS OF 子句,查詢時光旅行期間內任何時間點的資料表歷來資料。這個子句會採用常數時間戳記運算式,並參照該時間戳記當時的資料表版本。資料表必須儲存在 BigQuery 中,不得為外部資料表。使用 SYSTEM_TIME AS OF 時,資料表大小沒有限制。

舉例來說,下列查詢會傳回一小時前的資料表歷來版本:

SELECT *
FROM `mydataset.mytable`
  FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR);

如果時間戳記指定的時間早於時光旅行時間範圍,或早於資料表建立時間,查詢就會失敗,並傳回類似下列的錯誤:

Invalid snapshot time 1601168925462 for table
myproject:mydataset.table1@1601168925462. Cannot read before 1601573410026.

使用 CREATE OR REPLACE TABLE 陳述式取代現有資料表後,您可以使用 FOR SYSTEM_TIME AS OF 查詢資料表的先前版本。

如果資料表已刪除,您就無法直接使用 FOR SYSTEM_TIME AS OF 查詢歷來資料。查詢失敗,並傳回類似下列的錯誤:

Not found: Table myproject:mydataset.table was not found in location LOCATION

如要存取已刪除資料表的歷來資料,您必須從時間點還原資料表。

還原特定時間點的資料表

您可以將歷來資料複製到新資料表,藉此還原資料表。只要在時間回溯期內還原資料表,即使資料表已刪除或過期,仍可複製歷來資料。

從歷來資料還原資料表時,來源資料表的標記不會複製到目的地資料表。資料表分區資訊也不會複製到目的地資料表。如要重新建立原始資料表的分區配置,您可以在 Cloud Logging 中查看初始資料表建立要求,並使用該資訊對還原的資料表進行分區。

您只能還原整個資料表,無法還原個別分區。例如,不支援使用分區裝飾器 (例如 bq cp mydataset.mytable$20260712@1418864998000 mydataset.mytable_restored) 複製分區。

如要還原已刪除但仍在時光旅行時間範圍內的資料表,可以使用 @<time> 時間修飾符,將資料表複製到新資料表。即使使用時間修飾符,也無法查詢已刪除的資料表。必須先還原。

請使用下列語法搭配 @<time> 時間修飾符:

  • tableid@TIME,其中 TIME 是自 Unix 紀元起算的毫秒數。
  • tableid@-TIME_OFFSET,其中 TIME_OFFSET 是與目前時間的相對偏移量,以毫秒為單位。
  • tableid@0:指定可用的最早歷來資料。

如要還原資料表,請選取下列其中一個選項:

控制台

您無法使用 Cloud de Confiance 控制台取消刪除資料表。

bq

  1. 在 Cloud de Confiance 控制台啟用 Cloud Shell。

    啟用 Cloud Shell

    Cloud de Confiance 控制台底部會開啟 Cloud Shell 工作階段,並顯示指令列提示。Cloud Shell 是已安裝 Google Cloud CLI 的殼層環境,並已設定適用於您目前專案的值。工作階段可能要幾秒鐘的時間才能初始化。

  2. 如要還原資料表,請先判斷資料表存在的 UNIX 時間戳記 (以毫秒為單位)。您可以使用 Linux date 指令,從一般時間戳記值產生 Unix 時間戳記:

    date -d '2023-08-04 16:00:34.456789Z' +%s000
    
  3. 接著,使用 bq copy 指令搭配 @<time> 時光旅行裝飾器,執行資料表複製作業。

    例如,輸入下列指令,將 1418864998000 時間點的 mydataset.mytable 資料表複製到新資料表 mydataset.newtable。

    bq cp mydataset.mytable@1418864998000 mydataset.newtable
    

    (選用) 提供 --location 旗標,並將值設為您的位置。

    您也可以指定相對偏移量。以下範例會複製一小時前的資料表版本:

    bq cp mydataset.mytable@-3600000 mydataset.newtable
    

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

執行程式碼範例前,請將 GOOGLE_CLOUD_UNIVERSE_DOMAIN 環境變數設為 s3nsapis.fr。

import (
	"context"
	"fmt"
	"time"

	"cloud.google.com/go/bigquery"
)

// deleteAndUndeleteTable demonstrates how to recover a deleted table by copying it from a point in time
// that predates the deletion event.
func deleteAndUndeleteTable(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	ds := client.Dataset(datasetID)
	if _, err := ds.Table(tableID).Metadata(ctx); err != nil {
		return err
	}
	// Record the current time.  We'll use this as the snapshot time
	// for recovering the table.
	snapTime := time.Now()

	// "Accidentally" delete the table.
	if err := client.Dataset(datasetID).Table(tableID).Delete(ctx); err != nil {
		return err
	}

	// Construct the restore-from tableID using a snapshot decorator.
	snapshotTableID := fmt.Sprintf("%s@%d", tableID, snapTime.UnixNano()/1e6)
	// Choose a new table ID for the recovered table data.
	recoverTableID := fmt.Sprintf("%s_recovered", tableID)

	// Construct and run a copy job.
	copier := ds.Table(recoverTableID).CopierFrom(ds.Table(snapshotTableID))
	copier.WriteDisposition = bigquery.WriteTruncate
	job, err := copier.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}

	ds.Table(recoverTableID).Delete(ctx)
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

執行程式碼範例前,請將 GOOGLE_CLOUD_UNIVERSE_DOMAIN 環境變數設為 s3nsapis.fr。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.CopyJobConfiguration;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.TableId;

// Sample to undeleting a table
public class UndeleteTable {

  public static void runUndeleteTable() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_TABLE";
    String recoverTableName = "MY_RECOVER_TABLE_TABLE";
    undeleteTable(datasetName, tableName, recoverTableName);
  }

  public static void undeleteTable(String datasetName, String tableName, String recoverTableName) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      // "Accidentally" delete the table.
      bigquery.delete(TableId.of(datasetName, tableName));

      // Record the current time.  We'll use this as the snapshot time
      // for recovering the table.
      long snapTime = System.currentTimeMillis();

      // Construct the restore-from tableID using a snapshot decorator.
      String snapshotTableId = String.format("%s@%d", tableName, snapTime);

      // Construct and run a copy job.
      CopyJobConfiguration configuration =
          CopyJobConfiguration.newBuilder(
                  // Choose a new table ID for the recovered table data.
                  TableId.of(datasetName, recoverTableName),
                  TableId.of(datasetName, snapshotTableId))
              .build();

      Job job = bigquery.create(JobInfo.of(configuration));
      job = job.waitFor();
      if (job.isDone() && job.getStatus().getError() == null) {
        System.out.println("Undelete table recovered successfully.");
      } else {
        System.out.println(
            "BigQuery was unable to copy the table due to an error: \n"
                + job.getStatus().getError());
        return;
      }
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Table not found. \n" + e.toString());
    }
  }
}

Node.js

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Node.js 設定操作說明進行操作。詳情請參閱 BigQuery Node.js API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

執行程式碼範例前,請將 GOOGLE_CLOUD_UNIVERSE_DOMAIN 環境變數設為 s3nsapis.fr。

// Import the Google Cloud client library
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function undeleteTable() {
  // Undeletes "my_table_to_undelete" from "my_dataset".

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const datasetId = "my_dataset";
  // const tableId = "my_table_to_undelete";
  // const recoveredTableId = "my_recovered_table";

  /**
   * TODO(developer): Choose an appropriate snapshot point as epoch milliseconds.
   * For this example, we choose the current time as we're about to delete the
   * table immediately afterwards.
   */
  const snapshotEpoch = Date.now();

  // Delete the table
  await bigquery
    .dataset(datasetId)
    .table(tableId)
    .delete();

  console.log(`Table ${tableId} deleted.`);

  // Construct the restore-from table ID using a snapshot decorator.
  const snapshotTableId = `${tableId}@${snapshotEpoch}`;

  // Construct and run a copy job.
  await bigquery
    .dataset(datasetId)
    .table(snapshotTableId)
    .copy(bigquery.dataset(datasetId).table(recoveredTableId));

  console.log(
    `Copied data from deleted table ${tableId} to ${recoveredTableId}`
  );
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

執行程式碼範例前,請將 GOOGLE_CLOUD_UNIVERSE_DOMAIN 環境變數設為 s3nsapis.fr。

import time

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Choose a table to recover.
# table_id = "your-project.your_dataset.your_table"

# TODO(developer): Choose a new table ID for the recovered table data.
# recovered_table_id = "your-project.your_dataset.your_table_recovered"

# TODO(developer): Choose an appropriate snapshot point as epoch
# milliseconds. For this example, we choose the current time as we're about
# to delete the table immediately afterwards.
snapshot_epoch = int(time.time() * 1000)

# ...

# "Accidentally" delete the table.
client.delete_table(table_id)  # Make an API request.

# Construct the restore-from table ID using a snapshot decorator.
snapshot_table_id = "{}@{}".format(table_id, snapshot_epoch)

# Construct and run a copy job.
job = client.copy_table(
    snapshot_table_id,
    recovered_table_id,
    # Must match the source and destination tables location.
    location="US",
)  # Make an API request.

job.result()  # Wait for the job to complete.

print(
    "Copied data from deleted table {} to {}".format(table_id, recovered_table_id)
)

如果您預期可能需要在時光旅行期間允許的時間之後還原資料表,請建立資料表的資料表快照。詳情請參閱「資料表快照簡介」。

您無法直接還原邏輯檢視表。詳情請參閱「還原檢視表」。

後續步驟