בדף הזה מוסבר איך ליצור ולנהל אינדקסים של וקטורים ב-Spanner, שמשתמשים בחיפוש של השכן הקרוב המשוער (ANN) ובמבנים מבוססי-עץ כדי להאיץ חיפושים של דמיון וקטורי בנתונים שלכם.
Spanner מאיץ חיפושים של וקטורים של שכן קרוב משוער (ANN) באמצעות אינדקס וקטורים ייעודי. האינדקס הזה מבוסס על Scalable Nearest Neighbor (ScaNN) של Google Research, אלגוריתם יעיל מאוד לחיפוש השכן הקרוב ביותר.
אינדקס הווקטורים משתמש במבנה מבוסס-עץ כדי לחלק את הנתונים ולזרז את החיפושים. Spanner מציע הגדרות של עץ עם שתי רמות ועם שלוש רמות:
- הגדרת עץ דו-רמתי: צמתי עלים (
num_leaves) מכילים קבוצות של וקטורים קרובים יחד עם הצנטרואיד המתאים שלהם. רמת הבסיס מורכבת מהצנטרואידים מכל צמתי העלה. - הגדרת עץ עם שלוש רמות: דומה לקונספט של עץ עם שתי רמות, אבל כוללת שכבת ענפים נוספת (
num_branches), שממנה מרכזי הצמתים של העלים מחולקים עוד יותר כדי ליצור את רמת השורש (num_leaves).
Spanner בוחר בשבילכם אינדקס. עם זאת, אם אתם יודעים שאינדקס ספציפי פועל בצורה הכי טובה, אתם יכולים להשתמש ברמז FORCE_INDEX כדי לבחור להשתמש באינדקס הווקטורי המתאים ביותר לתרחיש לדוגמה שלכם.
מידע נוסף זמין במאמר בנושא VECTOR INDEX הצהרות.
מגבלות
- אי אפשר לפצל מראש אינדקסים של וקטורים. מידע נוסף מופיע במאמר סקירה כללית על פיצול מראש.
יצירת אינדקס וקטורי
כדי לבצע אופטימיזציה של ההחזרה והביצועים של אינדקס וקטורי, מומלץ:
יוצרים את אינדקס הווקטורים אחרי שרוב השורות עם ההטמעות נכתבות למסד הנתונים. יכול להיות שתצטרכו גם לבנות מחדש את אינדקס הווקטורים באופן תקופתי אחרי שמוסיפים נתונים חדשים. מידע נוסף זמין במאמר בנושא בנייה מחדש של אינדקס הווקטורים.
משתמשים בסעיף
STORINGכדי לאחסן עותק של עמודה באינדקס הווקטורי. אם ערך של עמודה מאוחסן באינדקס הווקטורי, Spanner מבצע סינון ברמת העלה של האינדקס כדי לשפר את ביצועי השאילתה. מומלץ לאחסן עמודה אם היא משמשת בתנאי סינון. מידע נוסף על שימוש ב-STORINGבאינדקס זמין במאמר יצירת אינדקס לסריקות של אינדקס בלבד.שימוש בעמודות מפתח שאינן מוטמעות באינדקס הווקטורי. עמודות מרכזיות דומות לעמודות
STORING, אבל הן מאפשרות למנוע השאילתות לבצע סינון בצורה יעילה יותר במהלך חיפוש וקטורי. מידע נוסף זמין במאמר יצירת אינדקס וקטורי.
כשיוצרים את הטבלה, עמודת ההטמעה צריכה להיות מערך של סוג הנתונים FLOAT32 (מומלץ) או FLOAT64, וצריכה להיות לה הערה vector_length שמציינת את המימד של הווקטורים. האורך האופטימלי של הווקטור תלוי בעומס העבודה, בגודל מערך הנתונים ובמשאבי החישוב הזמינים. כדאי להתנסות עם מימדים שונים כדי למצוא את הגודל הקטן ביותר ששומר על הדיוק והביצועים של האפליקציה.
הצהרת ה-DDL הבאה יוצרת טבלת Documents עם עמודת הטמעה DocEmbedding באורך וקטורי:
CREATE TABLE Documents (
UserId INT64 NOT NULL,
DocId INT64 NOT NULL,
Author STRING (1024),
DocContents Bytes(MAX),
DocEmbedding ARRAY<FLOAT32>(vector_length=>128) NOT NULL,
NullableDocEmbedding ARRAY<FLOAT32>(vector_length=>128),
WordCount INT64
) PRIMARY KEY (DocId);
אחרי שממלאים את הטבלה Documents, אפשר ליצור אינדקס וקטורי עם עץ דו-רמתי ו-1,000 צמתי עלים בטבלה Documents עם עמודת הטמעה DocEmbedding באמצעות מרחק קוסינוס:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
STORING (WordCount)
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
אם עמודת ההטמעה לא מסומנת כ-NOT NULL בהגדרת הטבלה, צריך להצהיר עליה באמצעות פסקה WHERE COLUMN_NAME IS NOT NULL בהגדרת אינדקס הווקטור, כאשר COLUMN_NAME הוא השם של עמודת ההטמעה. כדי ליצור אינדקס וקטורי עם עץ בן שלוש רמות ו-1,000,000 צמתי עלים בעמודת ההטמעה NullableDocEmbedding שאפשר להזין בה ערך null, באמצעות מרחק קוסינוס:
CREATE VECTOR INDEX DocEmbeddingThreeLevelIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);
סינון אינדקס וקטורי
אפשר גם ליצור אינדקס וקטורי מסונן כדי למצוא את הפריטים הכי דומים במסד הנתונים שתואמים לתנאי הסינון. אינדקס וקטורי מסונן מאנדקס באופן סלקטיבי שורות שעומדות בתנאי הסינון שצוינו, וכך משפר את ביצועי החיפוש.
בדוגמה הבאה, לטבלה Documents2 יש עמודה בשם Category.
בחיפוש הווקטורי שלנו, אנחנו רוצים להוסיף לאינדקס את הקטגוריה 'טכנולוגיה', ולכן אנחנו יוצרים עמודה שנוצרת על ידי המערכת ומחזירה את הערך NULL אם תנאי הקטגוריה לא מתקיים.
CREATE TABLE Documents2 (
UserId INT64 NOT NULL,
DocId INT64 NOT NULL,
DocName STRING (1024),
Author STRING (1024),
DocContents Bytes(MAX),
Category STRING(MAX),
NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
DocEmbedding ARRAY<FLOAT32>(vector_length=>128)
) PRIMARY KEY (DocId);
לאחר מכן, יוצרים אינדקס וקטורי עם מסנן. האינדקס הווקטורי TechDocEmbeddingIndex מוסיף לאינדקס רק מסמכים בקטגוריה 'טכנולוגיה'.
CREATE VECTOR INDEX TechDocEmbeddingIndex
ON Documents2(DocEmbedding)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
כש-Spanner מריץ את השאילתה הבאה, שיש לה מסננים שתואמים ל-TechDocEmbeddingIndex, הוא בוחר באופן אוטומטי את TechDocEmbeddingIndex ומאיץ את הביצוע שלה. השאילתה מחפשת רק מסמכים בקטגוריה 'טכנולוגיה'. אפשר גם להשתמש ב-{@FORCE_INDEX=TechDocEmbeddingIndex} כדי לחייב את Spanner להשתמש ב-TechDocEmbeddingIndex באופן מפורש.
SELECT *
FROM Documents2
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;
כדי לשפר את ביצועי השאילתות, אפשר לכלול בעמודות המפתח של אינדקס הווקטורים שלא מוטמעות. כך מנוע השאילתות יכול לבצע סינון בצורה יעילה יותר במהלך חיפוש וקטורי.
בהצהרה CREATE VECTOR INDEX, צריך לפרט את עמודות המפתח הנוספות האלה אחרי עמודת ההטמעה. לדוגמה, ההצהרה הבאה יוצרת אינדקס וקטורי שכולל את עמודות המפתח DocName ו-Author לסינון יעיל יותר:
CREATE VECTOR INDEX DocEmbeddingIndexWithKeys
ON Documents2(DocEmbedding, DocName, Author)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
המאמרים הבאים
מידע נוסף על הפונקציות של GoogleSQL
APPROXIMATE_COSINE_DISTANCE(),APPROXIMATE_EUCLIDEAN_DISTANCE(),APPROXIMATE_DOT_PRODUCT()