שיטות מומלצות לשימוש ב-BigQuery Storage Write API ‏ (gRPC)

כדי למקסם את תפוקת הכתיבה ולהימנע מצווארי בקבוק במכסות, אתם יכולים להשתמש ב-BigQuery Storage Write API ‏ (gRPC) ולשנות את האופן שבו האפליקציה יוצרת זרמים, מאגרי חיבורים ובקשות Batch. טיפול זהיר בהיסטים ובעדכוני סכימה גם עוזר לשמור על העמידות של צינורות עיבוד הנתונים להטמעה, ככל שסכימות הטבלאות מתפתחות. במאמר סקירה כללית על BigQuery Storage Write API ‏ (gRPC) מוסברים מושגים בסיסיים שקשורים לסטרימינג.

הגבלת הקצב של יצירת שידורים

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

בסטרימינג שנוצר על ידי אפליקציה, כדאי להימנע מקריאה ל-CreateWriteStream בתדירות גבוהה. באופן כללי, אם חורגים מ-40-50 קריאות לשנייה, זמן האחזור של הקריאות ל-API גדל באופן משמעותי (יותר מ-25 שניות). חשוב לוודא שהאפליקציה יכולה לקבל הפעלה במצב התחלתי (cold start), להגדיל את מספר הסטרימינג בהדרגה ולהגביל את קצב הקריאות ל-CreateWriteStream. אפשר גם להגדיר מועד אחרון ארוך יותר להמתנה עד להשלמת הקריאה, כדי שהיא לא תיכשל עם השגיאה DeadlineExceeded. יש גם מכסה לטווח ארוך יותר על הקצב המקסימלי של קריאות ל-CreateWriteStream. יצירת זרמים היא תהליך שדורש הרבה משאבים, ולכן הדרך הטובה ביותר לא לחרוג מהמגבלה הזו היא להפחית את קצב יצירת הזרמים ולנצל באופן מלא את הזרמים הקיימים.

ניהול מאגר החיבורים

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

כשמשתמשים בסטרימינג שמוגדר כברירת מחדל, אפשר להשתמש בריבוב של Storage Write API ‏ (gRPC) כדי לכתוב לכמה טבלאות יעד עם חיבורים משותפים. ריבוב מאגד חיבורים כדי לשפר את התפוקה ואת ניצול המשאבים. אם בתהליך העבודה יש יותר מ-20 חיבורים בו-זמניים, מומלץ להשתמש בריבוב. אפשר להשתמש בריבוב ב-Java וב-Go. פרטים על ההטמעה ב-Java זמינים במאמר שימוש בריבוב. לפרטים על ההטמעה ב-Go, קראו את המאמר שיתוף חיבורים (ריבוב). אם אתם משתמשים במחבר Beam עם סמנטיקה של לפחות פעם אחת, אתם יכולים להפעיל ריבוב באמצעות UseStorageApiConnectionPool. במחבר של Managed Service for Apache Spark, ריבוב מופעל כברירת מחדל.

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

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

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

יש גם מכסה על התפוקה הכוללת לכל פרויקט. המדד הזה מייצג את הבייטים לשנייה בכל החיבורים שעוברים דרך שירות Storage Write API ‏ (gRPC). אם הפרויקט חורג מהמכסה הזו, אתם יכולים לבקש לשנות את המכסה. בדרך כלל, צריך להגדיל מכסות נלוות, כמו המכסה של חיבורים בו-זמניים, ביחס שווה.

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

‫Storage Write API ‏ (gRPC) מאפשר רק כתיבה לסוף הנוכחי של ה-stream, שזז כשמתווספים נתונים. המיקום הנוכחי בסטרימינג מצוין כהיסט מתחילת הסטרימינג.

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

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

  • ‫ALREADY_EXISTS (StorageErrorCode.OFFSET_ALREADY_EXISTS): השורה כבר נכתבה. אפשר להתעלם מהשגיאה הזו.
  • ‫OUT_OF_RANGE (StorageErrorCode.OFFSET_OUT_OF_RANGE): פעולת כתיבה קודמת נכשלה. מנסים שוב מהכתיבה המוצלחת האחרונה.

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

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

לא לחסום ב-AppendRows שיחות

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

עדיפות לבקשות גדולות יותר

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

איך מטפלים בעדכוני סכימה

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

‫Storage Write API ‏ (gRPC) תומך בסכימות של טבלאות באופן הבא:

  • בקשת הכתיבה הראשונה כוללת את הסכימה.
  • אתם שולחים כל שורת נתונים כמאגר אחסון לפרוטוקולים בינארי. ‫BigQuery ממפה את הנתונים לסכימה.
  • אפשר להשמיט שדות שניתנים לאיפוס, אבל אי אפשר לכלול שדות שלא קיימים בסכימה הנוכחית. אם תשלחו שורות עם שדות נוספים, Storage Write API ‏ (gRPC) יחזיר StorageError עם StorageErrorCode.SCHEMA_MISMATCH_EXTRA_FIELD.

אם אתם רוצים לשלוח שדות חדשים במטען הייעודי (payload), קודם צריך לעדכן את סכימת הטבלה ב-BigQuery. ‫Storage Write API ‏ (gRPC) מזהה שינויים בסכימה אחרי זמן קצר, בסדר גודל של דקות. כשה-Storage Write API ‏ (gRPC) מזהה את השינוי בסכימה, הודעת התגובה AppendRowsResponse מכילה אובייקט TableSchema שמתאר את הסכימה החדשה.

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

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

אפשר גם להגדיר את JsonStreamWriter כך שיתעלם משדות לא ידועים בנתוני הקלט. כדי להגדיר את ההתנהגות הזו, קוראים ל-setIgnoreUnknownFields. ההתנהגות הזו דומה לאפשרות ignoreUnknownValues כשמשתמשים ב-BigQuery Storage Write API (REST). עם זאת, זה עלול להוביל לאובדן נתונים לא מכוון, כי שדות לא ידועים נמחקים בלי הודעה.