במאמר הזה מתוארות שיטות מומלצות לשיפור הביצועים של שאילתות Spanner Graph, כולל האופטימיזציות הבאות:
- מומלץ להימנע מסריקה מלאה של טבלת הקלט של הצמתים והקשתות.
- להקטין את כמות הנתונים שהשאילתה צריכה לקרוא מהאחסון.
- הקטינו את הגודל של נתוני הביניים.
התחלה מצמתים עם קרדינליות נמוכה
כדאי לכתוב את ה-Path traversal כך שהוא יתחיל בצמתים עם קרדינליות נמוכה יותר. הגישה הזו שומרת על קבוצת התוצאות הביניים קטנה ומאיצה את הביצוע של השאילתה.
לדוגמה, לשאילתות הבאות יש את אותה סמנטיקה:
מעבר קדימה בין קצוות:
GRAPH FinGraph MATCH (p:Person {name:"Alex"})-[:Owns]->(a:Account {is_blocked: true}) RETURN p.id AS person_id, a.id AS account_id;מעבר הפוך בין קצוות:
GRAPH FinGraph MATCH (a:Account {is_blocked:true})<-[:Owns]-(p:Person {name: "Alex"}) RETURN p.id AS person_id, a.id AS account_id;
בהנחה שיש פחות אנשים בשם Alex מאשר מספר החשבונות החסומים, מומלץ לכתוב את השאילתה הזו בחיפוש קדימה.
התחלה מצמתים עם קרדינליות נמוכה חשובה במיוחד למעבר בנתיב באורך משתנה. בדוגמה הבאה מוצגת הדרך המומלצת למצוא חשבונות שנמצאים במרחק של עד שלוש העברות מחשבון נתון.
GRAPH FinGraph
MATCH (:Account {id: 7})-[:Transfers]->{1,3}(a:Account)
RETURN a.id;
הגדרת כל התוויות כברירת מחדל
אם לא מציינים תוויות, Spanner Graph מסיק אילו צמתים ותוויות קצה מתאימים. מומלץ לציין תוויות לכל הצמתים והקצוות, כי יכול להיות שלא תמיד אפשר יהיה להסיק את התוויות האלה, וסריקה של יותר תוויות מהנדרש עלולה להתבצע.
הצהרת התאמה יחידה
בדוגמה הבאה מוצגים חשבונות שמקושרים באמצעות 3 העברות לכל היותר מהחשבון הנתון:
GRAPH FinGraph
MATCH (src:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id;
בכל הצהרות MATCH
מציינים תוויות בצמתים ובקשתות כשהם מתייחסים לאותו רכיב אבל נמצאים ב-MATCH הצהרות.
בדוגמה הבאה אפשר לראות את הגישה המומלצת:
GRAPH FinGraph
MATCH (acct:Account {id: 7})-[:Transfers]->{1,3}(other_acct:Account)
RETURN acct, COUNT(DISTINCT other_acct) AS related_accts
GROUP BY acct
NEXT
MATCH (acct:Account)<-[:Owns]-(p:Person)
RETURN p.id AS person, acct.id AS acct, related_accts;
שימוש ב-IS_FIRST כדי לבצע אופטימיזציה של שאילתות
אפשר להשתמש בפונקציה
IS_FIRST
כדי לשפר את ביצועי השאילתות על ידי דגימת קצוות והגבלת המעברים
בתרשימים. הפונקציה הזו עוזרת לטפל בצמתים עם קרדינליות גבוהה ולבצע אופטימיזציה של שאילתות מרובות קפיצות.
אם גודל המדגם שציינתם קטן מדי, יכול להיות שהשאילתה לא תחזיר נתונים. לכן, יכול להיות שתצטרכו לנסות גדלים שונים של מדגמים כדי למצוא את האיזון האופטימלי בין הנתונים שמוחזרים לבין שיפור הביצועים של השאילתה.
בדוגמאות האלה של IS_FIRST נעשה שימוש ב-FinGraph, תרשים פיננסי עם Account צמתים ו-Transfers קצוות להעברות כספים. כדי ליצור את FinGraph ולהשתמש בו להרצת השאילתות לדוגמה, אפשר לעיין במאמר הגדרה ושליחת שאילתות ב-Spanner Graph.
הגבלת הקצוות שמועברים כדי לשפר את ביצועי השאילתות
כששולחים שאילתות לגרפים, יכול להיות שלחלק מהצמתים יהיה מספר גדול משמעותית של קשתות נכנסות או יוצאות בהשוואה לצמתים אחרים. הצמתים האלה עם הקרדינליות הגבוהה נקראים לפעמים צמתים ראשיים או צמתים מרכזיים. צמתים ראשיים עלולים לגרום לבעיות בביצועים כי מעבר דרכם עשוי לכלול עיבוד של כמויות עצומות של נתונים, מה שמוביל לחלוקת נתונים לא מאוזנת (partition skew) ולזמני ביצוע ארוכים.
כדי לבצע אופטימיזציה של שאילתה של תרשים עם צמתים ראשיים, משתמשים בפונקציה IS_FIRST בתוך סעיף FILTER כדי להגביל את מספר הקשתות שהשאילתה עוברת מצומת. יכול להיות שבחשבונות ב-FinGraph יש מספרים גבוהים בהרבה של טרנזקציות בהשוואה לחשבונות אחרים, ולכן כדאי להשתמש ב-IS_FIRST כדי למנוע שאילתה לא יעילה. הטכניקה הזו שימושית במיוחד כשלא צריך ספירה מלאה של כל החיבורים מצומת על.
השאילתה הבאה מוצאת חשבונות (a2) שמקבלים העברות ישירות או עקיפות מחשבונות חסומים (a1). השאילתה משתמשת ב-IS_FIRST כדי למנוע ביצועים איטיים כשיש בחשבון הרבה העברות, על ידי הגבלת מספר הקצוות Transfers שצריך לקחת בחשבון לכל Account.
GRAPH FinGraph
MATCH
(a1:Account {is_blocked: true})
-[e:Transfers WHERE e IN
{
MATCH -[selected_e:Transfers]->
FILTER IS_FIRST(@max_transfers_per_account) OVER (
PARTITION BY SOURCE_NODE_ID(selected_e)
ORDER BY selected_e.create_time DESC)
RETURN selected_e
}
]->{1,5}
(a2:Account)
RETURN a1.id AS src_id, a2.id AS dst_id;
בדוגמה הזו נעשה שימוש ב:
@max_transfers_per_account: פרמטר של שאילתה שמציין את המספר המקסימלי של קצוותTransfersשצריך לקחת בחשבון עבור כל חשבון (a1).PARTITION BY SOURCE_NODE_ID(selected_e): המגבלה שלIS_FIRSTחלה בנפרד על כל חשבון (a1).
ORDER BY selected_e.create_time DESC: מציין שההעברות האחרונות יוחזרו.
דוגמה לצמתים ביניים לאופטימיזציה של שאילתות עם כמה קפיצות
אפשר גם לשפר את יעילות השאילתות באמצעות IS_FIRST כדי לדגום צמתים ביניים בשאילתות מרובות קפיצות. הטכניקה הזו משפרת את היעילות על ידי הגבלת מספר הנתיבים שהשאילתה בודקת עבור כל צומת ביניים. כדי לעשות את זה, צריך לפצל שאילתה מרובת קפיצות לכמה משפטי MATCH שמופרדים באמצעות NEXT, ולהחיל IS_FIRST באמצע, במקום שבו רוצים לדגום:
GRAPH FinGraph
MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->(a2:Account)
FILTER IS_FIRST(1) OVER (PARTITION BY a2)
RETURN a1, a2
NEXT
MATCH (a2)-[e2:Transfers]->(a3:Account)
RETURN a1.id AS src_id, a2.id AS mid_id, a3.id AS dst_id;
כדי להבין איך IS_FIRST מבצע אופטימיזציה של השאילתה הזו:
הסעיף
FILTER IS_FIRST(1) OVER (PARTITION BY a2)חל על המשפט הראשוןMATCH.לכל צומת של חשבון ביניים (
a2),IS_FIRSTמתייחס רק לTransfersהקצה הראשון (e1) שמגיע, וכך מצמצם את מספר הנתיבים שצריך לבדוק בהצהרה השנייהMATCH.היעילות הכוללת של שאילתת שני השלבים משתפרת כי
MATCHהשני לא מעבד נתונים מיותרים, במיוחד אם יש ל-a2הרבה העברות נכנסות.