משך החיים של פעולות קריאה וכתיבה ב-Spanner

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

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

במסמך הזה אנחנו מתארים בפירוט איך פעולות כתיבה וקריאה פועלות ב-Spanner ואיך Spanner מבטיח עקביות חזקה.

נקודות להתחלת שיחה

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

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

‫Spanner מספק גם עסקאות לקריאה בלבד וגם עסקאות לקריאה ולכתיבה. האחרונים הם סוג העסקה המועדף לפעולות (כולל הצהרות SQL SELECT) שלא משנות את הנתונים. טרנזקציות לקריאה בלבד עדיין מספקות עקביות חזקה ופועלות, כברירת מחדל, על העותק העדכני ביותר של הנתונים. אבל הן יכולות לפעול בלי צורך בכל סוג של נעילה פנימית, ולכן הן מהירות יותר וניתנות להרחבה בקלות רבה יותר. עסקאות עם הרשאת קריאה וכתיבה משמשות לעסקאות שמוסיפות, מעדכנות או מוחקות נתונים. זה כולל עסקאות שקוראות נתונים ואז כותבות אותם. הן עדיין ניתנות להרחבה, אבל טרנזקציות של קריאה וכתיבה יוצרות נעילה וצריכות להיות מנוהלות על ידי מנהיגי Paxos. הערה: הנעילה שקופה ללקוחות Spanner.

רמות בידוד של טרנזקציות

‫Spanner תומך בשתי רמות בידוד לעסקאות במסד נתונים: ניתנות לסריאליזציה (ברירת המחדל) וקריאה חוזרת (מומלץ לרוב לפריסות ולהעברות חדשות).

התנהגות הנעילה לקריאה ולכתיבה תלויה בסוג העסקה וברמת הבידוד:

  • עסקאות לקריאה בלבד: אלה תמיד ללא נעילה, ללא קשר לרמת הבידוד. הם מבצעים קריאות מתוך snapshot עקבי של מסד הנתונים, כך שהם לא חוסמים כתיבות בו-זמניות ולא נחסמים על ידי כתיבות כאלה.
  • טרנזקציות של קריאה וכתיבה בבידוד ניתן לסדר: Spanner משתמש בבקרת בו-זמניות פסימית. במהלך הביצוע, עסקאות רוכשות באופן יזום נעילות קריאה משותפות, שמונעות מעסקאות מקבילות לכתוב לאותם נתונים.
  • טרנזקציות של קריאה וכתיבה בבידוד קריאה חוזרת: Spanner משתמש כברירת מחדל בבקרת מקביליות אופטימית. פעולות קריאה לא מקבלות נעילות ופועלות על תמונת מצב עקבית. כך נמנעת חסימת קריאה-כתיבה במהלך הביצוע. אם זיהוי העימותים מזהה שעסקאות בו-זמניות שינו את הנתונים שנכתבו מאז חותמת הזמן של תמונת המצב, העסקה מבוטלת בזמן השמירה (השומר הראשון מנצח). אפשרות לניהול אופטימי של בקרת בו-זמניות זמינה בקריאה חוזרת אם משתמשים ברמזים לנעילה (כמו SELECT ... FOR UPDATE), שמקבלים נעילות בלעדיות במהלך ההרצה.

מערכות רבות של מסדי נתונים מבוזרים בעבר בחרו שלא לספק ערבויות חזקות לעקביות, בגלל התקשורת היקרה בין המכונות שנדרשת בדרך כלל. ‫Spanner יכול לספק תמונות מצב עקביות מאוד בכל מסד הנתונים באמצעות טכנולוגיה שפותחה על ידי Google שנקראת TrueTime. בדומה ל-Flux Capacitor במכונת זמן משנת 1985,‏ TrueTime הוא מה שמאפשר את Spanner. זהו API שמאפשר לכל מכונה במרכזי הנתונים של Google לדעת את השעה הגלובלית המדויקת ברמת דיוק גבוהה (כלומר, בתוך כמה אלפיות שנייה). כך מכונות שונות של Spanner יכולות להסיק לגבי הסדר של פעולות טרנזקציונליות (ולוודא שהסדר הזה תואם למה שהלקוח ראה), לרוב בלי תקשורת בכלל. כדי ש-TrueTime תפעל, Google הייתה צריכה לצייד את מרכזי הנתונים שלה בחומרה מיוחדת (שעונים אטומיים!). הדיוק והרזולוציה של הזמן שמתקבלים גבוהים בהרבה מאלה שאפשר להשיג באמצעות פרוטוקולים אחרים (כמו NTP). בפרט, Spanner מקצה חותמת זמן לכל פעולות הקריאה והכתיבה. עסקה בחותמת הזמן T1 משקפת את התוצאות של כל פעולות הכתיבה שהתרחשו לפני T1. אם מכונה רוצה לבצע קריאה ב-T2, היא צריכה לוודא שהתצוגה שלה של הנתונים עדכנית לפחות עד T2. בגלל TrueTime, ההערכה הזו בדרך כלל זולה מאוד. הפרוטוקולים להבטחת עקביות הנתונים הם מורכבים, אבל הם מוסברים יותר במאמר המקורי על Spanner ובמאמר הזה על Spanner ועקביות.

דוגמה מעשית

כדי להבין איך זה עובד, נבחן כמה דוגמאות מעשיות:

CREATE TABLE ExampleTable (
 Id INT64 NOT NULL,
 Value STRING(MAX),
) PRIMARY KEY(Id);

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

פיצול KeyRange
0 ‫[-∞,3)
1 [3,224)
2 ‫[224,712)
3 ‫[712,717)
4 [717,1265)
5 ‫[1265,1724)
6 [1724,1997)
7 [1997,2456)
8 ‫[2456,∞)

בהינתן הסכימה של ExampleTable שמופיעה למעלה, מרחב המפתחות הראשי מחולק לפצל. לדוגמה: אם יש שורה ב-ExampleTable עם Id של 3700, היא תופיע בפיצול 8. כפי שמפורט למעלה, Split 8 עצמו משוכפל בכמה מכונות.

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

בדוגמה הזו של מופע Spanner, ללקוח יש חמישה צמתים, והמופע משוכפל בשלושה אזורים. תשעת הפיצולים ממוספרים מ-0 עד 8, והמנהיגים של Paxos בכל פיצול מוצגים עם הצללה כהה. לפיצולים יש גם עותקים בכל אזור (מוצללים קלות). החלוקה של הפיצולים בין הצמתים עשויה להיות שונה בכל אזור, והמובילים של Paxos לא נמצאים כולם באותו אזור. הגמישות הזו עוזרת ל-Spanner להיות חזק יותר בפרופילים מסוימים של עומסי עבודה ובמצבי כשל מסוימים.

כתיבה עם פיצול יחיד

נניח שהלקוח רוצה להוסיף שורה חדשה (7, "Seven") ל-ExampleTable.

  1. שכבת ה-API מחפשת את הפיצול שבבעלותו טווח המפתחות שמכיל את 7. הוא נמצא בפיצול 1.
  2. שכבת ה-API שולחת את בקשת הכתיבה אל ה-Leader של Split 1.
  3. המוביל מתחיל טרנזקציה.
  4. הלידר מטפל בנעילה ובבקרת בו-זמניות על סמך רמת הבידוד:
    • בידוד ניתן לסדר (וקריאה חוזרת עם נעילה פסימית): הלידר מנסה לקבל נעילת כתיבה בשורה Id=7. זו פעולה מקומית. אם עסקה מקבילה אחרת קוראת את השורה הזו (מחזיקה בנעילת קריאה משותפת) או כותבת אותה, העסקה הנוכחית נחסמת עד שהיא יכולה לקבל את נעילת הכתיבה.
      1. יכול להיות שעסקה א' ממתינה לנעילה שמוחזקת על ידי עסקה ב', ועסקה ב' ממתינה לנעילה שמוחזקת על ידי עסקה א'. מכיוון שאף אחת מהעסקאות לא משחררת את הנעילות עד שהיא מקבלת את כל הנעילות, זה יכול להוביל למבוי סתום. ‫Spanner משתמש באלגוריתם סטנדרטי למניעת מצב של קיפאון (deadlock) מסוג wound-wait כדי לוודא שהעסקאות מתקדמות. עסקה 'חדשה' יותר מחכה לנעילה שמוחזקת על ידי עסקה 'ישנה' יותר, אבל עסקה 'ישנה' יותר 'פוגעת' (מבטלת) בעסקה 'חדשה' יותר שמחזיקה נעילה שהתבקשה על ידי העסקה הישנה יותר. לכן, ב-Spanner אין מחזורי קיפאון של תהליכים שממתינים לנעילה.
    • בידוד קריאה חוזרת (נעילה אופטימית כברירת מחדל): העסקה לא מקבלת נעילות במהלך שלב הביצוע. במקום זאת, התנגשויות כתיבה בו-זמניות נבדקות ומאומתות בזמן השמירה. מכיוון שעסקאות בשיטת'קריאה חוזרת' (נעילה אופטימית) לא חוסמות נעילות במהלך הביצוע, הן לא יכולות להגיע למצב של חסימה הדדית בשלב הזה.
  5. אחרי שהנעילה מתבצעת, ה-Leader מקצה חותמת זמן לעסקה על סמך TrueTime.
    1. חותמת הזמן הזו מובטחת להיות גדולה יותר מזו של כל טרנזקציה קודמת שנשמרה ונגעה בנתונים. כך מוודאים שסדר העסקאות (כפי שהוא נתפס על ידי הלקוח) תואם לסדר השינויים בנתונים.
  6. הלידר מודיע על העסקה ועל חותמת הזמן שלה לכל העותקים של Split 1. אחרי שרוב העותקים האלה מאחסנים את מוטציית העסקה באחסון יציב (במערכת הקבצים המבוזרת), העסקה מתבצעת. כך מוודאים שאפשר לשחזר את העסקה, גם אם יש כשל במספר קטן של מכונות. (הרפליקות עדיין לא מחילות את המוטציות על העותק שלהן של הנתונים).
  7. הלידר ממתין עד שהוא יכול להיות בטוח שחותמת הזמן של העסקה חלפה בזמן אמת. בדרך כלל נדרשות כמה אלפיות שנייה כדי שנוכל להמתין לכל אי ודאות בחותמת הזמן של TrueTime. כך מובטחת עקביות חזקה – אחרי שלקוח למד את התוצאה של טרנזקציה, מובטח שכל הקוראים האחרים יראו את ההשפעות של הטרנזקציה. הזמן שנדרש לביצוע פעולת ה-commit בדרך כלל חופף לזמן התקשורת עם העותק בשלב הקודם, ולכן העלות בפועל של זמן האחזור היא מינימלית. פרטים נוספים מופיעים במאמר.

  8. המוביל משיב ללקוח שהעסקה בוצעה, ויכול גם לדווח על חותמת הזמן של ביצוע העסקה.

  9. במקביל למענה ללקוח, המערכת מחילה את השינויים בעסקה על הנתונים.

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

כל התהליך הזה קורה בדרך כלל תוך כמה אלפיות שנייה. הכתיבה הזו היא הכתיבה הכי זולה שמתבצעת על ידי Spanner, כי היא כוללת פיצול אחד.

כתיבה מפוצלת

אם יש כמה פיצולים, נדרש תיאום נוסף (באמצעות אלגוריתם האישור דו-שלבי הרגיל).

נניח שהטבלה מכילה ארבעת אלפים שורות:

1 "one"
2 ‫"two"
... ...
4000 ‪"four thousand"

נניח שהלקוח רוצה לקרוא את הערך בשורה 1000 ולכתוב ערך בשורות 2000, 3000 ו-4000 בתוך טרנזקציה. הפעולה הזו תתבצע במסגרת טרנזקציה של קריאה וכתיבה באופן הבא:

  1. הלקוח מתחיל עסקה של קריאה וכתיבה, t.
  2. הלקוח שולח בקשת קריאה לשורה 1000 לשכבת ה-API ומסמן אותה כחלק מ-t.
  3. שכבת ה-API מחפשת את הפיצול שבבעלותו המפתח 1000. הוא נמצא בפיצול 4.
  4. שכבת ה-API שולחת בקשת קריאה ל-Leader של Split 4 ומתייגת אותה כחלק מ-t.

  5. הלידר של Split 4 מטפל בקריאות על סמך רמת הבידוד:

    • בידוד ניתן לסדר (וקריאה חוזרת עם נעילה פסימית): הלידר מנסה לקבל נעילת קריאה בשורה Id=1000. זו פעולה מקומית. אם לעסקה מקבילה אחרת יש נעילת כתיבה בשורה הזו, העסקה הנוכחית נחסמת עד שהיא יכולה לקבל את הנעילה. עם זאת, נעילת הקריאה הזו לא מונעת מעסקאות אחרות לקבל נעילות קריאה. כדי למנוע מצב של קיפאון, נעשה שימוש בשיטה wound-wait.
    • בידוד קריאה חוזרת (נעילה אופטימית כברירת מחדל): קריאות מתבצעות ללא נעילה ופועלות על תמונת מצב. העסקה לא מקבלת נעילת קריאה ולא נחסמת על ידי נעילות כתיבה מקבילות במהלך הביצוע.
  6. המוביל מחפש את הערך של Id 1000 ("One Thousand") ומחזיר את תוצאת הקריאה ללקוח.


    מאוחר יותר...

  7. הלקוח שולח בקשת אישור לעסקה t. בקשת השמירה הזו מכילה 3 שינויים: ([2000, "Dos Mil"],‏ [3000, "Tres Mil"] ו-[4000, "Quatro Mil"]).

    1. כל הפיצולים שקשורים לטרנזקציה הופכים למשתתפים בטרנזקציה. במקרה הזה, המשתתפים הם פיצול 4 (שמטפל בקריאה של מפתח 1000), פיצול 7 (שיטפל בשינוי של מפתח 2000) ופיצול 8 (שיטפל בשינויים של מפתח 3000 ומפתח 4000).
  8. אחד המשתתפים הופך להיות המתאם. במקרה כזה, יכול להיות שהמוביל של Split 7 יהפוך לרכז. תפקיד המתאם הוא לוודא שהטרנזקציה מתבצעת או מבוטלת באופן אטומי אצל כל המשתתפים. כלומר, לא יקרה מצב שבו הפעולה תתבצע אצל משתתף אחד ותבוטל אצל משתתף אחר.

    1. העבודה שמתבצעת על ידי המשתתפים והמתאמים מתבצעת בפועל על ידי המכונות המובילות של הפיצולים האלה.
  9. המשתתפים מקבלים נעילות. (זה השלב הראשון בתהליך אישור דו-שלבי).

    1. תהליך הפיצול 7 מקבל נעילת כתיבה על המפתח 2000.
    2. תהליך הפיצול 8 מקבל נעילת כתיבה על מפתח 3000 ומפתח 4000.
    3. בבידוד ניתן לסדר, Split 4 מוודא שהוא עדיין מחזיק נעילת קריאה במפתח 1000 (כלומר, הנעילה לא אבדה בגלל קריסת מכונה או האלגוריתם wound-wait). בבידוד של קריאה חוזרת (נעילה אופטימית), קריאות רגילות לא מקבלות נעילות, ולכן לא צריך לאמת נעילת קריאה ב-Split 4. במקום זאת, המשתתפים מאמתים שהשינויים לא סותרים עסקאות מקבילות (המשתתף הראשון שמאשר את השינוי הוא זה שזוכה). הם בודקים שהמפתחות שנכתבו לא השתנו מאז חותמת הזמן של תמונת המצב של העסקה. אם משתמשים ברמז נעילה (כמו SELECT ... FOR UPDATE) במפתח הקריאה 1000, ‏ Split 4 גם מקבל נעילה ומוודא שהוא לא השתנה.
    4. כל משתתף מפצל את הרשומות, נועל אותן ומאמת את התוצאות ביומן Paxos שלו. כך אפשר לוודא שמצב העסקה נשאר ללא שינוי גם אם השרת נכשל.
    5. אם כל המשתתפים מודיעים בהצלחה לתיאום שהנעילות שלהם מוחזקות והאימותים עברו, העסקה מתבצעת. הפעולה הזו יוצרת נקודת אישור כדי לסדר את ההשפעות שלה ביחס לעסקאות אחרות.
    6. אם אי אפשר לקבל נעילות או אם מזוהה סתירה באימות קריאה שניתן לחזור עליה, העסקה מבוטלת.
  10. אם כל המשתתפים והמתאם מקבלים נעילות בהצלחה, המתאם (פיצול 7) מחליט לבצע את העסקה. הוא מקצה חותמת זמן לעסקה על סמך TrueTime.

    1. ההחלטה הזו לגבי הוספת השינויים, וגם השינויים במפתח 2000, משוכפלים לחברים בפיצול 7. אחרי שרוב העותקים של Split 7 מתעדים את החלטת האישור באחסון יציב, העסקה מאושרת.
  11. המתאם מעביר את תוצאת העסקה לכל המשתתפים. (זה השלב השני של אישור דו-שלבי).

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

    1. כמו במקרה של פיצול יחיד, קוראים עוקבים של נתונים ב-Coordinator או ב-Participants צריכים לחכות עד שהנתונים יוחלו.
  13. המוביל של המתאם משיב ללקוח שהעסקה בוצעה, ויכול להיות שהוא יחזיר את חותמת הזמן של ביצוע העסקה

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

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

קריאה חזקה (פיצול מרובה)

נניח שהלקוח רוצה לקרוא את כל השורות שבהן Id >= 0 ו-Id < 700 כחלק מעסקה לקריאה בלבד.

  1. שכבת ה-API מחפשת את הפיצולים שכוללים מפתחות בטווח [0, 700). השורות האלה שייכות לפיצול 0, פיצול 1 ופיצול 2.
  2. מכיוון שמדובר בקריאה חזקה בכמה מכונות, שכבת ה-API בוחרת את חותמת הזמן של הקריאה באמצעות TrueTime הנוכחי. כך מוודאים ששתי הפעולות יחזירו נתונים מאותה תמונת מצב של מסד הנתונים.
    1. גם סוגים אחרים של קריאות, כמו קריאות בעבר, בוחרים חותמת זמן לקריאה (אבל חותמת הזמן עשויה להיות מהעבר).
  3. שכבת ה-API שולחת את בקשת הקריאה לכמה עותקים של Split 0, לכמה עותקים של Split 1 ולכמה עותקים של Split 2. הוא כולל גם את חותמת הזמן של הקריאה שנבחרה בשלב הקודם.
  4. במקרה של קריאות חזקות, העותק המשוכפל שמשרת את הבקשה בדרך כלל שולח RPC למוביל כדי לבקש את חותמת הזמן של העסקה האחרונה שהוא צריך להחיל, והקריאה יכולה להתבצע אחרי שהעסקה הזו מוחלת. אם העותק הוא הליבה או שהוא קובע שהוא מספיק מעודכן כדי להגיב לבקשה מהמצב הפנימי שלו ומ-TrueTime, הוא מגיב ישירות לבקשת הקריאה.

  5. התוצאות מהרפליקות משולבות ומוחזרות ללקוח (דרך שכבת ה-API).

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

סיכום

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