שיטות מומלצות לשימוש ב-BigQuery Storage Write API (gRPC)
במסמך הזה מפורטות שיטות מומלצות לשימוש ב-BigQuery Storage Write API (gRPC). לפני שקוראים את המאמר הזה, כדאי לקרוא את המאמר סקירה כללית של BigQuery Storage Write API (gRPC).
הגבלת קצב יצירת הזרמים
לפני שיוצרים שידור, כדאי לבדוק אם אפשר להשתמש בשידור ברירת המחדל. במקרים של סטרימינג, לסטרימינג שמוגדר כברירת מחדל יש פחות מגבלות מכסה, והוא יכול להתרחב יותר מאשר סטרימינג שנוצר על ידי האפליקציה. אם אתם משתמשים בסטרימינג שנוצר על ידי אפליקציה, הקפידו להשתמש ברוחב הפס המקסימלי בכל סטרימינג לפני שיוצרים סטרימינג נוסף. לדוגמה, אפשר להשתמש בכתיבות אסינכרוניות.
במקרה של סטרימינג שנוצר על ידי אפליקציה, מומלץ להימנע מהתקשרות אל CreateWriteStream בתדירות גבוהה. באופן כללי, אם חורגים מ-40-50 שיחות בשנייה, זמן האחזור של קריאות ה-API גדל באופן משמעותי (יותר מ-25 שניות). מוודאים שהאפליקציה יכולה לקבל הפעלה במצב התחלתי (cold start), מגדילים בהדרגה את מספר הזרמים ומגבילים את קצב הקריאות של CreateWriteStream. אפשר גם להגדיר מועד אחרון ארוך יותר כדי לחכות לסיום השיחה, וכך למנוע כשל עם שגיאה DeadlineExceeded. יש גם מכסה לטווח ארוך יותר על השיעור המקסימלי של קריאות ל-CreateWriteStream. יצירת זרמים היא תהליך שדורש הרבה משאבים, ולכן הדרך הכי טובה להימנע מחריגה מהמגבלה הזו היא לצמצם את קצב יצירת הזרמים ולנצל באופן מלא את הזרמים הקיימים.
ניהול מאגר חיבורים
השיטה AppendRows יוצרת חיבור דו-כיווני לזרם. אפשר לפתוח כמה חיבורים בזרם ברירת המחדל, אבל רק חיבור פעיל אחד בזרמים שנוצרו על ידי האפליקציה.
כשמשתמשים בזרם ברירת המחדל, אפשר להשתמש בריבוב של Storage Write API (gRPC) כדי לכתוב לכמה טבלאות יעד עם חיבורים משותפים. ריבוב של חיבורים למאגרי מידע לשיפור התפוקה והניצול של משאבים. אם בתהליך העבודה שלכם יש יותר מ-20 חיבורים בו-זמניים, מומלץ להשתמש בריבוב. ריבוב זמין ב-Java וב-Go. פרטים על הטמעה ב-Java מופיעים במאמר שימוש בריבוב. פרטים על הטמעה ב-Go זמינים במאמר בנושא שיתוף חיבורים (multiplexing). אם אתם משתמשים במחבר Beam עם סמנטיקה של 'לפחות פעם אחת', אתם יכולים להפעיל ריבוב באמצעות UseStorageApiConnectionPool. המחבר Managed Service for Apache Spark מופעל כברירת מחדל עם Multiplexing.
כדי לקבל את הביצועים הכי טובים, מומלץ להשתמש בחיבור אחד לכמה שיותר פעולות כתיבה של נתונים. אל תשתמשו בחיבור אחד רק לכתיבה אחת, או אל תפתחו ותסגרו זרמים לכתיבות קטנות רבות.
יש מכסה על מספר החיבורים בו-זמנית שאפשר לפתוח בו-זמנית לכל פרויקט. אם חורגים מהמגבלה, השיחות אל AppendRows נכשלות.
עם זאת, אפשר להגדיל את המכסה של חיבורים בו-זמניים, ובדרך כלל היא לא מהווה גורם מגביל להרחבת קנה המידה.
כל קריאה ל-AppendRows יוצרת אובייקט חדש של כתיבת נתונים. לכן, כשמשתמשים במקור נתונים שנוצר על ידי אפליקציה, מספר החיבורים תואם למספר מקורות הנתונים שנוצרו. בדרך כלל, חיבור יחיד תומך ברוחב פס של לפחות 1MBps. הגבול העליון תלוי בכמה גורמים, כמו רוחב פס ברשת, הסכימה של הנתונים ועומס השרת, אבל יכול להיות גבוה מ-10MBps.
יש גם מכסה על התפוקה הכוללת לכל פרויקט. הערך הזה מייצג את מספר הבייטים לשנייה בכל החיבורים שמועברים דרך שירות Storage Write API (gRPC). אם הפרויקט חורג מהמכסה הזו, אפשר לבקש לשנות את המכסה. בדרך כלל, כדי להגדיל את המכסה צריך להגדיל גם מכסות נלוות, כמו מכסת החיבורים בו-זמנית, באותו יחס.
ניהול היסטים של שידורים כדי להשיג סמנטיקה של 'פעם אחת בדיוק'
ממשק Storage Write API (gRPC) מאפשר כתיבה רק לסוף הנוכחי של הזרם, שמשתנה ככל שמוסיפים נתונים. המיקום הנוכחי בשידור מוגדר כהיסט מתחילת השידור.
כשכותבים לזרם שנוצר על ידי אפליקציה, אפשר לציין את ההיסט של הזרם כדי להשיג סמנטיקה של כתיבה בדיוק פעם אחת.
כשמציינים היסט, פעולת הכתיבה היא אידמפוטנטית, ולכן אפשר לנסות אותה מחדש בבטחה אם יש שגיאות ברשת או אם השרת לא מגיב. צריך לטפל בשגיאות הבאות שקשורות להיסטים:
-
ALREADY_EXISTS(StorageErrorCode.OFFSET_ALREADY_EXISTS): השורה כבר נכתבה. אפשר להתעלם מהשגיאה הזו. -
OUT_OF_RANGE (StorageErrorCode.OFFSET_OUT_OF_RANGE): פעולת כתיבה קודמת נכשלה. מנסים שוב מהכתיבה האחרונה שהצליחה.
חשוב לזכור שהשגיאות האלה יכולות לקרות גם אם מגדירים ערך לא נכון של היסט, ולכן צריך לנהל את ההיסטים בזהירות.
לפני שמשתמשים בהיסטים של זרם, כדאי לשקול אם נדרשת סמנטיקה של בדיוק פעם אחת. לדוגמה, אם צינור הנתונים שלכם במעלה הזרם מבטיח רק כתיבה לפחות פעם אחת, או אם אתם יכולים לזהות בקלות כפילויות אחרי הטמעת הנתונים, יכול להיות שלא תצטרכו כתיבה בדיוק פעם אחת. במקרה כזה, מומלץ להשתמש בזרם ברירת המחדל, שלא דורש מעקב אחרי היסטים של שורות.
לא לחסום שיחות בAppendRows
השיטה AppendRows היא אסינכרונית. אפשר לשלוח סדרה של פעולות כתיבה בלי לחכות לתגובה לכל פעולת כתיבה בנפרד. הודעות התגובה בחיבור הדו-כיווני מגיעות באותו סדר שבו הבקשות הוכנסו לתור.
כדי להשיג את התפוקה הגבוהה ביותר, צריך להתקשר אל AppendRows בלי לחסום את ההמתנה לתגובה.
עדיפות לבקשות גדולות יותר
יש תקורה של עיבוד גם בצד הלקוח וגם בצד השרת לכל בקשה שנשלחת. כדי לשפר את היעילות, מומלץ לשלוח בקשות גדולות (מעל 1MB) כשאפשר.
טיפול בעדכוני סכימה
בתרחישים של סטרימינג נתונים, סכימות של טבלאות מנוהלות בדרך כלל מחוץ לצינור הסטרימינג. מקובל שהסכימה תתפתח עם הזמן, למשל על ידי הוספה של שדות חדשים שניתן להשאיר ריקים. צינור נתונים חזק צריך לטפל בעדכוני סכימה מחוץ לפס.
Storage Write API (gRPC) תומך בסכימות של טבלאות באופן הבא:
- בקשת הכתיבה הראשונה כוללת את הסכימה.
- כל שורת נתונים נשלחת כפרוטוקול בינארי. BigQuery ממפה את הנתונים לסכימה.
- אפשר להשמיט שדות שניתן להגדיר להם ערך null, אבל אי אפשר לכלול שדות שלא מופיעים בסכימה הנוכחית. אם שולחים שורות עם שדות נוספים, 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).
עם זאת, היא עלולה לגרום לאובדן נתונים לא מכוון, כי שדות לא מוכרים מושמטים בלי התראה.