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

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

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

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

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

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

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

  • Partitioned DML – מיועד לעדכונים ולמחיקות בכמות גדולה, כמו בדוגמאות הבאות.

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

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

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

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

מהן מוטציות?

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

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

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

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

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

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

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

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

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

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

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

אם טרנזקציה מכילה גם הצהרות DML וגם שינויים בבקשת האישור, 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 רשומות עודכנו. למה? הסיבה לכך היא שהשינויים שביצענו באמצעות Mutations לא גלויים להצהרות הבאות עד שהעסקה מתבצעת. באופן אידיאלי, צריך לבצע כתיבות בזיכרון המטמון רק בסוף העסקה.

מה השלב הבא?