ניהול ערכי ברירת מחדל של מפתח ראשי

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

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

שיטות ליצירת מפתחות ראשיים באופן אוטומטי

כדי ליצור באופן אוטומטי ערכים של מפתח ראשי, אפשר להשתמש באסטרטגיות הבאות בעמודה עם ביטויי DEFAULT:

  • פונקציית UUID שמייצרת ערכי UUID בגרסה 4.
  • עמודות IDENTITY שיוצרות באופן אוטומטי ערכים של מספרים שלמים לעמודות מפתח ולעמודות שאינן מפתח.
  • SERIAL ב-PostgreSQL ו-AUTO_INCREMENT ב-GoogleSQL, שהן שפות הגדרת נתונים (DDL) של עמודות IDENTITY.
  • אובייקט סכימה, SEQUENCE, עם אפשרות bit_reversed_positive. ‫SEQUENCE זמין גם ב-GoogleSQL וגם ב-PostgreSQL.
  • עמודה rowid שנוצרת אוטומטית לטבלאות שאין להן מפתחות ראשיים שהוגדרו על ידי המשתמש.

מזהה ייחודי אוניברסלי (UUID)

מערכת Spanner יכולה ליצור באופן אוטומטי מזהה ייחודי אוניברסלי (UUID) בגרסה 4 לשימוש כמפתח ראשי. מספרי UUID מתאימים לאפליקציות חדשות ולטבלאות עם הרבה שורות. הם מפוזרים באופן אחיד בערך במרחב המפתחות, מה שמונע נקודות חמות בקנה מידה גדול. יצירת UUID יכולה ליצור מספר גדול של ערכים (2122), וכל ערך הוא ייחודי. לדוגמה, כדי לקבל הסתברות של 50% להתנגשות, צריך 2.71‎×1018 ערכים, או מיליארד ערכים בשנייה למשך 86 שנים. הטווח הרחב הזה מבטיח ערכים ייחודיים כשמשתמשים ב-UUID בטבלאות גדולות. מזהי UUID הם ייחודיים, בין אם יוצרים אותם במסד הנתונים או בלקוח. מומלץ להשתמש ב-UUID כשאפשר. אפשר לשלב בבטחה מזהי UUID שנוצרו על ידי לקוח ומזהי UUID שנוצרו על ידי Spanner באותה טבלה, אם מזהי ה-UUID שנוצרו על ידי הלקוח עוברים סריאליזציה באותיות קטנות, בהתאם ל-RFC 4122.

כדי ליצור ערכי ברירת מחדל לעמודה, אפשר להשתמש בפונקציה NEW_UUID של GoogleSQL או בפונקציה gen_random_uuid() של PostgreSQL. בדוגמה הבאה מוצג אופן היצירה של טבלה שבה לעמודת המפתח FanId יש UUID שנוצר בעמודת הערך כערך ברירת המחדל שלה.

GoogleSQL

CREATE TABLE Fans (
  FanId UUID DEFAULT (NEW_UUID()),
  Name STRING(MAX),
) PRIMARY KEY (FanId);

PostgreSQL

CREATE TABLE Fans (
  FanId uuid DEFAULT gen_random_uuid(),
  Name text,
  PRIMARY KEY (FanId)
);

GoogleSQL

INSERT INTO Fans (Name) VALUES ('Melissa Garcia')
THEN RETURN FanId;

PostgreSQL

INSERT INTO fans (name) VALUES ('Melissa Garcia')
RETURNING fanid;

ההצהרה הזו מחזירה תוצאה שדומה לתוצאה הבאה:

FanId
6af91072-f009-4c15-8c42-ebe38ae83751

מידע נוסף על פונקציות UUID שנוצרו זמין בדף העזר בנושא GoogleSQL או PostgreSQL.

IDENTITY עמודות

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

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

ב-Spanner, עמודות IDENTITY נתמכות גם ב-GoogleSQL וגם ב-PostgreSQL.

GoogleSQL

בדוגמה הבאה מוצג איך להשתמש בעמודות IDENTITY כדי ליצור עמודת מפתח ראשי של מספר שלם שנוצר אוטומטית עבור SingerId כשיוצרים טבלה חדשה באמצעות הפקודה CREATE TABLE:

CREATE TABLE Singers (
  SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
  Name STRING(MAX),
  Rank INT64
) PRIMARY KEY (SingerId);

אפשר גם לציין את נקודת ההתחלה של המונה בעמודה באמצעות האפשרות START COUNTER WITH. בדוגמה הבאה, נוצרת עמודה של מספרים שלמים שנוצרה באופן אוטומטי עבור SingerId, עם ערכים חיוביים שהביטים שלהם הפוכים ומונה פנימי שמתחיל ב-1,000.

CREATE TABLE Singers (
  SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE START COUNTER WITH 1000),
  Name STRING(MAX),
  Rank INT64
) PRIMARY KEY (SingerId);

PostgreSQL

בדוגמה הבאה מוצג שימוש בעמודות IDENTITY כדי ליצור עמודת מספרים שלמים שנוצרת אוטומטית עבור SingerId כשיוצרים טבלה חדשה באמצעות הפקודה CREATE TABLE:

CREATE TABLE Singers (
  SingerId bigint GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
  Name text,
  PRIMARY KEY (SingerId)
);

אפשר גם לציין את נקודת ההתחלה של המונה בעמודה באמצעות האפשרות START COUNTER WITH. בדוגמה הבאה, נוצרת עמודה של מספרים שלמים שנוצרו באופן אוטומטי [SingerId], שיוצרת ערכים חיוביים עם היפוך ביטים, והמונה הפנימי, לפני היפוך הביטים, מתחיל מ-1,000.

CREATE TABLE Singers (
  SingerId bigint GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE START COUNTER WITH 1000),
  Name text,
  PRIMARY KEY (SingerId)
);

SERIAL וגם AUTO_INCREMENT

‫Spanner תומך ב-SERIAL ב-PostgreSQL וב-AUTO_INCREMENT ב-GoogleSQL, שהם כינויים ל-DDL של IDENTITYcolumns ומשמשים ליצירת עמודות ייחודיות של מספרים שלמים. לפני שמשתמשים ב-SERIAL או ב-AUTO_INCREMENT, צריך להגדיר את האפשרות default_sequence_kind של מסד הנתונים. אפשר להשתמש בהצהרת ה-SQL הבאה כדי להגדיר את האפשרות default_sequence_kind של מסד הנתונים:

GoogleSQL

ALTER DATABASE db SET OPTIONS (default_sequence_kind = 'bit_reversed_positive');

CREATE TABLE Singers (
  id INT64 AUTO_INCREMENT PRIMARY KEY,
  name STRING(MAX),
);

PostgreSQL

ALTER DATABASE db SET spanner.default_sequence_kind = 'bit_reversed_positive';

CREATE TABLE Singers (
  id serial PRIMARY KEY,
  name text
);

מכיוון שהשדות SERIAL ו-AUTO_INCREMENT ממופים לעמודות IDENTITY, הם לא יופיעו כשמבצעים סריאליזציה של הסכימה. בסכימה הזו, הפלט של GetDatabaseDDL יהיה:

GoogleSQL

ALTER DATABASE db SET OPTIONS (default_sequence_kind = 'bit_reversed_positive');

CREATE TABLE Singers (
  id INT64 GENERATED BY DEFAULT AS IDENTITY,
  name STRING(MAX)
) PRIMARY KEY (id);

PostgreSQL

ALTER DATABASE db SET spanner.default_sequence_kind = 'bit_reversed_positive';

CREATE TABLE Singers (
  id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
  name character varying,
  PRIMARY KEY(id)
);

רצף הפוך של ביטים

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

ב-Spanner, משתמשים בהצהרות DDL יחד עם המאפיין bit_reversed_positive כדי ליצור, לשנות או להסיר רצף שמפיק ערכים חיוביים הפוכים (GoogleSQL או PostgreSQL).SEQUENCE

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

כשמגדירים עמודה עם ביטוי DEFAULT שמשתמש בפונקציה GET_NEXT_SEQUENCE_VALUE של GoogleSQL או בפונקציה nextval של PostgreSQL כערך ברירת המחדל שלה, מערכת Spanner קוראת לפונקציה באופן אוטומטי ומכניסה לעמודה את ערכי הפלט שהתקבלו אחרי היפוך הביטים. רצפים הפוכים של ביטים שימושיים במיוחד למפתחות ראשיים, כי הערכים ההפוכים של הביטים מפוזרים באופן שווה במרחב המפתחות, כך שהם לא גורמים לנקודות חמות.

בדוגמה הבאה מוצג איך ליצור רצף עם היפוך סיביות וטבלה שבה עמודת המפתח משתמשת ברצף כערך ברירת המחדל:

GoogleSQL

CREATE SEQUENCE SingerIdSequence OPTIONS (
  sequence_kind="bit_reversed_positive"
);

CREATE TABLE Singers (
  SingerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE SingerIdSequence)),
  Name STRING(MAX),
  Rank INT64
) PRIMARY KEY (SingerId);

PostgreSQL

CREATE SEQUENCE SingerIdSequence bit_reversed_positive;

CREATE TABLE Singers (
  SingerId bigint DEFAULT nextval('SingerIdSequence'),
  Name text,
  PRIMARY KEY (SingerId)
);

אחר כך אפשר להשתמש בהצהרת ה-SQL הבאה כדי להוסיף את ערך המפתח הראשי ולהחזיר אותו:

GoogleSQL

INSERT INTO Singers (Name) VALUES ('Example Singer')
THEN RETURN SingerId;

PostgreSQL

INSERT INTO Singers (name) VALUES ('Example Singer')
RETURNING SingerId;

ההצהרה הזו מחזירה תוצאה שדומה לתוצאה הבאה:

SingerId
3458764513820540928

יצירת טבלה בלי להגדיר מפתח ראשי

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

אפשר ליצור טבלה בלי להגדיר מפתחות ראשיים באופן הבא:

GoogleSQL

CREATE TABLE Singers (
  Name STRING(MAX),
  Rank INT64
);

PostgreSQL

CREATE TABLE Singers (
  Name text,
  Rank bigint
);

הסכימה שמתקבלת מוצגת כך:

GoogleSQL

CREATE TABLE Singers (
  Name STRING(MAX),
  Rank INT64,
  rowid INT64 NOT NULL GENERATED BY DEFAULT AS IDENTITY
    (BIT_REVERSED_POSITIVE) HIDDEN
) PRIMARY KEY (rowid);

PostgreSQL

CREATE TABLE Singers (
  Name text,
  Rank bigint,
  rowid bigint GENERATED BY DEFAULT AS IDENTITY
    (BIT_REVERSED_POSITIVE) NOT NULL HIDDEN,
  PRIMARY KEY (rowid)
);

אי אפשר להוסיף מפתח ראשי לטבלה שיצרתם בעבר בלי להגדיר מפתח ראשי.

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

GoogleSQL

CREATE TABLE Singers (
  rowid INT64,
  Name STRING(MAX),
);

PostgreSQL

CREATE TABLE Singers (
  rowid bigint,
  Name text
);

מערכת Spanner מגדירה את העמודה rowid לערך HIDDEN. המשמעות היא שהעמודה לא מופיעה בשאילתת SELECT *:

GoogleSQL

SELECT * FROM Singers;

/*-------+------+
 | Name  | Rank |
 +-------+------+
 | Alice | 1    |
 | David | 2    |
 +-------+------*/

PostgreSQL

SELECT * FROM Singers;

/*-------+------+
 | Name  | Rank |
 +-------+------+
 | Alice | 1    |
 | David | 2    |
 +-------+------*/

אבל אפשר לשלוח שאילתה מפורשת לעמודה המוסתרת rowid:

GoogleSQL

SELECT rowid FROM Singers;

/*---------------------+
 | rowid               |
 +---------------------+
 | 3458764513820540928 |
 +---------------------*/

PostgreSQL

SELECT rowid FROM Singers;

/*---------------------+
 | rowid               |
 +---------------------+
 | 3458764513820540928 |
 +---------------------*/

למרות שהעמודה rowid לא מופיעה בשאילתה SELECT *, העמודה rowid מופיעה בטבלאות INFORMATION_SCHEMA. לדוגמה, אפשר להריץ את השאילתה הבאה:

GoogleSQL

SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name LIKE 'PK_%' AND table_name = 'Singers';

/*-------------+
 | column_name |
 +-------------+
 | rowid       |
 +-------------*/

PostgreSQL

SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name LIKE 'PK_%' AND table_name = 'singers';

 /*-------------+
 | column_name |
 +-------------+
 | "rowid"     |
 +-------------*/

תרחישים לשימוש ב-UUID וברצפים כערכי ברירת מחדל למפתחות ראשיים

התרחישים של UUID ורצפים כוללים את האפשרויות הבאות:

  • אפליקציות חדשות
  • מיגרציות

בקטעים הבאים מתואר כל תרחיש.

אפליקציות חדשות

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

אם האפליקציה הקיימת שלכם דורשת מפתחות INT64 ב-GoogleSQL או מפתחות bigint ב-PostgreSQL,‏ Spanner מציע את אובייקט הסכימה של רצף חיובי הפוך סיביות (PostgreSQL או GoogleSQL).

מיגרציות

כדי להעביר טבלאות ל-Spanner, יש לכם את האפשרויות הבאות:

  • אם אתם משתמשים ב-UUID במסד הנתונים של המקור, ב-Spanner, אתם יכולים להשתמש בעמודה מסוג UUID ובפונקציית ה-UUID שנוצרה ( GoogleSQL או PostgreSQL) כערך ברירת המחדל שלה.
  • אם אתם משתמשים במפתח ראשי מסוג מספר שלם, והאפליקציה שלכם צריכה רק שהמפתח יהיה ייחודי, אתם יכולים להשתמש בעמודת מפתח ב-INT64 ולהשתמש ברצף חיובי הפוך סיביות כערך ברירת המחדל של המפתח הראשי. מידע נוסף מופיע במאמר בנושא העברת עמודות של מפתחות הפוכים בביטים.
  • אם אתם משתמשים במפתח מונוטוני, כמו הסוג SERIAL של PostgreSQL או המאפיין AUTO_INCREMENT של MySQL, ואתם צריכים מפתחות מונוטוניים חדשים ב-Spanner, אתם יכולים להשתמש במפתח מורכב כי Spanner לא תומך בדרך ליצירת ערכים מונוטוניים. מידע נוסף זמין במאמרים החלפת הסדר של המפתחות וגיבוב של המפתח הייחודי ופיזור פעולות הכתיבה על פני רסיסים לוגיים.
  • אם האפליקציה שלכם מבצעת היפוך ביטים ידני של מפתח INT64 ב-GoogleSQL או של מפתח bigint ב-PostgreSQL, אתם יכולים להשתמש ברצף חיובי עם היפוך ביטים (GoogleSQL או PostgreSQL) כדי ליצור ערכי מפתח חדשים. למידע נוסף, ראו העברת עמודות של מפתחות עם היפוך ביטים.

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