База данни на Messenger (част 2): разделяне "за печалба"

Успешно проектирахме структурата на нашата база данни PostgreSQL за съхранение на кореспонденция, измина една година, потребителите активно я попълват и сега милиони записи, и ... нещо започна да забавя всичко.

База данни на Messenger (част 2): разделяне "за печалба"
Факт е, че с нарастването на обема на таблицата расте и “дълбочината” на индексите - макар и логаритмично. Но с течение на времето принуждава сървъра да изпълнява същите задачи за четене/запис обработва многократно повече страници с данниотколкото в началото.

Тук идва на помощ разделяне.

Отбелязвам, че не става въпрос за шардинг, тоест разпределение на данни между различни бази данни или сървъри. Защото дори разделянето на данните по малко сървъри, няма да се отървете от проблема с "набъбването" на индексите с течение на времето. Ясно е, че ако можете да си позволите да пускате нов сървър в експлоатация всеки ден, тогава вашите проблеми вече няма да лежат на равнината на определена база данни.

Ще разгледаме не конкретни скриптове за реализиране на разделяне "хардуерно", а самия подход - какво и как трябва да се "нареже на парчета" и до какво води такова желание.

понятие

Още веднъж, ние определяме нашата цел: искаме да сме сигурни, че днес, утре и година по-късно количеството данни, прочетени от PostgreSQL за всяка операция за четене/запис, остава приблизително същото.

За всеки хронологично натрупани данни (съобщения, документи, регистрационни файлове, архиви, ...) естественият избор за ключ за дял е дата/час на събитието. В нашия случай това събитие е в момента, в който съобщението е изпратено.

Имайте предвид, че потребителите почти винаги работа само с най-новите такива данни - те четат последните съобщения, анализират последните логове, ... Не, разбира се, те могат да превъртат по-назад във времето, само че го правят много рядко.

От тези ограничения става очевидно, че оптималното решение за съобщения ще бъде "ежедневни" раздели - в крайна сметка почти винаги нашият потребител ще прочете това, което му е дошло "днес" или "вчера".

Ако пишем и четем на практика само в една част през деня, то и това ни дава по-ефективно използване на паметта и диска - тъй като всички индекси на секции лесно се побират в RAM, за разлика от "големите и дебели" в цялата таблица.

стъпка по стъпка

Като цяло всичко изброено по-горе звучи като една голяма печалба. И е постижимо, но за това ще трябва да се потрудим - т.к решението за разделяне на едно от образуванията води до необходимостта от "видене" и свързване.

Съобщение, неговите свойства и проекции

Тъй като решихме да изрежем съобщенията по дати, също така е разумно да разделим зависещите от тях свойства на обекта (прикачени файлове, списък с получатели) и също и по дата на публикуване.

Тъй като една от типичните ни задачи е просто да преглеждаме регистрите на съобщенията (непрочетени, входящи, всички), също е логично да ги „начертаем“ в секции по дати на съобщения.

База данни на Messenger (част 2): разделяне "за печалба"

Добавяме ключа на дяла (датата на съобщението) към всички таблици: получатели, файл, регистри. Не можете да добавяте към самото съобщение, а да използвате съществуващия DateTime.

Темы

Тъй като темата е една за няколко съобщения, няма да е възможно да я "нарежете" в един и същи модел, трябва да разчитате на нещо друго. Перфектен за нашия случай. дата на първото съобщение в кореспонденцията - тоест моментът на създаване, всъщност, на темата.

База данни на Messenger (част 2): разделяне "за печалба"

Добавете разделителен ключ (тема дата) към всички таблици: тема, член.

Но сега имаме два проблема едновременно:

  • в кой раздел да търся съобщения по темата?
  • в кой раздел да търся темата на съобщението?

Можете, разбира се, да продължите да търсите във всички секции, но това ще бъде много тъжно и ще обезсили всичките ни печалби. Ето защо, за да знаем къде точно да търсим, ще направим логически връзки / указатели към раздели:

  • добавете към съобщението поле за дата на тема
  • добавете към темата зададена дата на съобщението това съответствие (можете да използвате отделна таблица или можете да използвате масив от дати)

База данни на Messenger (част 2): разделяне "за печалба"

Тъй като ще има малко промени в списъка с дати на съобщения за всяка отделна кореспонденция (в края на краищата почти всички съобщения попадат в 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

Добавяне на нов коментар