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

בחירת גרסת תיעוד:

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

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

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

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

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

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

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

  • (ברירת מחדל) recall וזמן אחזור של חיפוש וקטורי במחיר של משך זמן של תהליך build של האינדקס
  • איזון בין משך זמן של תהליך build של האינדקס לבין ביצועי החיפוש

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

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)

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

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

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

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

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

    • מרחק L2: ‏ l2

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

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

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

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (MODE='AUTO',
      OPTIMIZATION='OPTIMIZATION')

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

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

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

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

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

    • מרחק L2: ‏ l2

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

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

  • (אופציונלי) OPTIMIZATION: מוגדר לאחת מהאפשרויות הבאות:

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

    • BALANCED: איזון משך זמן של תהליך build של אינדקס וביצועי החיפוש.

    אם המדיניות OPTIMIZATION מוגדרת, חובה לכלול גם את MODE='AUTO'.

יצירת אינדקס שכוונן באופן ידני

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

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

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

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      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 ל-30 מיליון. מידע נוסף על בחירת הערך הזה זמין במאמר שיפור מדד ScaNN.
  • QUANTIZER: סוג הכמת שרוצים להשתמש בו. שימו לב שאפשר לטעון את אינדקס ScaNN לתוך מנוע מבוסס-עמודות כדי להאיץ עוד יותר את חיפוש הווקטורים. בוחרים באחת מהאפשרויות הבאות:
    • (ברירת מחדל) SQ8: מספק איזון בין ביצועי השאילתה לבין אובדן מינימלי של נתונים. השיעור הזה בדרך כלל נמוך מ-1-2%.
    • (תצוגה מקדימה) ‫AH: גיבוב אסימטרי (AH) דחוס עד פי 4 בהשוואה ל-SQ8. כדי לשפר את הביצועים של השאילתות כשמנוע העמודות מופעל והנתונים של האינדקס והטבלה מאוכלסים במנוע העמודות, כדאי לשקול את האפשרות הזו. מידע נוסף זמין במאמר בנושא שיטות מומלצות להתאמת ScaNN.
    • FLAT: מספק את הזיכרון הכי טוב, 99% ומעלה, על חשבון ביצועי החיפוש.

אינדקס עץ ברמה שלישית

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 2);
  • INDEX_NAME: השם של האינדקס שרוצים ליצור. לדוגמה, my_scann_index. שמות האינדקסים משותפים בכל מסד הנתונים. חשוב לוודא שכל שם אינדקס ייחודי לכל טבלה במסד הנתונים.
  • TABLE: הטבלה שאליה רוצים להוסיף את האינדקס.
  • EMBEDDING_COLUMN: העמודה שבה מאוחסנים נתוני `vector`.
  • DISTANCE_FUNCTION: פונקציית המרחק לשימוש עם האינדקס הזה. בוחרים באחת מהאפשרויות הבאות:
    • מרחק L2: ‏ l2
    • מכפלה סקלרית: dot_product
    • מרחק קוסינוס: cosine
  • NUM_LEAVES_VALUE: מספר המחיצות שיחולו על האינדקס הזה. הערך יכול להיות בין 1 ל-30 מיליון. מידע נוסף על בחירת הערך הזה זמין במאמר שיפור מדד ScaNN.
  • QUANTIZER: סוג הכמת שרוצים להשתמש בו. שימו לב שאפשר לטעון את אינדקס ScaNN למנוע מבוסס-עמודות כדי להאיץ עוד יותר את חיפוש הווקטורים. בוחרים באחת מהאפשרויות הבאות:
    • (ברירת מחדל) SQ8: מספק איזון בין ביצועי השאילתה לבין אובדן מינימלי של נתונים. השיעור הזה בדרך כלל נמוך מ-1-2%.
    • (תצוגה מקדימה) ‫AH: גיבוב אסימטרי (AH) דחוס עד פי 4 בהשוואה ל-SQ8. כדי לשפר את הביצועים של השאילתות כשמנוע העמודות מופעל והנתונים של האינדקס והטבלה מאוכלסים במנוע העמודות, כדאי לשקול את האפשרות הזו. מידע נוסף זמין במאמר בנושא שיטות מומלצות להתאמת ScaNN.
    • FLAT: מספק את הזיכרון הכי טוב, 99% ומעלה, על חשבון ביצועי החיפוש.
  • (אופציונלי) AUTO_MAINTENANCE: ההגדרה הזו קובעת אם תחזוקה אוטומטית של האינדקס מופעלת או מושבתת. מידע נוסף על תחזוקה אוטומטית זמין במאמר בנושא תחזוקה של אינדקסים של וקטורים.
    • (ברירת מחדל) ON: מערכת AlloyDB Omni מבצעת תחזוקה אוטומטית של האינדקס.
    • OFF: ב-AlloyDB Omni לא מתבצעת תחזוקה אוטומטית של האינדקס.

מדד עץ עם ארבע רמות

אינדקסים של עץ עם ארבע רמות נמצאים בגרסת טרום-השקה.

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 = 3);
  • INDEX_NAME: השם של האינדקס שרוצים ליצור. לדוגמה, my_scann_index. שמות האינדקסים משותפים בכל מסד הנתונים. חשוב לוודא שכל שם אינדקס ייחודי לכל טבלה במסד הנתונים.
  • TABLE: הטבלה שאליה רוצים להוסיף את האינדקס.
  • EMBEDDING_COLUMN: העמודה שבה מאוחסנים נתוני `vector`.
  • DISTANCE_FUNCTION: פונקציית המרחק לשימוש עם האינדקס הזה. בוחרים באחת מהאפשרויות הבאות:
    • מרחק L2: ‏ l2
    • מכפלה סקלרית: dot_product
    • מרחק קוסינוס: cosine
  • NUM_LEAVES_VALUE: מספר המחיצות שיחולו על האינדקס הזה. הערך יכול להיות בין 1 ל-30 מיליון. מידע נוסף על בחירת הערך הזה זמין במאמר שיפור מדד ScaNN.
  • QUANTIZER: סוג הכמת שרוצים להשתמש בו. שימו לב שאפשר לטעון את אינדקס ScaNN למנוע מבוסס-עמודות כדי להאיץ עוד יותר את חיפוש הווקטורים. בוחרים באחת מהאפשרויות הבאות:
    • (ברירת מחדל) SQ8: מספק איזון בין ביצועי השאילתה לבין אובדן מינימלי של נתונים. השיעור הזה בדרך כלל נמוך מ-1-2%.
    • תצוגה מקדימה AH: גיבוב אסימטרי (AH) דחוס עד פי 4 בהשוואה ל-SQ8. כדי לשפר את הביצועים של השאילתות כשמנוע העמודות מופעל והנתונים של האינדקס והטבלה מאוכלסים במנוע העמודות, כדאי לשקול את האפשרות הזו. מידע נוסף זמין במאמר בנושא שיטות מומלצות להתאמת ScaNN.
    • FLAT: מספק את הזיכרון הכי טוב, 99% ומעלה, על חשבון ביצועי החיפוש.

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

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

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

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    מחליפים את המשתנים הבאים:

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

    • PARAMETER_NAME: רשימה מופרדת בפסיקים שמכילה את השמות של פרמטרים של שאילתה שרוצים לאפס. לדוגמה, num_leaves, quantization.

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

  2. צריך ליצור מחדש את האינדקס שהותאם ידנית כדי להמיר אותו לאינדקס שהותאם אוטומטית.

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

יצירת אינדקס ScaNN ל-real[] סוגי נתונים

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

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)

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

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

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

  • DIMENSIONS: מספר המאפיינים שהמודל תומך בהם.

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

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

    • מרחק L2: ‏ l2

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

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

הצגת התקדמות ההוספה לאינדקס

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

SELECT * FROM pg_stat_progress_create_index;

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

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

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

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

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

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

הפעלת יצירת אינדקסים מושהית

כדי להפעיל יצירת אינדקס מושהית:

  1. מפעילים את התכונה הניסיונית scann.enable_index_maintenance (מופעלת כברירת מחדל) ואת התכונה הניסיונית scann.enable_preview_features. הדגל scann.enable_preview_features מאפשר גם תכונות אחרות בגרסת טרום-השקה.

    gcloud alloydb instances update INSTANCE_ID \
       --database-flags scann.enable_index_maintenance=on \
       --database-flags scann.enable_preview_features=on \
       --region=REGION_ID \
       --cluster=CLUSTER_ID \
       --project=PROJECT_ID
    

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

    • INSTANCE_ID: המזהה של המכונה.
    • REGION_ID: האזור שבו המכונה ממוקמת, לדוגמה us-central1.
    • CLUSTER_ID: המזהה של האשכול שבו נמצא המופע.
    • PROJECT_ID: מזהה הפרויקט שבו נמצא האשכול.
  2. יוצרים אינדקס ScaNN. אם יוצרים אינדקס במצב ידני, צריך לוודא שהפרמטר auto_maintenance מוגדר לערך on. מידע נוסף זמין במאמר יצירת אינדקס בהתאמה ידנית.

מגבלות

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

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

‫AlloyDB Omni משתמש באימותים כדי למנוע יצירה של אינדקס ScaNN בטבלה ריקה או בטבלה עם מעט מאוד שורות, מהסיבות הבאות:

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

  • יכול להיות שתהיה ירידה בביצועי הכתיבה למסד הנתונים.

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

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

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

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

    SET scann.allow_blocked_operations = true;
    
  2. אם למשתמש שמשמש להרצת השאילתות האלה אין SUPERUSERהרשאות, צריך להקצות לו אותן:

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    מחליפים את המשתנים הבאים:

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

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

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

למרות ש-AlloyDB Omni מבצע אופטימיזציה אוטומטית של מספר העובדים המקבילים, אתם יכולים לכוון את העובדים המקבילים באמצעות הפרמטרים הבאים של תכנון שאילתות ב-PostgreSQL:

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

הרצת שאילתה

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

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

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

SELECT * FROM TABLE
ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY
  google_ml.embedding(
      model_id => 'MODEL_ID',
      content => 'CONTENT')::vector
LIMIT ROW_COUNT

מחליפים את המשתנים הבאים:

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

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

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

    • מרחק L2: ‏ <->

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

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

  • MODEL_ID: המזהה של מודל ההטמעה הרשום שרוצים להשתמש בו.

  • CONTENT: מחרוזת הטקסט שרוצים לתרגם להטמעה ולחפש.

  • ROW_COUNT: מספר השורות שיוחזרו. לדוגמה, מציינים 1 אם רוצים את ההתאמה היחידה הטובה ביותר.

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