יצירת אינדקס ScaNN

בדף הזה מוסבר איך להשתמש בהטמעות מאוחסנות כדי ליצור אינדקסים ולהריץ שאילתות על הטמעות באמצעות ScaNN אינדקס ב-AlloyDB ל-PostgreSQL. מידע נוסף על אחסון הטמעה זמין במאמר אחסון הטמעות של וקטורים.

‫AlloyDB alloydb_scann, תוסף ל-PostgreSQL שפותח על ידי Google ומיישם אינדקס יעיל מאוד של השכן הקרוב ביותר שמבוסס על אלגוריתם ScaNN.

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

לפני שמתחילים

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

  • וקטורי הטמעה מתווספים לטבלה במסד הנתונים של AlloyDB.

  • התוסף vector שמבוסס על pgvector, הורחב על ידי Google ל-AlloyDB, והתוסף alloydb_scann מותקנים:

    CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE;
    
  • אם רוצים ליצור אינדקסים של ScaNN עם כוונון אוטומטי, צריך לוודא שהדגל scann.enable_preview_features מופעל. אם אתם לא רוצים להפעיל תכונות בגרסת Preview, או אם אתם משתמשים במופעים של סביבת ייצור, אתם יכולים ליצור אינדקס ScaNN עם פרמטרים ספציפיים במקום זאת.

יצירת אינדקס ScaNN עם כוונון אוטומטי

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

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

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

יצירת אינדקס ScaNN במצב AUTO

לפני שיוצרים אינדקסים במצב AUTO, חשוב לשים לב לנקודות הבאות:

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

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

  CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (mode='AUTO');

מחליפים את מה שכתוב בשדות הבאים:

  • INDEX_NAME: השם של האינדקס שרוצים ליצור, לדוגמה my-scann-index. שמות האינדקסים משותפים במסד הנתונים. מוודאים שכל שם אינדקס ייחודי לכל טבלה במסד הנתונים.

  • TABLE: הטבלה שאליה רוצים להוסיף את האינדקס.

  • EMBEDDING_COLUMN: העמודה שבה מאוחסנים נתוני vector.

  • DISTANCE_FUNCTION: פונקציית המרחק שבה רוצים להשתמש עם האינדקס הזה. צריך לבחור אחת מהאפשרויות:

    • מרחק L2: l2

    • מכפלה סקלרית: dot_product

    • מרחק קוסינוס: cosine

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

    • SEARCH_OPTIMIZED (ברירת מחדל): כדי לבצע אופטימיזציה של ההחזרה של חיפוש וקטורי ושל זמן האחזור של חיפוש וקטורי, על חשבון משך זמן של תהליך build ארוך יותר.
    • BALANCED: כדי ליצור אינדקס שמאזן בין זמן בניית האינדקס לבין ביצועי החיפוש.

יצירת אינדקס ScaNN במצב ידני

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

כדי ליצור אינדקס ScaNN במצב MANUAL, מריצים את הפקודה הבאה:

  CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (mode='MANUAL', num_leaves=NUM_LEAVES_VALUE, quantizer =QUANTIZER, max_num_levels=MAX_NUM_LEVELS);

מחליפים את מה שכתוב בשדות הבאים:

  • INDEX_NAME: השם של האינדקס שרוצים ליצור, לדוגמה my-scann-index. שמות האינדקסים משותפים במסד הנתונים. מוודאים שכל שם אינדקס ייחודי לכל טבלה במסד הנתונים.

  • TABLE: הטבלה שאליה רוצים להוסיף את האינדקס.

  • EMBEDDING_COLUMN: העמודה שבה מאוחסנים נתוני vector.

  • DISTANCE_FUNCTION: פונקציית המרחק שבה רוצים להשתמש עם האינדקס הזה. צריך לבחור אחת מהאפשרויות:

    • מרחק L2: l2

    • מכפלה סקלרית: dot_product

    • מרחק קוסינוס: cosine

  • NUM_LEAVES_VALUE: מספר המחיצות שיוחלו על האינדקס הזה. הערך יכול להיות בין 1 ל-10 מיליון. למידע נוסף על בחירת הערך הזה, אפשר לעיין במאמר התאמה של אינדקס ScaNN.

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

    • SQ8: מספק איזון בין ביצועי השאילתה לבין אובדן מינימלי של מידע, בדרך כלל פחות מ-1-2%. זה ערך ברירת המחדל.
    • AH: AH דחוס פי 4 בהשוואה ל-SQ8. כדאי לשקול את האפשרות הזו כדי לשפר את ביצועי השאילתות כשמנוע העמודות מופעל והנתונים של האינדקס והטבלה מאוכלסים במנוע העמודות, בהתאם לגודל המוגדר שלו. מידע נוסף זמין במאמר שיטות מומלצות להתאמת ScaNN.
    • FLAT: מספק את הזיכרון הגבוה ביותר של 99% ומעלה, במחיר של ביצועי חיפוש.
  • MAX_NUM_LEVELS: המספר המקסימלי של רמות בעץ של אשכולות K-means. הערך 1(ברירת מחדל) מגדיר קוונטיזציה מבוססת-עץ בשתי רמות, והערך 2 מגדיר קוונטיזציה מבוססת-עץ בשלוש רמות.

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

יצירת אינדקס ScaNN עם פרמטרים ספציפיים

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

אינדקס עץ דו-רמתי

כדי להחיל אינדקס של עץ דו-רמתי באמצעות אלגוריתם ScaNN על עמוד שמכיל הטמעות וקטוריות מאוחסנות, מריצים את שאילתת ה-DDL הבאה:

CREATE INDEX INDEX_NAME ON TABLE
USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (num_leaves=NUM_LEAVES_VALUE, quantizer =QUANTIZER);

מחליפים את מה שכתוב בשדות הבאים:

  • INDEX_NAME: השם של האינדקס שרוצים ליצור, לדוגמה my-scann-index. השמות של האינדקסים משותפים במסד הנתונים. חשוב לוודא שכל שם אינדקס הוא ייחודי לכל טבלה במסד הנתונים.

  • TABLE: הטבלה שאליה רוצים להוסיף את האינדקס.

  • EMBEDDING_COLUMN: עמודה שמאחסנת נתונים vector.

  • DISTANCE_FUNCTION: פונקציית המרחק לשימוש באינדקס הזה. צריך לבחור אחת מהאפשרויות:

    • מרחק L2: l2

    • מכפלה סקלרית: dot_product

    • מרחק קוסינוס: cosine

  • NUM_LEAVES_VALUE: מספר המחיצות שיוחלו על האינדקס הזה. הערך יכול להיות בין 1 ל-10 מיליון. מידע נוסף על בחירת הערך הזה זמין במאמר התאמה של אינדקס ScaNN.

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

    • SQ8: מספק איזון בין ביצועי השאילתה לבין אובדן מינימלי של מידע, בדרך כלל פחות מ-1-2%. זה ערך ברירת המחדל.
    • AH: AH דחוס פי 4 בהשוואה ל-SQ8. כדאי לשקול את האפשרות הזו כדי לשפר את ביצועי השאילתות כשמנוע העמודות מופעל והנתונים של האינדקס והטבלה מאוכלסים במנוע העמודות, בהתאם לגודל המוגדר שלו. מידע נוסף זמין במאמר שיטות מומלצות להתאמת ScaNN.
    • FLAT: מספק את הזיכרון הגבוה ביותר של 99% ומעלה, במחיר של ביצועי חיפוש.

אינדקס עץ עם שלוש רמות

כדי ליצור אינדקס של עץ עם שלוש רמות באמצעות אלגוריתם ScaNN בעמודה שמכילה הטמעות וקטוריות מאוחסנות, מריצים את שאילתת ה-DDL הבאה:

CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (num_leaves=NUM_LEAVES_VALUE, max_num_levels = 2);

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

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

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

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)
WITH (num_leaves=NUM_LEAVES_VALUE, max_num_levels = MAX_NUM_LEVELS);

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

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

כדי לראות את התקדמות ההוספה לאינדקס, משתמשים בתצוגה pg_stat_progress_create_index:

SELECT * FROM pg_stat_progress_create_index;

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

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

הכפייה של יצירת אינדקס בטבלאות ריקות או קטנות

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

כדי לאלץ יצירת אינדקס, פועלים לפי השלבים הבאים:

  1. מגדירים את הפרמטר scann.allow_blocked_operations creation ברמת הסשן לערך true במסד הנתונים:

    SET scann.allow_blocked_operations = true;
    
  2. מקצים את ההרשאה SUPERUSER למשתמש שמריץ את השאילתות האלה במסד הנתונים:

    CREATE USER USER_NAME WITH SUPERUSER PASSWORD PASSWORD;
    

    מחליפים את מה שכתוב בשדות הבאים:

    • USER_NAME: השם של המשתמש שרוצים לתת לו את ההרשאה.
    • PASSWORD: הסיסמה של המשתמש.

יצירת אינדקסים במקביל

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

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

מערכת AlloyDB מבצעת אופטימיזציה אוטומטית של מספר העובדים המקבילים, אבל אתם יכולים לשנות את מספר העובדים המקבילים באמצעות הפרמטרים max_parallel_maintenance_workers,‏ max_parallel_workers ו-min_parallel_table_scan_size של תכנון שאילתות ב-PostgreSQL.

הרצת שאילתה

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

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

  SELECT * FROM TABLE
  ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY 'EMBEDDING'
  LIMIT ROW_COUNT

מחליפים את מה שכתוב בשדות הבאים:

  • TABLE: הטבלה שמכילה את ההטמעה שאליה רוצים להשוות את הטקסט.

  • INDEX_NAME: השם של האינדקס שרוצים להשתמש בו, לדוגמה my-scann-index.

  • EMBEDDING_COLUMN: העמודה שמכילה את ההטמעות המאוחסנות.

  • DISTANCE_FUNCTION_QUERY: פונקציית המרחק שרוצים להשתמש בה בשאילתה הזו. בוחרים אחת מהאפשרויות הבאות בהתאם לפונקציית המרחק שבה השתמשתם כשיצרתם את האינדקס:

    • מרחק L2: <->

    • מכפלה פנימית: <#>

    • מרחק קוסינוס: <=>

  • EMBEDDING: וקטור ההטמעה שרוצים למצוא את השכנים הסמנטיים הקרובים ביותר שלו שמאוחסנים.

  • ROW_COUNT: מספר השורות שיש להחזיר.

    מציינים 1 אם רוצים רק את ההתאמה הטובה ביותר.

אפשר גם להשתמש בפונקציה embedding() כדי לתרגם את הטקסט לווקטור. מכיוון שהפונקציה embedding() מחזירה מערך real, צריך להחיל המרה מפורשת של הקריאה embedding() ל-vector לפני שמחילים אותה על אחד מאופרטורי השכן הקרוב ביותר (למשל, <-> למרחק L2). לאחר מכן, האופרטורים יכולים להשתמש באינדקס ScaNN כדי למצוא את השורות במסד הנתונים עם ההטמעות הדומות ביותר מבחינה סמנטית.

המאמרים הבאים