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

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

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

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

הסבר על הרכיבים ברמה הגבוהה

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

רכיבים ברמה גבוהה

ערכות SDK, ספריות לקוח ודרייברים

‫Firestore תומך בערכות SDK, בספריות לקוח ובדרייברים לפלטפורמות שונות.

ממשק קצה של Google‏ (GFE)

זהו שירות תשתית שמשותף לכל Google Cloud השירותים. ה-GFE מקבל בקשות נכנסות ומעביר אותן לשירות Google הרלוונטי (שירות Firestore בהקשר הזה).

שירות Firestore

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

שכבת האחסון של Firestore

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

טווחים ופיצולים של מקשים

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

‫Firestore מחלק באופן אוטומטי את הנתונים באוספים בין כמה שרתי אחסון. המחיצות האלה נקראות פיצולים.

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

שכפול סינכרוני

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

אזור יחיד לעומת מספר אזורים

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

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

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

מידע נוסף על המיקומים של אזור מסוים זמין במאמר מיקומי Firestore.

אזור יחיד לעומת מספר אזורים

הסבר על מחזור החיים של פעולת כתיבה

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

לכל סוגי הכתיבה, Firestore מספק את מאפייני ACID (אטומיות, עקביות, בידוד ועמידות) של מסדי נתונים יחסיים. ב-Firestore יש גם serializability, כלומר כל העסקאות מוצגות כאילו הן בוצעו בסדר סדרתי.

שלבים כלליים בעסקת כתיבה

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

בשלב הראשון של טרנזקציה, Firestore קורא את המסמך הקיים וקובע את השינויים שיש לבצע בנתונים במסמך.

הפעולה הזו כוללת גם עדכונים של כל האינדקסים הרלוונטיים:

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

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

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

הסבר על עסקת כתיבה בשכבת האחסון

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

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

פיצול מסד נתונים ב-Firestore

נניח שיש מסד נתונים ב-Firestore עם אוסף Restaurants באופן הבא:

קולקציית מסעדות

הנהג מבקש לשנות את המסמך הבא באוסף Restaurant על ידי עדכון הערך של השדה priceCategory.

איך עוברים למסמך באוסף

בשלבים הבאים מוסבר מה קורה במהלך הכתיבה:

  1. יוצרים טרנזקציה עם הרשאות קריאה וכתיבה.
  2. קוראים את המסמך restaurant1 באוסף Restaurants.
  3. קוראים את האינדקסים של המסמך.
  4. חישוב השינויים שצריך לבצע בנתונים. במקרה הזה, יש חמש מוטציות:
    • ‫M1: מעדכנים את השורה של restaurant1 כך שתשקף את השינוי בערך של השדה priceCategory.
    • ‫M2 ו-M3: מוחקים את רשומות האינדקס הישנות של priceCategory.
    • ‫M4 ו-M5: מוסיפים רשומות חדשות לאינדקס עבור priceCategory.
  5. מאשרים את המוטציות האלה.

לקוח האחסון בשירות Firestore מחפש את הפיצולים שכוללים את המפתחות של השורות שרוצים לשנות. ניקח לדוגמה מקרה שבו פיצול 3 משרת את M1, ופיצול 6 משרת את M2-M5. מדובר בעסקה מבוזרת, שבה כל הפיצולים האלה הם משתתפים. הפיצולים של המשתתפים עשויים לכלול גם פיצולים אחרים שמהם נתונים נקראו קודם כחלק מעסקת הקריאה והכתיבה.

בשלבים הבאים מתואר מה קורה כחלק מההתחייבות:

  1. לקוח האחסון שולח אישור. השמירה מכילה את המוטציות M1-M5.
  2. המשתתפים בעסקה הזו הם פיצול 3 ופיצול 6. אחד מהמשתתפים נבחר כמתאם, כמו Split 3. תפקיד המתאם הוא לוודא שהעסקה מתבצעת או מבוטלת באופן אטומי אצל כל המשתתפים.
    • העותקים המשוכפלים של המנהיגים בחלוקות האלה אחראים לעבודה שנעשית על ידי המשתתפים והמתאמים.
  3. כל משתתף וכל מתאם מריצים אלגוריתם Paxos עם העותקים המתאימים שלהם.
    • המוביל מריץ אלגוריתם Paxos עם העותקים. הקונצנזוס מושג אם רוב העותקים משיבים למנהיג בתשובה ok to commit.
    • כל משתתף מודיע לרכז כשהוא מוכן (השלב הראשון של אישור דו-שלבי). אם אחד מהמשתתפים לא יכול לאשר את העסקה, העסקה כולה aborts.
  4. אחרי שהמתאם יודע שכל המשתתפים, כולל הוא עצמו, מוכנים, הוא מעביר את תוצאת העסקה accept לכל המשתתפים (השלב השני של אישור דו-שלבי). בשלב הזה, כל משתתף מתעד את החלטת האישור באחסון יציב והעסקה מאושרת.
  5. המתאם משיב ללקוח האחסון ב-Firestore שהטרנזקציה בוצעה. במקביל, הרכיב המתאם וכל המשתתפים מחילים את השינויים על הנתונים.

מחזור החיים של שמירה (commit)

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

כתיבה במספר אזורים

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

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

הסבר על משך החיים של קריאה

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

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

הסבר על עסקת קריאה בשכבת האחסון

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

קריאות חזקות

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

קריאה של מסך מפוצל יחיד

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

בשלב הזה, יכולים לקרות המקרים הבאים, בהתאם לשכפול שנבחר:

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

לאחר מכן, Firestore מחזיר את התגובה ללקוח שלו.

קריאה עם פיצול מסך

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

הימנעות מנקודות חמות

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

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

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

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

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