БД месэнджэра (ч.2): секцыянуем «нажывую»

Мы ўдала спраектавалі структуру нашай PostgreSQL-базы для захоўвання перапіскі, мінуў год, карыстачы актыўна яе напаўняюць, вось у ёй ужо мільёны запісаў, і… нешта ўсё пачало падтармажваць.

БД месэнджэра (ч.2): секцыянуем «нажывую»
Справа ў тым, што з ростам аб'ёму табліцы расце і "глыбіня" індэксаў - хоць і лагарыфмічна. Але з часам гэта прымушае сервер для выканання тых жа задач чытання/запісы. апрацоўваць у разы больш старонак дадзеных, чым у пачатку.

Вось тут на дапамогу і прыходзіць секцыянаванне.

Заўважу, што прамова пайдзе не аб шардынгу, гэта значыць размеркаванні дадзеных паміж рознымі базамі ці серверамі. Таму што, нават падзяліўшы дадзеныя на некалькі сервераў, вы ніяк не пазбавіцеся ад праблемы "распухання" індэксаў з часам. Зразумела, што калі вы можаце дазволіць сабе кожны дзень уводзіць у строй новы сервер, то вашы праблемы будуць ляжаць ужо зусім не плоскасці канкрэтнай БД.

Мы ж разгледзім не канкрэтныя скрыпты для рэалізацыі секцыянавання "ў жалезе", а сам падыход - што і як варта "парэзаць на дзелькі", і да чаго такое жаданне прыводзіць.

Канцэпт

Яшчэ раз вызначым нашу мэту: мы жадаем зрабіць так, каб і сёння, і заўтра, і праз год колькасць чытэльных PostgreSQL дадзеных пры любой аперацыі чытання/запісы заставалася прыкладна адным і тым жа.

Для любых храналагічна назапашваных дадзеных (паведамленні, дакументы, логі, архівы, …) натуральным выбарам у якасці ключа секцыянавання з'яўляецца дата/час падзеі. У нашым выпадку такой падзеяй з'яўляецца момант адпраўкі паведамлення.

Заўважым, што карыстачы практычна заўсёды працуюць толькі з "апошнімі" такімі дадзенымі - чытаюць апошнія паведамленні, аналізуюць апошнія логі, ... Не, вядома, яны могуць прагартаць і далей назад у часе, толькі робяць гэта вельмі рэдка.

З гэтых абмежаванняў становіцца відавочна, што аптымальным рашэннем для паведамленняў будуць "посуточные" секцыі - бо амаль заўсёды наш карыстальнік будзе чытаць тое, што прыйшло яму "сёння" ці "ўчора".

Калі мы на працягу дня пішам і чытаем практычна толькі ў адну секцыю, то гэта дае нам яшчэ і больш эфектыўнае выкарыстанне памяці і дыска - паколькі ўсе індэксы секцыі лёгка змяшчаюцца ў аператыўку, у адрозненне ад "вялікіх і тоўстых" па ўсёй табліцы.

паэтапны

Увогуле, усё сказанае вышэй гучыць як адзін суцэльны профіт. І ён дасягальны, але дзеля гэтага нам давядзецца добра пастарацца - таму што рашэнне секцыянаваць адну з сутнасцяў прыводзіць да неабходнасці «распілоўваць» і звязаныя з ёй.

Паведамленне, яго ўласцівасці і праекцыі

Раз мы вырашылі парэзаць паведамленні па дат, то і залежныя ад іх сутнасці-уласцівасці (прыкладзеныя файлы, спіс адрасатаў) разумна таксама дзяліць, і таксама па даце паведамлення.

Паколькі адной з тыпавых нашых задач з'яўляецца як раз прагляд рэестраў паведамленняў (непрачытаныя, уваходныя, усе), то іх таксама лагічна "ўцягнуць" у секцыянаванне па дат паведамленняў.

БД месэнджэра (ч.2): секцыянуем «нажывую»

Дадаем ключ секцыянавання (дату паведамлення) ва ўсе табліцы: адрасаты, файл, рэестры. У само паведамленне можна не дадаваць, а выкарыстоўваць існуючы ДатаЧас.

Тэмы

Паколькі тэма адна на некалькі паведамленняў, то яе парэзаць у той жа мадэлі ўжо ніяк не атрымаецца, трэба абапірацца на нешта іншае. У нашым выпадку ідэальна падыходзіць дата першага паведамлення ў перапісцы - Гэта значыць момант стварэння, уласна, тэмы.

БД месэнджэра (ч.2): секцыянуем «нажывую»

Дадаем ключ секцыянавання (дату тэмы) ва ўсе табліцы: тэма, удзельнік.

Але зараз у нас узнікаюць адразу дзве праблемы:

  • у якой секцыі шукаць паведамленні па тэме?
  • у якой секцыі шукаць тэму ад паведамлення?

Можна, вядома, працягваць шукаць па ўсіх секцыях, але гэта будзе вельмі сумна, і звядзе на нішто ўсе нашы выйгрышы. Таму, каб ведаць, дзе канкрэтна шукаць, зробім лагічныя спасылкі/паказальнікі на секцыі:

  • у паведамленні дадамо поле з датай тэмы
  • да тэмы дадамо набор дат паведамленняў гэтай перапіскі (можна асобнай табліцай, а можна і масівам дат)

БД месэнджэра (ч.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), а "уручную" на ўзроўні прыкладання, то можна заўважыць, што значэнне ключа секцыянавання ўжо захоўваецца ў назве самой табліцы.

Таму калі вы настолькі моцна хвалюецеся за аб'ём захоўваемых дадзеных, то ад гэтых "лішніх" палёў можна і пазбавіцца і звяртацца адрасна да пэўных табліц. Праўда, усе выбаркі з некалькіх секцый у гэтым выпадку ўжо давядзецца вынесці на бок прыкладання.

Крыніца: habr.com

Дадаць каментар