במסד נתונים של Spanner, המערכת יוצרת באופן אוטומטי אינדקס לכל מפתח ראשי של טבלה. לדוגמה, לא צריך לעשות שום דבר כדי ליצור אינדקס למפתח הראשי של Singers, כי הוא נוצר אוטומטית.
אפשר גם ליצור אינדקסים משניים לעמודות אחרות. הוספת אינדקס משני לעמודה מייעלת את החיפוש של נתונים בעמודה הזו. לדוגמה, אם אתם צריכים לחפש במהירות אלבום לפי שם, כדאי ליצור אינדקס משני ב-AlbumTitle, כדי ש-Spanner לא יצטרך לסרוק את כל הטבלה.
אם החיפוש בדוגמה הקודמת מתבצע בתוך טרנזקציה של קריאה וכתיבה,
החיפוש היעיל יותר גם מונע נעילות בטבלה כולה,
מה שמאפשר הוספות ועדכונים בו-זמניים בטבלה לשורות מחוץ לטווח החיפוש AlbumTitle.
בנוסף ליתרונות שהם מביאים לחיפושים, אינדקסים משניים יכולים גם לעזור ל-Spanner לבצע סריקות בצורה יעילה יותר, ולאפשר סריקות אינדקס במקום סריקות מלאות של טבלאות.
מערכת Spanner שומרת את הנתונים הבאים בכל אינדקס משני:
- כל עמודות המפתח מטבלת הבסיס
- כל העמודות שנכללות באינדקס
- כל העמודות שצוינו בסעיף האופציונלי
STORING(מסדי נתונים של GoogleSQL) או בסעיףINCLUDE(מסדי נתונים של PostgreSQL) בהגדרת האינדקס.
עם הזמן, Spanner מנתח את הטבלאות כדי לוודא שהאינדקסים המשניים משמשים לשאילתות המתאימות.
הוספת אינדקס משני
הזמן היעיל ביותר להוספת אינדקס משני הוא כשיוצרים את הטבלה. כדי ליצור טבלה ואינדקסים שלה בו-זמנית, שולחים את הצהרות ה-DDL של הטבלה החדשה והאינדקסים החדשים בבקשה אחת ל-Spanner.
ב-Spanner, אפשר גם להוסיף אינדקס משני חדש לטבלה קיימת בזמן שמסד הנתונים ממשיך לשרת תנועה. בדומה לשינויים אחרים בסכימה ב-Spanner, הוספת אינדקס למסד נתונים קיים לא מחייבת להעביר את מסד הנתונים למצב אופליין, ולא נועלת עמודות או טבלאות שלמות.
בכל פעם שמוסיפים אינדקס חדש לטבלה קיימת, Spanner מבצע backfill באופן אוטומטי, כלומר מאכלס את האינדקס כדי לשקף תצוגה עדכנית של הנתונים שמתווספים לאינדקס. מערכת Spanner מנהלת את תהליך המילוי החוזר הזה בשבילכם, והתהליך פועל ברקע באמצעות משאבי צומת בעדיפות נמוכה. מהירות מילוי החסר של האינדקס מותאמת לשינויים במשאבי הצומת במהלך יצירת האינדקס, ומילוי החסר לא משפיע באופן משמעותי על הביצועים של מסד הנתונים.
תהליך יצירת האינדקס יכול להימשך כמה דקות עד כמה שעות. מכיוון שיצירת אינדקס היא עדכון סכימה, היא כפופה לאותם אילוצים של ביצועים כמו כל עדכון סכימה אחר. הזמן שנדרש ליצירת אינדקס משני תלוי בכמה גורמים:
- גודל מערך הנתונים
- קיבולת החישוב של המכונה
- העומס על המופע
כדי לראות את ההתקדמות בתהליך מילוי חוסרים באינדקס, אפשר לעיין בקטע ההתקדמות.
חשוב לדעת שהשימוש בעמודה commit timestamp כחלק הראשון של האינדקס המשני עלול ליצור נקודות חמות ולפגוע בביצועים של פעולות כתיבה.
משתמשים בהצהרת CREATE INDEX כדי להגדיר אינדקס משני בסכימה. הנה כמה דוגמאות:
כדי ליצור אינדקס לכל Singers במסד הנתונים לפי השם הפרטי ושם המשפחה:
GoogleSQL
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
PostgreSQL
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
כדי ליצור אינדקס של כל Songs במסד הנתונים לפי הערך של SongName:
GoogleSQL
CREATE INDEX SongsBySongName ON Songs(SongName);
PostgreSQL
CREATE INDEX SongsBySongName ON Songs(SongName);
כדי להוסיף לאינדקס רק את השירים של זמר מסוים, משתמשים בסעיף INTERLEAVE IN כדי לשלב את האינדקס בטבלה Singers:
GoogleSQL
CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName),
INTERLEAVE IN Singers;
PostgreSQL
CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName)
INTERLEAVE IN Singers;
כדי להוסיף לאינדקס רק את השירים באלבום מסוים:
GoogleSQL
CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName),
INTERLEAVE IN Albums;
PostgreSQL
CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName)
INTERLEAVE IN Albums;
כדי ליצור אינדקס בסדר יורד של SongName:
GoogleSQL
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC),
INTERLEAVE IN Albums;
PostgreSQL
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC)
INTERLEAVE IN Albums;
הערה: ההערה הקודמת DESC רלוונטית רק ל-SongName. כדי ליצור אינדקס לפי סדר יורד של מפתחות אינדקס אחרים, צריך להוסיף להם את ההערה DESC:
SingerId DESC, AlbumId DESC.
חשוב גם לזכור ש-PRIMARY_KEY היא מילה שמורה ואי אפשר להשתמש בה כשם של אינדקס. זה השם שניתן לפסאודו-אינדקס
שנוצר כשיוצרים טבלה עם הגדרת PRIMARY KEY
פרטים נוספים ושיטות מומלצות לבחירת אינדקסים לא משולבים ואינדקסים משולבים זמינים במאמרים אפשרויות לאינדקסים ושימוש באינדקס משולב בעמודה שהערך שלה עולה או יורד באופן מונוטוני.
אינדקסים ושיבוץ
אפשר לשלב אינדקסים של Spanner עם טבלאות אחרות כדי למקם שורות אינדקס באותו מיקום עם שורות של טבלה אחרת. בדומה לשיטת השילוב של טבלאות ב-Spanner, העמודות של המפתח הראשי של ההורה של האינדקס חייבות להיות קידומת של העמודות באינדקס, עם התאמה בסוג ובסדר המיון. בשונה מטבלאות משולבות, לא נדרש להתאים את שמות העמודות. כל שורה באינדקס משולב מאוחסנת פיזית יחד עם שורת ההורה המשויכת.
לדוגמה, נניח את הסכימה הבאה:
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo PROTO<Singer>(MAX)
) PRIMARY KEY (SingerId);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
PublisherId INT64 NOT NULL
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE TABLE Songs (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
TrackId INT64 NOT NULL,
PublisherId INT64 NOT NULL,
SongName STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
CREATE TABLE Publishers (
Id INT64 NOT NULL,
PublisherName STRING(MAX)
) PRIMARY KEY (Id);
כדי ליצור אינדקס לכל Singers במסד הנתונים לפי השם הפרטי ושם המשפחה, צריך ליצור אינדקס. כך מגדירים את האינדקס SingersByFirstLastName:
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
אם רוצים ליצור אינדקס של Songs ב-(SingerId, AlbumId, SongName), אפשר לעשות את הפעולות הבאות:
CREATE INDEX SongsBySingerAlbumSongName
ON Songs(SingerId, AlbumId, SongName);
אפשר גם ליצור אינדקס שמשולב עם ישות אב של Songs, כמו בדוגמה הבאה:
CREATE INDEX SongsBySingerAlbumSongName
ON Songs(SingerId, AlbumId, SongName),
INTERLEAVE IN Albums;
בנוסף, אפשר ליצור אינדקס של Songs ב-(PublisherId, SingerId, AlbumId, SongName) שמשולב עם טבלה שלא נמצאת בהיררכיה מעל Songs, כמו Publishers. הערה: המפתח הראשי של טבלת Publishers (id) לא מופיע כקידומת של העמודות עם האינדקס בדוגמה הבאה. הפעולה הזו עדיין מותרת כי Publishers.Id ו-Songs.PublisherId חולקים את אותו סוג, סדר מיון ואפשרות להגדרה כ-null.
CREATE INDEX SongsByPublisherSingerAlbumSongName
ON Songs(PublisherId, SingerId, AlbumId, SongName),
INTERLEAVE IN Publishers;
בדיקת ההתקדמות של מילוי חוסרים באינדקס
המסוף
בתפריט הניווט של Spanner, לוחצים על הכרטיסייה Operations (פעולות). בדף Operations מוצגת רשימה של פעולות שפועלות.
מחפשים את פעולת המילוי החוזר ברשימה. אם התהליך עדיין פועל, אינדיקטור ההתקדמות בעמודה שעת סיום מציג את אחוז הפעולה שהושלמה, כמו שמוצג בתמונה הבאה:

gcloud
כדי לבדוק את התקדמות הפעולה, משתמשים ב-gcloud spanner operations describe.
קבלת מזהה הפעולה:
gcloud spanner operations list --instance=INSTANCE-NAME \ --database=DATABASE-NAME --type=DATABASE_UPDATE_DDL
מחליפים את מה שכתוב בשדות הבאים:
- INSTANCE-NAME בשם של מכונת Spanner.
- DATABASE-NAME בשם של מסד הנתונים.
הערות שימוש:
כדי להגביל את הרשימה, מציינים את הדגל
--filter. לדוגמה:--filter="metadata.name:example-db"מציג רק את הפעולות במסד נתונים ספציפי.--filter="error:*"מציג רק את פעולות הגיבוי שנכשלו.
מידע על תחביר המסננים זמין במאמר gcloud topic filters. מידע על סינון פעולות גיבוי מופיע בשדה
filterבמאמר ListBackupOperationsRequest.הדגל
--typeלא תלוי באותיות רישיות.
הפלט אמור להיראות כך:
OPERATION_ID STATEMENTS DONE @TYPE _auto_op_123456 CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName) False UpdateDatabaseDdlMetadata CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName), INTERLEAVE IN Albums _auto_op_234567 True CreateDatabaseMetadataמריצים את
gcloud spanner operations describe:gcloud spanner operations describe \ --instance=INSTANCE-NAME \ --database=DATABASE-NAME \ projects/PROJECT-NAME/instances/INSTANCE-NAME/databases/DATABASE-NAME/operations/OPERATION_ID
מחליפים את מה שכתוב בשדות הבאים:
- INSTANCE-NAME: שם מכונת Spanner.
- DATABASE-NAME: שם מסד הנתונים של Spanner.
- PROJECT-NAME: שם הפרויקט.
- OPERATION-ID: מזהה הפעולה שרוצים לבדוק.
בקטע
progressבפלט מוצג האחוז של הפעולה שהושלמה. הפלט אמור להיראות כך:done: true ... progress: - endTime: '2021-01-22T21:58:42.912540Z' progressPercent: 100 startTime: '2021-01-22T21:58:11.053996Z' - progressPercent: 67 startTime: '2021-01-22T21:58:11.053996Z' ...
REST v1
קבלת מזהה הפעולה:
gcloud spanner operations list --instance=INSTANCE-NAME
--database=DATABASE-NAME --type=DATABASE_UPDATE_DDL
מחליפים את מה שכתוב בשדות הבאים:
- INSTANCE-NAME בשם של מכונת Spanner.
- DATABASE-NAME בשם של מסד הנתונים.
לפני שמשתמשים בנתוני הבקשה, צריך להחליף את הנתונים הבאים:
- PROJECT-ID: מזהה הפרויקט.
- INSTANCE-ID: מזהה המכונה.
- DATABASE-ID: מזהה מסד הנתונים.
- OPERATION-ID: מזהה הפעולה.
ה-method של ה-HTTP וכתובת ה-URL:
GET https://spanner.googleapis.com/v1/projects/PROJECT-ID/instances/INSTANCE-ID/databases/DATABASE-ID/operations/OPERATION-ID
כדי לשלוח את הבקשה צריך להרחיב אחת מהאפשרויות הבאות:
אתם אמורים לקבל תגובת JSON שדומה לזו:
{
...
"progress": [
{
"progressPercent": 100,
"startTime": "2023-05-27T00:52:27.366688Z",
"endTime": "2023-05-27T00:52:30.184845Z"
},
{
"progressPercent": 100,
"startTime": "2023-05-27T00:52:30.184845Z",
"endTime": "2023-05-27T00:52:40.750959Z"
}
],
...
"done": true,
"response": {
"@type": "type.googleapis.com/google.protobuf.Empty"
}
}
ב-gcloud וב-REST, אפשר לראות את ההתקדמות של כל הצהרת מילוי חוזר של אינדקס בקטע progress. לכל הצהרה במערך ההצהרות,
יש שדה תואם במערך ההתקדמות. סדר המערך הזה של התקדמות התהליך
תואם לסדר של מערך ההצהרות. אחרי שהם יהיו זמינים, השדות startTime, progressPercent ו-endTime יאוכלסו בהתאם.
שימו לב: בפלט לא מוצג זמן משוער לסיום ההתקדמות של מילוי החסר.
אם הפעולה נמשכת יותר מדי זמן, אפשר לבטל אותה. מידע נוסף זמין במאמר ביטול יצירת אינדקס.
תרחישים שבהם אפשר לראות את ההתקדמות של מילוי החוסרים באינדקס
יש תרחישים שונים שיכולים לקרות כשמנסים לבדוק את ההתקדמות של מילוי חוזר של אינדקס. הצהרות על יצירת אינדקס שדורשות מילוי חוזר של אינדקס הן חלק מפעולות עדכון סכימה, ויכולות להיות כמה הצהרות שהן חלק מפעולת עדכון סכימה.
התרחיש הראשון הוא הפשוט ביותר, שבו הצהרת יצירת האינדקס היא ההצהרה הראשונה בפעולת עדכון הסכימה. מכיוון שהצהרת יצירת האינדקס היא ההצהרה הראשונה, היא הראשונה שעוברת עיבוד ומופעלת בגלל סדר ההפעלה.
השדה startTime של הצהרת יצירת האינדקס יתמלא באופן מיידי בשעת ההתחלה של פעולת עדכון הסכימה. לאחר מכן, השדה progressPercent של הצהרת יצירת האינדקס מאוכלס כשההתקדמות של מילוי האינדקס היא מעל 0%. לבסוף, השדה endTime יאוכלס אחרי שהדוח יאושר.
התרחיש השני הוא כשמשפט יצירת האינדקס הוא לא המשפט הראשון בפעולת עדכון הסכימה. אף שדה שקשור להצהרה על יצירת האינדקס לא יאוכלס עד שההצהרות הקודמות יאושרו, בגלל סדר הביצוע.
בדומה לתרחיש הקודם, אחרי שהדוחות הקודמים יאושרו, השדה startTime של הצהרת יצירת האינדקס יתמלא קודם, ואחריו השדה progressPercent. לבסוף, השדה endTime מתמלא אחרי שהדוח מסיים את השמירה.
ביטול יצירת האינדקס
אפשר להשתמש ב-Google Cloud CLI כדי לבטל את יצירת האינדקס. כדי לאחזר רשימה של פעולות עדכון סכימה במסד נתונים של Spanner, משתמשים בפקודה gcloud spanner operations list וכוללים את האפשרות --filter:
gcloud spanner operations list \
--instance=INSTANCE \
--database=DATABASE \
--filter="@TYPE:UpdateDatabaseDdlMetadata"
מאתרים את OPERATION_ID של הפעולה שרוצים לבטל, ואז משתמשים בפקודה gcloud spanner operations cancel כדי לבטל אותה:
gcloud spanner operations cancel OPERATION_ID \
--instance=INSTANCE \
--database=DATABASE
צפייה באינדקסים קיימים
כדי לראות מידע על אינדקסים קיימים במסד נתונים, אפשר להשתמש במסוףGoogle Cloud או ב-Google Cloud CLI:
המסוף
נכנסים לדף Instances של Spanner במסוף Google Cloud .
לוחצים על השם של המכונה שרוצים להציג.
בחלונית הימנית, לוחצים על מסד הנתונים שרוצים לראות ואז על הטבלה שרוצים לראות.
לוחצים על הכרטיסייה Indexes (אינדקסים). Google Cloud במסוף מוצגת רשימה של אינדקסים.
אופציונלי: כדי לקבל פרטים על אינדקס, כמו העמודות שהוא כולל, לוחצים על שם האינדקס.
gcloud
משתמשים בפקודה gcloud spanner databases ddl describe:
gcloud spanner databases ddl describe DATABASE \
--instance=INSTANCE
ה-CLI של gcloud מדפיס את ההצהרות של שפת הגדרת הנתונים (DDL) כדי ליצור את הטבלאות והאינדקסים של מסד הנתונים. הצהרות CREATE
INDEX מתארות את האינדקסים הקיימים. לדוגמה:
--- |-
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
) PRIMARY KEY(SingerId)
---
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)
שאילתה עם אינדקס ספציפי
בקטעים הבאים מוסבר איך לציין אינדקס בהצהרת SQL ובממשק הקריאה של Spanner. בדוגמאות שבקטעים האלה מניחים שהוספתם עמודה MarketingBudget לטבלה Albums ויצרתם אינדקס בשם AlbumsByAlbumTitle:
GoogleSQL
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
MarketingBudget INT64,
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);
PostgreSQL
CREATE TABLE Albums (
SingerId BIGINT NOT NULL,
AlbumId BIGINT NOT NULL,
AlbumTitle VARCHAR,
MarketingBudget BIGINT,
PRIMARY KEY (SingerId, AlbumId)
) INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);
ציון אינדקס בהצהרת SQL
כשמשתמשים ב-SQL כדי לשלוח שאילתה לטבלת Spanner, המערכת משתמשת באופן אוטומטי בכל האינדקסים שיכולים לייעל את השאילתה. כתוצאה מכך, לא צריך לציין אינדקס לשאילתות SQL. עם זאת, לשאילתות שחשובות לעומס העבודה שלכם, Google ממליצה להשתמש בהנחיות FORCE_INDEX בהצהרות SQL כדי לקבל ביצועים עקביים יותר.
במקרים מסוימים, יכול להיות ש-Spanner יבחר אינדקס שיגרום להגדלת זמן האחזור של השאילתה. אם ביצעתם את השלבים לפתרון בעיות של ירידה בביצועים ואישרתם שכדאי לנסות אינדקס אחר לשאילתה, אתם יכולים לציין את האינדקס כחלק מהשאילתה.
כדי לציין אינדקס בהצהרת SQL, משתמשים ברמז FORCE_INDEX כדי לספק הוראת אינדקס. התחביר של הוראות האינדוקס הוא:
GoogleSQL
FROM MyTable@{FORCE_INDEX=MyTableIndex}
PostgreSQL
FROM MyTable /*@ FORCE_INDEX = MyTableIndex */
אפשר גם להשתמש בהוראת אינדקס כדי להנחות את Spanner לסרוק את טבלת הבסיס במקום להשתמש באינדקס:
GoogleSQL
FROM MyTable@{FORCE_INDEX=_BASE_TABLE}
PostgreSQL
FROM MyTable /*@ FORCE_INDEX = _BASE_TABLE */
אפשר להשתמש בהוראת אינדקס כדי להנחות את Spanner לסרוק אינדקס בטבלה עם סכימות בעלות שם:
GoogleSQL
FROM NAMED_SCHEMA_NAME.TABLE_NAME@{FORCE_INDEX="NAMED_SCHEMA_NAME.TABLE_INDEX_NAME"}
PostgreSQL
FROM NAMED_SCHEMA_NAME.TABLE_NAME /*@ FORCE_INDEX = TABLE_INDEX_NAME */
בדוגמה הבאה מוצגת שאילתת SQL שמציינת אינדקס:
GoogleSQL
SELECT AlbumId, AlbumTitle, MarketingBudget
FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
WHERE AlbumTitle >= "Aardvark" AND AlbumTitle < "Goo";
PostgreSQL
SELECT AlbumId, AlbumTitle, MarketingBudget
FROM Albums /*@ FORCE_INDEX = AlbumsByAlbumTitle */
WHERE AlbumTitle >= 'Aardvark' AND AlbumTitle < 'Goo';
הוראת אינדקס עשויה לחייב את מעבד השאילתות של Spanner לקרוא עמודות נוספות שנדרשות בשאילתה אבל לא מאוחסנות באינדקס.
מעבד השאילתות מאחזר את העמודות האלה על ידי צירוף האינדקס לטבלת הבסיס. כדי להימנע מהצטרפות נוספת, משתמשים בסעיף STORING (מסדי נתונים של ניב GoogleSQL) או בסעיף INCLUDE (מסדי נתונים של ניב PostgreSQL) כדי לאחסן את העמודות הנוספות באינדקס.
בדוגמה הקודמת, העמודה MarketingBudget לא מאוחסנת באינדקס, אבל שאילתת ה-SQL בוחרת את העמודה הזו. כתוצאה מכך, מערכת Spanner צריכה לחפש את העמודה MarketingBudget בטבלת הבסיס, ואז לצרף אותה לנתונים מהאינדקס כדי להחזיר את תוצאות השאילתה.
מערכת Spanner מעלה שגיאה אם יש בהוראת האינדקס אחת מהבעיות הבאות:
- האינדקס לא קיים.
- האינדקס נמצא בטבלת בסיס אחרת.
- בשאילתה חסר ביטוי סינון
NULLנדרש לאינדקסNULL_FILTERED.
בדוגמאות הבאות מוצגות שאילתות ששולפות את הערכים של AlbumId, AlbumTitle ו-MarketingBudget באמצעות האינדקס AlbumsByAlbumTitle:
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
ציון אינדקס בממשק הקריאה
כשמשתמשים בממשק הקריאה של Spanner ורוצים ש-Spanner ישתמש באינדקס, צריך לציין את האינדקס. בממשק הקריאה, האינדקס לא נבחר באופן אוטומטי.
בנוסף, האינדקס צריך להכיל את כל הנתונים שמופיעים בתוצאות השאילתה, לא כולל עמודות שמהוות חלק מהמפתח הראשי. המגבלה הזו קיימת כי ממשק הקריאה לא תומך בצירופים בין האינדקס לבין טבלת הבסיס. אם אתם צריכים לכלול עמודות אחרות בתוצאות השאילתה, יש לכם כמה אפשרויות:
- משתמשים בסעיף
STORINGאוINCLUDEכדי לאחסן את העמודות הנוספות באינדקס. - מריצים שאילתה בלי לכלול את העמודות הנוספות, ואז משתמשים במפתחות הראשיים כדי לשלוח שאילתה נוספת שקוראת את העמודות הנוספות.
Spanner מחזיר ערכים מהאינדקס בסדר מיון עולה לפי מפתח האינדקס. כדי לאחזר ערכים בסדר יורד, מבצעים את השלבים הבאים:
מוסיפים הערה למפתח האינדקס עם
DESC. לדוגמה:CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle DESC);ההערה
DESCחלה על מפתח אינדקס יחיד. אם האינדקס כולל יותר ממפתח אחד, ואתם רוצים שתוצאות השאילתה יופיעו בסדר יורד על סמך כל המפתחות, צריך לכלול הערתDESCלכל מפתח.אם הקריאה מציינת טווח מפתחות, צריך לוודא שטווח המפתחות גם הוא בסדר יורד. במילים אחרות, הערך של מפתח ההתחלה צריך להיות גדול מהערך של מפתח הסיום.
בדוגמה הבאה אפשר לראות איך מאחזרים את הערכים של AlbumId ושל AlbumTitle באמצעות האינדקס AlbumsByAlbumTitle:
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
יצירת אינדקס לסריקות של אינדקס בלבד
אופציונלי: אפשר להשתמש בסעיף STORING (למסדי נתונים של GoogleSQL) או בסעיף INCLUDE (למסדי נתונים של PostgreSQL) כדי לאחסן עותק של עמודה באינדקס. סוג האינדקס הזה מספק יתרונות לשאילתות ולקריאות קריאה שמשתמשות באינדקס, אבל הוא דורש שימוש באחסון נוסף:
- שאילתות SQL שמשתמשות באינדקס ובעמודות שנבחרו ומאוחסנות בסעיף
STORINGאוINCLUDEלא דורשות איחוד נוסף עם טבלת הבסיס. - קריאות
read()שמשתמשות באינדקס יכולות לקרוא עמודות שמאוחסנות על ידי פסוקיתSTORING/INCLUDE.
לדוגמה, נניח שיצרתם גרסה חלופית של AlbumsByAlbumTitle
שמאחסנת עותק של העמודה MarketingBudget באינדקס (שימו לב לסעיף STORING או INCLUDE שמודגש):
GoogleSQL
CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);
PostgreSQL
CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) INCLUDE (MarketingBudget);
עם האינדקס הישן AlbumsByAlbumTitle, Spanner צריך לבצע הצטרפות לאינדקס עם טבלת הבסיס, ואז לאחזר את העמודה מטבלת הבסיס. עם האינדקס החדשAlbumsByAlbumTitle2, Spanner קורא את העמודה ישירות מהאינדקס, וזה יעיל יותר.
אם משתמשים בממשק הקריאה במקום ב-SQL, האינדקס החדש AlbumsByAlbumTitle2
מאפשר גם לקרוא את העמודה MarketingBudget ישירות:
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
שינוי אינדקס
אפשר להשתמש בהצהרה ALTER INDEX כדי להוסיף עמודות נוספות לאינדקס קיים או להסיר עמודות. הפעולה הזו יכולה לעדכן את רשימת העמודות שמוגדרת על ידי פסקה STORING (מסדי נתונים של ניב GoogleSQL) או פסקה INCLUDE (מסדי נתונים של ניב PostgreSQL) כשיוצרים את האינדקס. אי אפשר להשתמש בהצהרה הזו כדי להוסיף עמודות למפתח האינדקס או להסיר ממנו עמודות. לדוגמה, במקום ליצור אינדקס חדש AlbumsByAlbumTitle2, אפשר להשתמש ב-ALTER INDEX כדי להוסיף עמודה ל-AlbumsByAlbumTitle, כמו בדוגמה הבאה:
GoogleSQL
ALTER INDEX AlbumsByAlbumTitle ADD STORED COLUMN MarketingBudget
PostgreSQL
ALTER INDEX AlbumsByAlbumTitle ADD INCLUDE COLUMN MarketingBudget
כשמוסיפים עמודה חדשה לאינדקס קיים, Spanner משתמש בתהליך מילוי חוזר ברקע. במהלך מילוי החוסרים, אי אפשר לקרוא את העמודה באינדקס, ולכן יכול להיות שלא תהיה עלייה בביצועים כמו שציפיתם. אפשר להשתמש בפקודה gcloud spanner operations כדי להציג את רשימת הפעולות הממושכות ולראות את הסטטוס שלהן.
מידע נוסף זמין במאמר בנושא תיאור פעולה.
אפשר גם להשתמש באפשרות ביטול הפעולה כדי לבטל פעולה שפועלת.
אחרי שהמילוי החוזר מסתיים, Spanner מוסיף את העמודה לאינדקס. ככל שהאינדקס גדל, יכול להיות שהשאילתות שמשתמשות באינדקס ירוצו לאט יותר.
בדוגמה הבאה אפשר לראות איך משמיטים עמודה מאינדקס:
GoogleSQL
ALTER INDEX AlbumsByAlbumTitle DROP STORED COLUMN MarketingBudget
PostgreSQL
ALTER INDEX AlbumsByAlbumTitle DROP INCLUDE COLUMN MarketingBudget
אינדקס של ערכי NULL
כברירת מחדל, Spanner יוצר אינדקס לערכים NULL. לדוגמה, נניח שזו ההגדרה של האינדקס SingersByFirstLastName בטבלה Singers:
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
כל השורות של Singers נוספות לאינדקס גם אם FirstName או LastName, או שניהם, הם NULL.
כשמבצעים אינדוקס של ערכי NULL, אפשר לבצע שאילתות SQL וקריאות יעילות של נתונים שכוללים ערכי NULL. לדוגמה, אפשר להשתמש בהצהרת שאילתת ה-SQL הזו
כדי למצוא את כל Singers עם NULL FirstName:
GoogleSQL
SELECT s.SingerId, s.FirstName, s.LastName
FROM Singers@{FORCE_INDEX=SingersByFirstLastName} AS s
WHERE s.FirstName IS NULL;
PostgreSQL
SELECT s.SingerId, s.FirstName, s.LastName
FROM Singers /* @ FORCE_INDEX = SingersByFirstLastName */ AS s
WHERE s.FirstName IS NULL;
סדר המיון של ערכי NULL
Spanner ממיין את NULL כערך הקטן ביותר לכל סוג נתונים נתון. אם ממיינים עמודה בסדר עולה (ASC), הערכים NULL ממוינים ראשונים. אם בוחרים למיין עמודה בסדר יורד (DESC), הערכים NULL יופיעו בסוף.
השבתת יצירת אינדקס לערכי NULL
GoogleSQL
כדי להשבית את יצירת האינדקס של ערכי null, מוסיפים את מילת המפתח NULL_FILTERED להגדרת האינדקס. אינדקסים של NULL_FILTERED שימושיים במיוחד לאינדקס של עמודות חסכוניות (sparse), שבהן רוב השורות מכילות ערך NULL_FILTERED.NULL במקרים כאלה, האינדקס NULL_FILTERED יכול להיות קטן בהרבה ויעיל יותר לתחזוקה מאשר אינדקס רגיל שכולל ערכים של NULL.
הנה הגדרה חלופית של SingersByFirstLastName שלא כוללת אינדקס של ערכי NULL:
CREATE NULL_FILTERED INDEX SingersByFirstLastNameNoNulls
ON Singers(FirstName, LastName);
מילת המפתח NULL_FILTERED חלה על כל העמודות של מפתח האינדקס. אי אפשר לציין סינון של NULL על בסיס כל עמודה.
PostgreSQL
כדי לסנן שורות עם ערכי null בעמודה אחת או יותר עם אינדקס, משתמשים בפרדיקט WHERE COLUMN IS NOT NULL.
אינדקסים עם סינון של ערכי Null שימושיים במיוחד לאינדוקס של עמודות דלילות, שרוב השורות שלהן מכילות ערך NULL. במקרים כאלה, האינדקס עם הסינון של ערכי null יכול להיות קטן בהרבה ויעיל יותר לתחזוקה מאשר אינדקס רגיל שכולל ערכי NULL.
הנה הגדרה חלופית של SingersByFirstLastName שלא כוללת אינדקס של ערכי NULL:
CREATE INDEX SingersByFirstLastNameNoNulls
ON Singers(FirstName, LastName)
WHERE FirstName IS NOT NULL
AND LastName IS NOT NULL;
סינון ערכי NULL מונע מ-Spanner להשתמש בהם בחלק מהשאילתות. לדוגמה, Spanner לא משתמש באינדקס בשאילתה הזו, כי האינדקס משמיט שורות Singers שבהן LastName הוא NULL. כתוצאה מכך, שימוש באינדקס ימנע מהשאילתה להחזיר את השורות הנכונות:
GoogleSQL
FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
WHERE FirstName = "John";
PostgreSQL
FROM Singers /*@ FORCE_INDEX = SingersByFirstLastNameNoNulls */
WHERE FirstName = 'John';
כדי לאפשר ל-Spanner להשתמש באינדקס, צריך לשכתב את השאילתה כך שהיא לא תכלול את השורות שגם לא נכללות באינדקס:
GoogleSQL
SELECT FirstName, LastName
FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
WHERE FirstName = 'John' AND LastName IS NOT NULL;
PostgreSQL
SELECT FirstName, LastName
FROM Singers /*@ FORCE_INDEX = SingersByFirstLastNameNoNulls */
WHERE FirstName = 'John' AND LastName IS NOT NULL;
שדות פרוטו של אינדקס
אפשר להשתמש בעמודות שנוצרו כדי ליצור אינדקס לשדות ב-protocol buffers שמאוחסנים בעמודות PROTO, כל עוד השדות שעבורם נוצר האינדקס משתמשים בסוגי הנתונים הפרימיטיביים או ENUM.
אם מגדירים אינדקס בשדה של הודעת פרוטוקול, אי אפשר לשנות או להסיר את השדה הזה מסכימת ה-proto. מידע נוסף זמין במאמר בנושא עדכונים בסכימות שמכילות אינדקס בשדות פרוטו.
הדוגמה הבאה היא של טבלת Singers עם עמודה של הודעת proto SingerInfo. כדי להגדיר אינדקס בשדה nationality של PROTO, צריך ליצור עמודה וירטואלית מאוחסנת:
GoogleSQL
CREATE PROTO BUNDLE (googlesql.example.SingerInfo, googlesql.example.SingerInfo.Residence);
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
...
SingerInfo googlesql.example.SingerInfo,
SingerNationality STRING(MAX) AS (SingerInfo.nationality) STORED
) PRIMARY KEY (SingerId);
ההגדרה של סוג ה-proto googlesql.example.SingerInfo היא:
GoogleSQL
package googlesql.example;
message SingerInfo {
optional string nationality = 1;
repeated Residence residence = 2;
message Residence {
required int64 start_year = 1;
optional int64 end_year = 2;
optional string city = 3;
optional string country = 4;
}
}
לאחר מכן מגדירים אינדקס בשדה nationality של ה-proto:
GoogleSQL
CREATE INDEX SingersByNationality ON Singers(SingerNationality);
שאילתת ה-SQL הבאה קוראת נתונים באמצעות האינדקס הקודם:
GoogleSQL
SELECT s.SingerId, s.FirstName
FROM Singers AS s
WHERE s.SingerNationality = "English";
הערות:
- משתמשים בהוראת אינדקס כדי לגשת לאינדקסים בשדות של עמודות מאגר אחסון לפרוטוקולים.
- אי אפשר ליצור אינדקס בשדות חוזרים של מאגר אחסון לפרוטוקולים.
עדכונים בסכימות שמכילות אינדקס בשדות פרוטו
אם מגדירים אינדקס בשדה של הודעת פרוטוקול, אי אפשר לשנות או להסיר את השדה הזה מסכימת ה-proto. הסיבה לכך היא שאחרי שמגדירים את האינדקס, מתבצעת בדיקת סוג בכל פעם שהסכימה מתעדכנת. Spanner מתעד את פרטי הסוג של כל השדות בנתיב שמשמשים בהגדרת האינדקס.
אינדקסים ייחודיים
אפשר להצהיר על אינדקסים UNIQUE. אינדקסים של UNIQUE מוסיפים אילוץ לנתונים שמתווספים לאינדקס, שאוסר על כניסות כפולות למפתח אינדקס נתון.
האילוץ הזה נאכף על ידי Spanner בזמן אישור העסקה.
באופן ספציפי, כל עסקה שתגרום לכך שיהיו כמה רשומות אינדקס לאותו מפתח תיכשל.
אם טבלה מכילה נתוני UNIQUE שאינם נתוני UNIQUE, ניסיון ליצור אינדקס UNIQUE בטבלה ייכשל.
הערה לגבי אינדקסים מסוג UNIQUE NULL_FILTERED
UNIQUE NULL_FILTERED אינדקס לא אוכף ייחודיות של מפתח האינדקס כשחלק אחד לפחות ממפתח האינדקס הוא NULL.
לדוגמה, נניח שיצרתם את הטבלה והאינדקס הבאים:
GoogleSQL
CREATE TABLE ExampleTable (
Key1 INT64 NOT NULL,
Key2 INT64,
Key3 INT64,
Col1 INT64,
) PRIMARY KEY (Key1, Key2, Key3);
CREATE UNIQUE NULL_FILTERED INDEX ExampleIndex ON ExampleTable (Key1, Key2, Col1);
PostgreSQL
CREATE TABLE ExampleTable (
Key1 BIGINT NOT NULL,
Key2 BIGINT,
Key3 BIGINT,
Col1 BIGINT,
PRIMARY KEY (Key1, Key2, Key3)
);
CREATE UNIQUE INDEX ExampleIndex ON ExampleTable (Key1, Key2, Col1)
WHERE Key1 IS NOT NULL
AND Key2 IS NOT NULL
AND Col1 IS NOT NULL;
בשתי השורות הבאות ב-ExampleTable יש את אותם ערכים עבור מפתחות האינדקס המשני Key1, Key2 ו-Col1:
1, NULL, 1, 1
1, NULL, 2, 1
מכיוון שהערך של Key2 הוא NULL והאינדקס מסונן לפי ערך null, השורות לא יופיעו באינדקס ExampleIndex. הן לא מוכנסות לאינדקס, ולכן האינדקס לא ידחה אותן בגלל הפרת הייחודיות ב-(Key1, Key2,
Col1).
אם רוצים שהאינדקס יאכוף את הייחודיות של ערכי הטופל (Key1, Key2, Col1), צריך להוסיף את ההערה NOT NULL ל-Key2 בהגדרת הטבלה או ליצור את האינדקס בלי לסנן ערכי null.
הסרת אינדקס
משתמשים בהצהרה DROP INDEX כדי להסיר אינדקס משני מהסכימה.
כדי להסיר את האינדקס בשם SingersByFirstLastName:
DROP INDEX SingersByFirstLastName;
יצירת אינדקס לסריקה מהירה יותר
כש-Spanner צריך לבצע סריקת טבלה (במקום חיפוש באינדקס) כדי לאחזר ערכים מעמודה אחת או יותר, אפשר לקבל תוצאות מהירות יותר אם קיים אינדקס לעמודות האלה, ובסדר שצוין בשאילתה. אם אתם מבצעים לעיתים קרובות שאילתות שדורשות סריקות, כדאי ליצור אינדקסים משניים כדי שהסריקות האלה יתבצעו בצורה יעילה יותר.
במיוחד אם אתם צריכים ש-Spanner יסרוק לעיתים קרובות את המפתח הראשי של טבלה או אינדקס אחר בסדר הפוך, אתם יכולים להגדיל את היעילות שלו באמצעות אינדקס משני שמבהיר את הסדר שנבחר.
לדוגמה, השאילתה הבאה תמיד מחזירה תוצאה מהירה, למרות ש-Spanner צריך לסרוק את Songs כדי למצוא את הערך הנמוך ביותר של SongId:
SELECT SongId FROM Songs LIMIT 1;
SongId הוא המפתח הראשי של הטבלה, שמאוחסן (כמו כל המפתחות הראשיים) בסדר עולה. מערכת Spanner יכולה לסרוק את האינדקס של המפתח הזה ולמצוא את התוצאה הראשונה במהירות.
עם זאת, בלי עזרה של אינדקס משני, השאילתה הבאה לא תחזיר תוצאות כל כך מהר, במיוחד אם Songs מכיל הרבה נתונים:
SELECT SongId FROM Songs ORDER BY SongId DESC LIMIT 1;
למרות ש-SongId הוא המפתח הראשי של הטבלה, ל-Spanner אין דרך לאחזר את הערך הכי גבוה בעמודה בלי לבצע סריקה מלאה של הטבלה.
הוספת האינדקס הבא תאפשר לשאילתה הזו להחזיר תוצאות מהר יותר:
CREATE INDEX SongIdDesc On Songs(SongId DESC);
אם האינדקס הזה קיים, Spanner ישתמש בו כדי להחזיר תוצאה לשאילתה השנייה הרבה יותר מהר.
המאמרים הבאים
- מידע על שיטות מומלצות לשימוש ב-SQL ב-Spanner
- הסבר על תוכניות להפעלת שאילתות ב-Spanner
- איך פותרים בעיות של ירידה בביצועים בשאילתות SQL