БД наMessaging (ч.1): проектиране на основата

Как можем да преведем бизнес изискванията в конкретни структури от данни на примера на проектирането на база за месинджър "от нулата".

БД наMessaging (ч.1): проектиране на основата
Нашата база няма да е толкова мащабна и разпределена, както е при ВКонтакте или Badoo, а "за да има", но да е добре — функционално, бързо и да се побира на един сървър PostgreSQL — за да можем да разположим отделен екземпляр на услугата някъде настрани, например.

Затова няма да разглеждаме въпросите за шардинг, репликация и геораспределени системи, а ще се фокусираме върху схемни решения вътре в БД.

Стъпка 1: Малко бизнес-специфика

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

А какви могат да са бизнес задачите?.. Нека погледнем примера на Василий — ръководител на отдела за разработки.

  • "Николай, за тази задача патчът е нужен днес!"
    Значи, кореспонденцията може да се води в контекста на някакво документ.
  • "Коля, идвай вечерта на дота?"
    Тоест, дори при една двойка събеседници, комуникацията може да се води по различни теми.
  • "Петър, Николай, вижте прикачения файл с цените на новия сървър."
    Така, едно съобщение може да има няколко адресата. При това съобщението може да съдържа прикачени файлове.
  • "Семен, и ти погледни."
    И трябва да има възможност в вече съществуваща кореспонденция да се покани нов участник.

Засега ще спрем на този списък с "очевидни" нужди.

Без да разбираме приложната специфика на задачата и зададените от нея ограничения, е практически невъзможно да проектираме ефективна схема на БД за нейното решение.

Стъпка 2: Минимална логическа схема

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

Нека фиксираме вече получената логическа схема на отношенията между съществата. За удобство на разбирането на нашата модел ще използваме най-примитивния вариант на ER-модел без усложнения от UML или IDEF нотации:

БД наMessaging (ч.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: Разумна денормализация

И двете наши проблеми могат да бъдат решени с помощта на допълнителни таблици, в които ще дублираме част от данните, необходими за формирането на подходящи индекси за нашите задачи.
БД наMessaging (ч.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)
);

Тук приложихме два типични подхода, които се използват при създаване на помощни таблици:

  • Умножаване на записите
    Създаваме по една основна вхoдяща запис на съобщение с веднага няколко произтичащи записа в различни видове списъци за различни собственици — както за подателя, така и за получателя. Но всеки от списъците вече попада в индекса — тъй като в типичния случай ще искаме да виждаме само първата страница.
  • Уникализация на записите
    При всяко изпращане на съобщение в конкретна тема е достатъчно да проверим дали такава запис вече съществува. Ако не — добавяме го в нашия „речник“.

В следващата част на статията ще говорим за внедряване на секциониране в структурата на нашата база.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster