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

עובדה היא כי ככל שגודל הטבלה גדל, כך גם "עומק" האינדקסים גדל. - אם כי לוגריתמית. אבל לאורך זמן זה מאלץ את השרת לבצע את אותן משימות קריאה/כתיבה לעבד פי הרבה יותר דפי נתוניםמאשר בהתחלה.
כאן זה בא להצלה חתך.
הרשו לי לציין שאיננו מדברים על פיצול, כלומר הפצת נתונים בין מסדי נתונים או שרתים שונים. כי אפילו חלוקת הנתונים ל כמה שרתים, לא תיפטר מבעיית "התנפחות" האינדקסים לאורך זמן. ברור שאם אתה יכול להרשות לעצמך להפעיל שרת חדש בכל יום, אז הבעיות שלך לא יהיו עוד בכלל במישור של מסד נתונים ספציפי.
נשקול לא סקריפטים ספציפיים ליישום מחיצות "בחומרה", אלא את הגישה עצמה - מה ואיך צריך "לחתוך לפרוסות", ולמה רצון כזה מוביל.
מוּשָׂג
בואו נגדיר שוב את המטרה שלנו: אנחנו רוצים לוודא שהיום, מחר ובעוד שנה, כמות הנתונים הנקראת על ידי PostgreSQL במהלך כל פעולת קריאה/כתיבה תישאר זהה.
לכל נתונים שנצברו באופן כרונולוגי (הודעות, מסמכים, יומנים, ארכיונים,...) הבחירה הטבעית כמפתח מחיצה היא תאריך/שעה של האירוע. במקרה שלנו, אירוע כזה הוא רגע שליחת ההודעה.
שימו לב שמשתמשים כמעט תמיד לעבוד רק עם ה"עדכניים ביותר". נתונים כאלה - הם קוראים את ההודעות האחרונות, מנתחים את היומנים האחרונים,... לא, כמובן, הם יכולים לגלול אחורה בזמן, אבל הם עושים זאת לעתים רחוקות מאוד.
מהאילוצים הללו ברור שפתרון המסר האופטימלי יהיה קטעים "יומיים". - אחרי הכל, המשתמש שלנו יקרא כמעט תמיד את מה שבא לו "היום" או "אתמול".
אם אנחנו כותבים וקוראים כמעט רק בקטע אחד במהלך היום, אז זה גם נותן לנו שימוש יעיל יותר בזיכרון ובדיסק - מכיוון שכל אינדקסי הסעיפים מתאימים בקלות ל-RAM, בניגוד לאלו "הגדולים והשמנים" בכל הטבלה.
צעד אחר צעד
באופן כללי, כל מה שנאמר למעלה נשמע כמו רווח אחד מתמשך. וזה בר השגה, אבל בשביל זה נצטרך להתאמץ - כי ההחלטה לחלק את אחד הישויות מובילה לצורך "לראות" את המשויך.
מסר, תכונותיו ותחזיותיו
מכיוון שהחלטנו לחתוך הודעות לפי תאריכים, הגיוני לחלק גם את הישויות-המאפיינים התלויים בהן (קבצים מצורפים, רשימת נמענים), וכן גם לפי תאריך ההודעה.
מכיוון שאחת המשימות האופייניות שלנו היא בדיוק צפייה ברישומי הודעות (שלא נקראו, נכנסות, הכל), זה גם הגיוני "למשוך אותם" לחלוקה לפי תאריכי הודעות.

אנו מוסיפים את מפתח המחיצות (תאריך ההודעה) לכל הטבלאות: נמענים, קובץ, רישום. אינך צריך להוסיף אותו להודעה עצמה, אלא להשתמש ב-DateTime הקיים.
אשכולות
מכיוון שיש רק נושא אחד למספר הודעות, אין דרך "לחתוך" אותו באותו דגם; אתה צריך להסתמך על משהו אחר. במקרה שלנו זה אידיאלי תאריך ההודעה הראשונה בהתכתבות - כלומר, רגע היצירה, למעשה, של הנושא.

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

מכיוון שיהיו מעט שינויים ברשימת תאריכי ההודעות עבור כל התכתבות בודדת (הרי כמעט כל ההודעות נופלות על 1-2 ימים סמוכים), אתמקד באפשרות זו.
בסך הכל, מבנה מסד הנתונים שלנו קיבל את הצורה הבאה, תוך התחשבות בחלוקה למחיצות:
טבלאות: RU, אם יש לך סלידה מהאלפבית הקירילי בשמות של טבלאות/שדות, עדיף לא להסתכל
-- секции по дате сообщения
CREATE TABLE "Сообщение_YYYYMMDD"(
"Сообщение"
uuid
PRIMARY KEY
, "Тема"
uuid
, "ДатаТемы"
date
, "Автор"
uuid
, "ДатаВремя" -- используем как дату
timestamp
, "Текст"
text
);
CREATE TABLE "Адресат_YYYYMMDD"(
"ДатаСообщения"
date
, "Сообщение"
uuid
, "Персона"
uuid
, PRIMARY KEY("Сообщение", "Персона")
);
CREATE TABLE "Файл_YYYYMMDD"(
"ДатаСообщения"
date
, "Файл"
uuid
PRIMARY KEY
, "Сообщение"
uuid
, "BLOB"
uuid
, "Имя"
text
);
CREATE TABLE "РеестрСообщений_YYYYMMDD"(
"ДатаСообщения"
date
, "Владелец"
uuid
, "ТипРеестра"
smallint
, "ДатаВремя"
timestamp
, "Сообщение"
uuid
, PRIMARY KEY("Владелец", "ТипРеестра", "Сообщение")
);
CREATE INDEX ON "РеестрСообщений_YYYYMMDD"("Владелец", "ТипРеестра", "ДатаВремя" DESC);
-- секции по дате темы
CREATE TABLE "Тема_YYYYMMDD"(
"ДатаТемы"
date
, "Тема"
uuid
PRIMARY KEY
, "Документ"
uuid
, "Название"
text
);
CREATE TABLE "УчастникТемы_YYYYMMDD"(
"ДатаТемы"
date
, "Тема"
uuid
, "Персона"
uuid
, PRIMARY KEY("Тема", "Персона")
);
CREATE TABLE "ДатыСообщенийТемы_YYYYMMDD"(
"ДатаТемы"
date
, "Тема"
uuid
PRIMARY KEY
, "Дата"
date
);
תחסוך שקל יפה
ובכן, מה אם אנחנו לא משתמשים בהתבסס על הפצה של ערכי שדות (דרך טריגרים והורשה או PARTITION BY), ו"ידנית" ברמת האפליקציה, תבחין שהערך של מפתח המחיצות כבר מאוחסן בשם הטבלה עצמה.
אז אם אתה כזה האם אתה מודאג מאוד מכמות הנתונים המאוחסנים?, אז תוכל להיפטר מהשדות ה"נוספים" הללו ולהתייחס לטבלאות ספציפיות. נכון, כל הבחירות ממספר סעיפים במקרה זה יצטרכו לעבור לצד האפליקציה.
מקור: www.habr.com
