БД месэнджэра (ч.1): праектуем каркас базы

Як можна перавесці бізнес-патрабаванні ў канкрэтныя структуры дадзеных на прыкладзе праектавання "з нуля" базы для месэнджэра.

БД месэнджэра (ч.1): праектуем каркас базы
Наша база будзе не такой маштабнай і размеркаванай, як ва Укантакце або Badoo, а "каб было", але было добра - функцыянальна, хутка і змяшчалася на адным серверы PostgreSQL - каб можна было разгарнуць асобны асобнік сэрвісу дзесьці на баку, напрыклад.

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

Крок 1: Крыху бізнес-спецыфікі

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

А якія бываюць задачы ў бізнэсу?.. Паглядзім на прыкладзе Васіля - кіраўніка аддзела распрацоўкі.

  • «Мікалай, вось па гэтай задачы патч патрэбен ужо сёння!»
    Значыць, перапіска можа весціся ў кантэксце нейкага дакумента.
  • «Коля, га ўвечар у доту?»
    Гэта значыць, нават у адной пары суразмоўцаў зносіны адначасова могуць весціся. па розных тэмах.
  • "Пётр, Мікалай, паглядзіце ў атачы прайс на новы сервер."
    Так, у аднаго паведамлення можа быць некалькі адрасатаў. Пры гэтым паведамленне можа змяшчаць прымацаваныя файлы.
  • "Сямён, і ты таксама зірні."
    І павінна быць магчымасць ва ўжо існуючую перапіску запрасіць новага ўдзельніка.

Спынімся пакуль на гэтым пераліку «відавочных» запатрабаванняў.

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

Крок 2: Мінімальная лагічная схема

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

Давайце зафіксуем ужо атрыманую лагічную схему адносін сутнасцяў. Для прастаты разумення нашай мадэлі скарыстаемся самым прымітыўным варыянтам адлюстравання ER-мадэлі без ускладненняў UML або IDEF-натацый:

БД месэнджэра (ч.1): праектуем каркас базы

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

Малюйце схемы як мага прасцей - Большасць тых, каму вы іх будзеце паказваць, не з'яўляюцца экспертамі ў чытанні UML/IDEF. Але - малюйце абавязкова.

Крок 3: Накідваем структуру табліц

Пра імёны табліц і палёўДа «рускіх» назваў палёў і табліц можна ставіцца па-рознаму, але гэтая справа густу. Паколькі у нас у «Тэнзары» няма распрацоўшчыкаў-замежнікаў, а PostgreSQL дазваляе нам даваць назвы хоць іерогліфамі, калі яны заключаны ў двукоссі, то мы аддаем перавагу назваць аб'екты адназначна-зразумела, каб не ўзнікала розначытанняў.
Паколькі паведамленні ў нас пішуць шмат людзей адразу, частка з іх увогуле могуць рабіць гэта у афлайн-рэжыме, то самы просты варыянт выкарыстоўваць UUID у якасці ідэнтыфікатараў не толькі для вонкавых сутнасцяў, але і для ўсіх аб'ектаў усярэдзіне нашага сэрвісу. Прычым генераваць іх можна нават на кліенцкім баку - гэта дапаможа нам падтрымаць адпраўку паведамленняў пры кароткачасовай недаступнасці БД, а верагоднасць калізіі вельмі малая.

Чарнавая структура табліц у нашай базе набудзе вось такі выгляд:
Табліцы: RU

CREATE TABLE "Тема"(
  "Тема"
    uuid
      PRIMARY KEY
, "Документ"
    uuid
, "Название"
    text
);

CREATE TABLE "Сообщение"(
  "Сообщение"
    uuid
      PRIMARY KEY
, "Тема"
    uuid
, "Автор"
    uuid
, "ДатаВремя"
    timestamp
, "Текст"
    text
);

CREATE TABLE "Адресат"(
  "Сообщение"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Сообщение", "Персона")
);

CREATE TABLE "Файл"(
  "Файл"
    uuid
      PRIMARY KEY
, "Сообщение"
    uuid
, "BLOB"
    uuid
, "Имя"
    text
);

Табліцы: EN

CREATE TABLE theme(
  theme
    uuid
      PRIMARY KEY
, document
    uuid
, title
    text
);

CREATE TABLE message(
  message
    uuid
      PRIMARY KEY
, theme
    uuid
, author
    uuid
, dt
    timestamp
, body
    text
);

CREATE TABLE message_addressee(
  message
    uuid
, person
    uuid
, PRIMARY KEY(message, person)
);

CREATE TABLE message_file(
  file
    uuid
      PRIMARY KEY
, message
    uuid
, content
    uuid
, filename
    text
);

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

Крок 4: Высвятляем невідавочныя запатрабаванні

Усё, мы спраектавалі базу, у якую можна выдатна пісаць і неяк чытаць.

Давайце паставім сябе на месца карыстальніка нашага сэрвісу - што мы захочам рабіць з яго дапамогай?

  • Апошнія паведамленні
    Гэта храналагічна адсартаваны па розных прыкметах рэестр "маіх" паведамленняў. Дзе я адзін з адрасатаў, дзе я аўтар, дзе мне напісалі, а я не адказаў, дзе не адказалі мне, …
  • Удзельнікі перапіскі
    Хто ўвогуле ўдзельнічае ў гэтым доўгім-доўгім чаце?

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

Непрадугледжаныя карыстацкія задачы могуць паставіць тоўсты крыж на прадукцыйнасці.

Крок 5: Разумная дэнармалізацыя

Абедзве нашыя праблемы дапамогуць вырашыць дадатковыя табліцы, у якія мы будзем дубляваць частку дадзеных, Неабходных для фарміравання на іх падыходных да нашых задач індэксаў.
БД месэнджэра (ч.1): праектуем каркас базы

Табліцы: RU

CREATE TABLE "РеестрСообщений"(
  "Владелец"
    uuid
, "ТипРеестра"
    smallint
, "ДатаВремя"
    timestamp
, "Сообщение"
    uuid
, PRIMARY KEY("Владелец", "ТипРеестра", "Сообщение")
);
CREATE INDEX ON "РеестрСообщений"("Владелец", "ТипРеестра", "ДатаВремя" DESC);

CREATE TABLE "УчастникТемы"(
  "Тема"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Тема", "Персона")
);

Табліцы: EN

CREATE TABLE message_registry(
  owner
    uuid
, registry
    smallint
, dt
    timestamp
, message
    uuid
, PRIMARY KEY(owner, registry, message)
);
CREATE INDEX ON message_registry(owner, registry, dt DESC);

CREATE TABLE theme_participant(
  theme
    uuid
, person
    uuid
, PRIMARY KEY(theme, person)
);

Тут мы прымянілі два тыповыя падыходы, якія прымяняюцца пры стварэнні дапаможных табліц:

  • Множанне запісаў
    Фарміруем па адным зыходным запісе паведамлення адразу некалькі запісаў-следстваў у розныя віды рэестраў для розных уладальнікаў - як для адпраўніка, так і для атрымальніка. Затое кожны з рэестраў зараз кладзецца на індэкс - бо ў тыпавым выпадку мы захочам бачыць толькі першую старонку.
  • Унікалізацыя запісаў
    Пры кожнай адпраўцы паведамлення ўнутры канкрэтнай тэмы дастаткова праверыць, ці існуе ўжо такі запіс. Калі не - дадаем яе ў наш "слоўнік".

У наступнай частцы артыкула гаворка пойдзе пра укараненне секцыянавання у структуру нашай базы.

Крыніца: habr.com

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