השוואה בין DML לבין Mutations

שפת טיפול בנתונים (DML) ומוטציות הם שני ממשקי API ב-Spanner שבהם אתם יכולים להשתמש כדי לשנות נתונים. כל אחת מהן מציעה תכונות דומות לטיפול בנתונים. בדף הזה אנחנו משווים בין שתי הגישות.

מהי שפת טיפול בנתונים (DML)?

שפת הטיפול בנתונים (DML) ב-Spanner מאפשרת לכם לטפל בנתונים בטבלאות של מסד הנתונים באמצעות ההצהרות INSERT,‏ UPDATE ו-DELETE. אפשר להריץ פקודות DML באמצעות ספריות הלקוח, מסוףGoogle Cloud ו-gcloud spanner.

ב-Spanner יש שתי הטמעות של הפעלת DML, שלכל אחת מהן יש מאפיינים שונים.

  • ‫DML רגיל – מתאים לעומסי עבודה רגילים של עיבוד עסקאות אונליין (OLTP).

    למידע נוסף, כולל דוגמאות קוד, קראו את המאמר שימוש ב-DML.

  • Partitioned DML – מיועד לעדכון מספר פריטים בו-זמנית ולמחיקות בכמות גדולה, כמו בדוגמאות הבאות.

    • ניקיון תקופתי ואיסוף אשפה. לדוגמה, מחיקת שורות ישנות או הגדרת עמודות כ-NULL.

    • מילוי עמודות חדשות בערכי ברירת מחדל. לדוגמה, אפשר להשתמש בהצהרת UPDATE כדי להגדיר את הערך של עמודה חדשה כ-False במקום שבו הוא NULL.

    למידע נוסף, כולל דוגמאות קוד, קראו את המאמר שימוש ב-DML מחולק למחיצות.

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

מהן מוטציות?

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

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

השוואת תכונות בין DML למוטציות

בטבלה הבאה מפורט סיכום של התמיכה ב-DML ובמוטציות של פעולות ותכונות נפוצות במסדי נתונים.

תפעול DML מוטציות
הוספת נתונים נתמך נתמך
מחיקת נתונים נתמך נתמך
עדכון הנתונים נתמך נתמך
הוספה או התעלמות מהנתונים נתמך לא נתמך
Read Your Writes (RYW) נתמך לא נתמך
הוספה או עדכון של נתונים (Upsert) נתמך נתמך
תחביר SQL נתמך לא נתמך
בדיקת אילוצים אחרי כל דף חשבון בזמן השמירה
אכיפת מגבלת המוטציות (כולל אינדקסים) לכל הצהרה (מתאפס אחרי ההפעלה) לכל התחייבות (מצטבר)

יש הבדלים בין DML למוטציות בתמיכה בתכונות הבאות:

  • ‫Read Your Writes: קריאת תוצאות שלא בוצע להן commit בטרנזקציה פעילה. השינויים שאתם מבצעים באמצעות פקודות DML גלויים לפקודות הבאות באותה עסקה. זה שונה משימוש במוטציות, שבהן השינויים לא גלויים בקריאות (כולל קריאות שבוצעו באותה טרנזקציה) עד שהטרנזקציה מתבצעת. הסיבה לכך היא שהמוטציות בטרנזקציה נשמרות במאגר נתונים זמני בצד הלקוח (באופן מקומי) ונשלחות לשרת כחלק מפעולת ה-commit. כתוצאה מכך, שינויים בבקשת ה-commit לא גלויים להצהרות SQL או פקודות DML באותה עסקה.

  • בדיקת אילוצים: Spanner בודק את האילוצים אחרי כל פקודת DML. זה שונה משימוש במוטציות, שבהן Spanner מאחסן מוטציות במאגר נתונים זמני בלקוח עד לביצוע ה-commit, ובודק את האילוצים בזמן ה-commit. הערכת האילוצים אחרי כל פקודת DML מאפשרת ל-Spanner להבטיח שהנתונים שיוחזרו על ידי שאילתה עוקבת באותה עסקה יהיו עקביים עם הסכימה.

  • אכיפת מגבלת המוטציות: ב-DML, מגבלת המוטציות של 80,000 (כולל אינדקסים) חלה על כל הצהרה ומתאפסת אחרי שההצהרה מופעלת. כך אפשר לבצע כמה פקודות DML בטרנזקציה אחת, שביחד חורגות מ-80,000 מוטציות, כל עוד אף פקודה לא חורגת מהמגבלה. ב-Mutation API, שאפשר להשתמש בו רק כחלק מפעולת ה-commit, המגבלה של 80,000 מוטציות (כולל אינדקסים) היא מצטברת בכל המוטציות ב-commit. שתי השיטות כפופות למגבלת גודל העסקה של ‎100MiB.

  • תחביר SQL: שפת DML מספקת דרך מקובלת לטפל בנתונים. אתם יכולים להשתמש במיומנויות SQL כדי לשנות את הנתונים באמצעות DML API.

שיטה מומלצת – הימנעו משילוב של DML ומוטציה באותה עסקה

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

הדוגמה הבאה ב-Java ממחישה התנהגות שעלולה להפתיע. הקוד מוסיף שתי שורות ל-Albums באמצעות Mutation API. לאחר מכן, קטע הקוד קורא ל-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 לא גלויים להצהרות הבאות עד שהטרנזקציה מתבצעת. באופן אידיאלי, כתיבות במאגר נתונים זמני צריכות להתבצע רק בסוף העסקה.

מה השלב הבא?