שחזור טבלאות שנמחקו

במאמר הזה מוסבר איך לשחזר (או לבטל מחיקה) של טבלה שנמחקה ב-BigQuery. אפשר לשחזר טבלה שנמחקה במסגרת חלון הזמן של Time Travel שצוין עבור מערך הנתונים, כולל מחיקות מפורשות ומחיקות מרומזות עקב תפוגה של הטבלה. אפשר גם להגדיר את חלון הזמן של ההיסטוריה.

למידע על שחזור של קבוצת נתונים או תמונת מצב שלמה שנמחקו, אפשר לעיין במקורות המידע הבאים:

משך חלון הזמן של המבט לאחור יכול להיות בין יומיים לשבעה ימים. אחרי שחלון הזמן של Time Travel מסתיים, BigQuery מספק תקופת ביטול מחיקה שבה הנתונים שנמחקו נשמרים אוטומטית למשך שבעה ימים נוספים. אחרי שתקופת ההגנה מסתיימת, אי אפשר לשחזר טבלה באף שיטה, כולל פתיחת כרטיס תמיכה.

לפני שמתחילים

חשוב לוודא שיש לכם את ההרשאות הנדרשות לניהול זהויות והרשאות גישה (IAM) כדי לשחזר טבלה שנמחקה.

התפקידים הנדרשים

כדי לקבל את ההרשאות שנדרשות לשחזור טבלה שנמחקה, צריך לבקש מהאדמין להקצות לכם את תפקיד ה-IAM‏ BigQuery User (roles/bigquery.user) בפרויקט. כדי לקרוא הסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

שחזור טבלה

אפשר לשחזר טבלה מנתונים היסטוריים על ידי העתקת הנתונים ההיסטוריים לטבלה חדשה. העתקת נתונים היסטוריים פועלת גם אם הטבלה נמחקה או שתוקפה פג, כל עוד משחזרים את הטבלה במהלך חלון הזמן של 'מסע בזמן'.

כשמשחזרים טבלה מנתונים היסטוריים, תגים מטבלת המקור לא מועתקים לטבלת היעד. גם מידע על חלוקת הטבלה לא מועתק לטבלת היעד. כדי ליצור מחדש את סכימת החלוקה למחיצות של הטבלה המקורית, אפשר לעיין בבקשה הראשונית ליצירת הטבלה ב-Cloud Logging ולהשתמש במידע הזה כדי לחלק את הטבלה המשוחזרת למחיצות.

כדי לשחזר טבלה שנמחקה אבל עדיין נמצאת בחלון הזמן של Time Travel, מעתיקים את הטבלה לטבלה חדשה באמצעות הכלי @<time> Time Decorator. אי אפשר להריץ שאילתה על טבלה שנמחקה, גם אם משתמשים במעצב זמן. קודם צריך לשחזר אותו.

משתמשים בתחביר הבא עם @<time> מעצב הזמן:

  • tableid@TIME כאשר TIME הוא מספר אלפיות השנייה מאז ראשית זמן יוניקס (Unix epoch).
  • 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 היא סביבת מעטפת שבה ה-CLI של Google Cloud מותקן ומוגדרים ערכים לפרויקט הקיים. הסשן יופעל תוך כמה שניות.

  2. כדי לשחזר טבלה, קודם צריך לקבוע חותמת זמן של מערכת UNIX שבה הטבלה הייתה קיימת (באלפיות השנייה). אפשר להשתמש בפקודה date של Linux כדי ליצור את חותמת הזמן של מערכת Unix מערך רגיל של חותמת זמן:

    date -d '2023-08-04 16:00:34.456789Z' +%s000
    
  3. לאחר מכן, משתמשים בפקודה bq copy עם קישוט ה-time travel‏ @<time> כדי לבצע את פעולת העתקת הטבלה.

    לדוגמה, מזינים את הפקודה הבאה כדי להעתיק את הטבלה mydataset.mytable בשעה 1418864998000 לטבלה חדשה mydataset.newtable.

    bq cp mydataset.mytable@1418864998000 mydataset.newtable
    

    (אופציונלי) מציינים את הדגל --location ומגדירים את הערך למיקום.

    אפשר גם לציין היסט יחסי. בדוגמה הבאה מועתקת הגרסה של טבלה מלפני שעה:

    bq cp mydataset.mytable@-3600000 mydataset.newtable
    

המשך

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Goהוראות ההגדרה שבמדריך למתחילים של BigQuery באמצעות ספריות לקוח. מידע נוסף מופיע במאמרי העזרה של BigQuery Go API.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

לפני שמריצים דוגמאות קוד, צריך להגדיר את משתנה הסביבה 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

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Javaהוראות ההגדרה שבמדריך למתחילים של BigQuery באמצעות ספריות לקוח. מידע נוסף מופיע במאמרי העזרה של BigQuery Java API.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

לפני שמריצים דוגמאות קוד, צריך להגדיר את משתנה הסביבה 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

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Node.jsהוראות ההגדרה שבמדריך למתחילים של BigQuery באמצעות ספריות לקוח. מידע נוסף מופיע במאמרי העזרה של BigQuery Node.js API.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

לפני שמריצים דוגמאות קוד, צריך להגדיר את משתנה הסביבה 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

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Pythonהוראות ההגדרה שבמדריך למתחילים של BigQuery באמצעות ספריות לקוח. מידע נוסף מופיע במאמרי העזרה של BigQuery Python API.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

לפני שמריצים דוגמאות קוד, צריך להגדיר את משתנה הסביבה 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)
)

אם אתם חושבים שאולי תרצו לשחזר טבלה מאוחר יותר ממה שמותר בחלון הזמן של Time Travel, כדאי ליצור תמונת מצב של הטבלה. מידע נוסף זמין במאמר מבוא לצילומי מצב של טבלאות.

אי אפשר לשחזר תצוגה לוגית ישירות. מידע נוסף מופיע במאמר בנושא שחזור תצוגה.

איך מזהים מתי נמחקה טבלה

כדי לזהות את רשומת הביקורת שבה מוצג התאריך של התפוגה או המחיקה של טבלה מסוימת, משתמשים במסנן הבא ב-Logs Explorer במסוף Cloud de Confiance :

resource.type="bigquery_resource"
protoPayload.resourceName="projects/PROJECT_ID/datasets/DATASET_ID/tables/TABLE_ID"
(protoPayload.methodName="google.cloud.bigquery.v2.TableService.DeleteTable" OR protoPayload.methodName="tableservice.delete" OR protoPayload.serviceData.jobCompletedEvent.job.jobConfiguration.query.statementType="DROP_TABLE" OR protoPayload.methodName="InternalTableExpired")

מחליפים את מה שכתוב בשדות הבאים:

  • PROJECT_ID: מזהה הפרויקט.
  • DATASET_ID: המזהה של מערך הנתונים שהטבלה נכללת בו.
  • TABLE_ID: המזהה של הטבלה שנמחקה.

אפשר גם להשתמש במסנן הבא כדי למצוא את התאריך של התפוגה או המחיקה של מערך הנתונים שהכיל את הטבלה:

resource.type="bigquery_dataset"
protoPayload.resourceName="projects/PROJECT_ID/datasets/DATASET_ID"
(protoPayload.methodName="google.cloud.bigquery.v2.DatasetService.DeleteDataset" OR protoPayload.methodName="datasetservice.delete")

מחליפים את מה שכתוב בשדות הבאים:

  • PROJECT_ID: מזהה הפרויקט.
  • DATASET_ID: המזהה של מערך הנתונים שהטבלה נכללת בו.

זיהוי הסיבה למחיקת הטבלה

אפשר להשתמש בתצוגה INFORMATION_SCHEMA.TABLE_STORAGE כדי לראות איך טבלה נמחקה.

התצוגה INFORMATION_SCHEMA.TABLE_STORAGE מכילה מידע על טבלאות נוכחיות ועל טבלאות שנמחקו במסגרת הזמן של ההחזרה לנקודת זמן. אם טבלה נמחקה, העמודה table_deletion_time מכילה את חותמת הזמן של המחיקה, והעמודה table_deletion_reason מכילה את שיטת המחיקה.

כדי לגלות למה טבלה נמחקה, שולחים שאילתה לתצוגה INFORMATION_SCHEMA.TABLE_STORAGE:

SELECT
  table_name,
  deleted,
  table_deletion_time,
  table_deletion_reason
FROM
  `PROJECT_ID`.`region-REGION`.INFORMATION_SCHEMA.TABLE_STORAGE
WHERE
  table_schema = "DATASET_ID"
  AND table_name = "TABLE_ID"

מחליפים את המשתנים הבאים:

  • PROJECT_ID: מזהה הפרויקט.
  • REGION: האזור של מערך הנתונים שהכיל את הטבלה.
  • DATASET_ID: המזהה של מערך הנתונים שהטבלה נכללת בו.
  • TABLE_ID: המזהה של הטבלה שנמחקה.

בעמודה table_deletion_reason מוסבר למה הטבלה נמחקה:

  • TABLE_EXPIRATION: הטבלה נמחקה אחרי שחלף הזמן שנקבע לתפוגה.
  • DATASET_DELETION: מערך הנתונים שהטבלה הייתה שייכת אליו נמחק על ידי משתמש.
  • USER_DELETED: הטבלה נמחקה על ידי משתמש.

פתרון בעיות בשחזור טבלאות

שאילתה של הטבלה שנמחקה באמצעות חותמת זמן מהעבר

אי אפשר לשחזר נתונים מטבלה באמצעות שאילתה של טבלה שנמחקה בעבר באמצעות קישוט חותמת זמן או באמצעות FOR SYSTEM_TIME AS OF כדי לשמור את התוצאה בטבלת יעד. שימוש באחת מהשיטות האלה יוצר את השגיאה הבאה:

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

במקום זאת, כדי להעתיק את הטבלה, פועלים לפי השלבים במאמר שחזור טבלה.

שגיאה: VPC Service Controls: Request is prohibited by organization's policy

כשמנסים להריץ את פקודת ההעתקה מ-Google Cloud Shell, יכול להיות שתופיע שגיאה כמו זו שבהמשך:

BigQuery error in cp operation: VPC Service Controls: Request is prohibited by organization's policy

השימוש ב-Cloud Shell ממסוף Cloud de Confiance עם VPC SC לא נתמך, כי הוא נחשב לבקשה מחוץ לגבולות הגזרה של השירות, והגישה לנתונים שמוגנים על ידי VPC Service Controls נדחית. כדי לעקוף את הבעיה, מפעילים את Cloud Shell ומתחברים אליו באופן מקומי באמצעות Google Cloud CLI.

שגיאה: Latest categories are incompatible with schema

אם מריצים את פקודת ההעתקה מ-Google Cloud Shell, יכול להיות שתקבלו שגיאה כמו הבאה:

Latest categories are incompatible with schema at TIMESTAMP

יש כמה סיבות אפשריות לשגיאה הזו:

כדי לפתור את השגיאה:

  1. מוודאים שהסכימה של טבלת היעד זהה, ומוודאים שכל העמודות בטבלת המקור קיימות גם בטבלת היעד.
  2. מסירים תגי מדיניות ברמת העמודה מטבלת היעד שלא נמצאים בסכימה של הטבלה המקורית.

שגיאה: BigQuery error in cp operation: Invalid time travel timestamp

אם מריצים את הפקודה bq copy מ-Google Cloud Shell, יכול להיות שתופיע שגיאה כמו זו שבהמשך:

BigQuery error in cp operation: Invalid time travel timestamp 1744343690000 for
table PROJECT_ID:DATASET_ID.TABLE_ID@1744343690000.
Cannot read before 1744843691075

השגיאה הזו מציינת שאתם מנסים לשחזר נתונים ממצב הטבלה לפני חלון הזמן של Time Travel או לפני שהטבלה נוצרה. האפשרות הזו לא נתמכת. הודעת השגיאה מכילה את חותמת הזמן האחרונה שאפשר להשתמש בה כדי לקרוא את נתוני הטבלה. משתמשים בחותמת הזמן שמופיעה בשגיאה בפקודה bq copy.

השגיאה הזו יכולה להופיע גם כשמספקים ערך שלילי של חותמת זמן, לדוגמה, TABLE@-1744963620000. במקום זאת, צריך להשתמש בהזזת זמן שאפשר להשתמש בה עם הסימן -.

BigQuery error in cp operation: Invalid time travel timestamp 584878816 for
table PROJECT_ID:DATASET_ID.TABLE_ID@584878816.
Cannot read before 1744843691075

הודעת השגיאה הזו מציינת שהפקודה bq cp מכילה ערך חותמת זמן שלילי כהיסט, ושהניסיון לקרוא את הטבלה בוצע ב-CURRENT_TIMESTAMP - PROVIDED TIMESTAMP. הערך הזה הוא בדרך כלל חותמת זמן משנת 1970. כדי לעקוף את הבעיה הזו, צריך לאמת את ערכי ההיסט או חותמת הזמן כשמגדירים את ערך מעצב הטבלה, ולהשתמש בסימן - בצורה מתאימה.

תצוגות מהותיות

אי אפשר לשחזר תצוגה חומרית שנמחקה באופן ישיר. אם מוחקים תצוגה חומרית, צריך ליצור אותה מחדש.

אם מוחקים טבלה שהיא טבלת בסיס לתצוגה חומרית, אי אפשר יותר לבצע שאילתות או לרענן את התצוגה החומרית. אם משחזרים את טבלת הבסיס לפי השלבים שמפורטים במאמר שחזור טבלה, צריך גם ליצור מחדש את כל התצוגות החומריות שמשתמשות בטבלה הזו.

טבלאות חיצוניות

אי אפשר לשחזר טבלה חיצונית שנמחקה באופן ישיר. אם מוחקים טבלה חיצונית, צריך ליצור אותה מחדש. כדי ליצור מחדש את הטבלה, צריך לדעת את ההגדרה המקורית שלה, ובעיקר את הפרטים הבאים:

  • הסכימה של הטבלה
  • מקור ה-URI שמפנה לנתונים החיצוניים
  • הפורמט של הנתונים החיצוניים

אפשר לקבל את המידע הזה מ-Cloud Logging על ידי חיפוש של רשומת היומן של יצירת הטבלה. אם הטבלה נמחקה לאחרונה, אפשר גם לנסות לקבל את ה-URI באמצעות שאילתה של התצוגה INFORMATION_SCHEMA.TABLE_OPTIONS.

מחיקת הטבלה החיצונית לא מוחקת את הנתונים הבסיסיים.

המאמרים הבאים