Baza komunikatorów (część 1): projektowanie frameworka bazowego

Jak przełożyć wymagania biznesowe na konkretne struktury danych na przykładzie projektowania od podstaw bazy komunikatorów.

Baza komunikatorów (część 1): projektowanie frameworka bazowego
Nasza baza nie będzie tak duża i rozproszona, jak VKontakte lub Badoo, ale „aby było”, ale było dobrze – funkcjonalnie, szybko i zmieścić się na jednym serwerze PostgreSQL - żeby można było gdzieś z boku wdrożyć oddzielną instancję usługi np.

Dlatego nie będziemy poruszać zagadnień shardingu, replikacji i systemów rozproszonych geograficznie, ale skupimy się na rozwiązaniach obwodów wewnątrz bazy danych.

Krok 1: Niektóre szczegóły biznesowe

Nie będziemy projektować naszego przekazu abstrakcyjnie, ale zintegrujemy go z otoczeniem korporacyjna sieć społecznościowa. Oznacza to, że nasi ludzie nie „tylko korespondują”, ale komunikują się ze sobą w kontekście rozwiązywania określonych problemów biznesowych.

A jakie są zadania firmy?.. Spójrzmy na przykład Wasilija, szefa działu rozwoju.

  • „Nikołaju, do tego zadania potrzebujemy dzisiaj łatki!”
    Oznacza to, że korespondencja może być prowadzona w kontekście niektórych dokument.
  • „Kolya, wybierasz się dziś wieczorem do Dota?”
    Oznacza to, że nawet jedna para rozmówców może komunikować się jednocześnie na różne tematy.
  • „Piotr, Nikołaj, w załączniku znajdziecie cennik nowego serwera.”
    Zatem jedna wiadomość może mieć kilku odbiorców. W takim przypadku wiadomość może zawierać Załączone pliki.
  • „Siemion, ty też spójrz”.
    Powinna istnieć możliwość włączenia się w istniejącą korespondencję zaproś nowego członka.

Zatrzymajmy się na razie na tej liście „oczywistych” potrzeb.

Bez zrozumienia zastosowanej specyfiki problemu i narzuconych mu ograniczeń, projektuj skuteczny schemat bazy danych, aby go rozwiązać, jest prawie niemożliwy.

Krok 2: Minimalny obwód logiczny

Na razie wszystko przebiega bardzo podobnie do korespondencji e-mailowej – tradycyjnego narzędzia biznesowego. Tak, „algorytmicznie” wiele problemów biznesowych jest do siebie podobnych, dlatego narzędzia do ich rozwiązywania będą strukturalnie podobne.

Naprawmy już uzyskany logiczny diagram relacji encji. Aby ułatwić zrozumienie naszego modelu, użyjemy najbardziej prymitywnej opcji wyświetlania modele oddziałów ratunkowych bez komplikacji związanych z notacjami UML lub IDEF:

Baza komunikatorów (część 1): projektowanie frameworka bazowego

W naszym przykładzie osoba, dokument i binarna „treść” pliku to byty „zewnętrzne”, które istnieją niezależnie bez naszej usługi. Dlatego w przyszłości będziemy je po prostu postrzegać jako linki „gdzieś” według UUID.

Rysować schematy tak proste, jak to możliwe - większość osób, którym je pokażesz, nie jest ekspertami w czytaniu UML/IDEF. Ale pamiętaj, aby narysować.

Krok 3: Szkicowanie struktury tabeli

Informacje o nazwach tabel i pól„Rosyjskie” nazwy pól i tabel można traktować inaczej, ale to kwestia gustu. Ponieważ tutaj, w Tensorze nie ma zagranicznych programistów, a PostgreSQL pozwala nam nadawać nazwy nawet hieroglifami, jeśli takowe są ujęte w cudzysłów, wówczas wolimy nazywać obiekty jednoznacznie i wyraźnie, aby nie było żadnych rozbieżności.
Ponieważ wiele osób pisze do nas wiadomości na raz, niektóre z nich mogą nawet to zrobić nieaktywny, to najprostsza opcja używaj identyfikatorów UUID jako identyfikatorów nie tylko dla podmiotów zewnętrznych, ale także dla wszystkich obiektów znajdujących się w naszym serwisie. Co więcej, można je wygenerować nawet po stronie klienta – ułatwi nam to obsługę wysyłania wiadomości w sytuacji, gdy baza danych jest chwilowo niedostępna, a prawdopodobieństwo kolizji jest wyjątkowo niskie.

Struktura tabeli roboczej w naszej bazie danych będzie wyglądać następująco:
Stoły: 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
);

Tabele: 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
);

Najprostszą rzeczą przy opisywaniu formatu jest rozpoczęcie „rozwijania” wykresu połączenia z tabel, do których nie ma odniesień siebie nikomu.

Krok 4: Poznaj nieoczywiste potrzeby

To wszystko, zaprojektowaliśmy bazę danych, w której możesz pisać doskonale i jakoś czytać.

Postawmy się w sytuacji użytkownika naszego serwisu – co chcemy z tym zrobić?

  • Ostatnie wiadomości
    To posortowane chronologicznie rejestr „moich” wiadomości oparty na różnych kryteriach. Gdzie jestem jednym z odbiorców, gdzie jestem autorem, gdzie do mnie pisali, a ja nie odpowiedziałem, gdzie mi nie odpowiedzieli…
  • Uczestnicy korespondencji
    Kto w ogóle uczestniczy w tej długiej, długiej czacie?

Nasza struktura pozwala nam rozwiązać oba te problemy „w ogólności”, ale nie szybko. Problem polega na sortowaniu w ramach pierwszego zadania nie można utworzyć indeksu, odpowiedni dla każdego z uczestników (i będziesz musiał wyodrębnić wszystkie rekordy) i rozwiązać drugi, którego potrzebujesz wyodrębnij wszystkie wiadomości w tym temacie.

Niezamierzone zadania użytkownika mogą być pogrubione krępuj się produktywnością.

Krok 5: Inteligentna denormalizacja

Obydwa nasze problemy rozwiążą dodatkowe tabele, w których to zrobimy zduplikowaną część danych, niezbędne do utworzenia na nich indeksów odpowiednich dla naszych zadań.
Baza komunikatorów (część 1): projektowanie frameworka bazowego

Stoły: RU

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

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

Tabele: 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)
);

Tutaj zastosowaliśmy dwa typowe podejścia stosowane przy tworzeniu tabel pomocniczych:

  • Mnożenie rekordów
    Wykorzystując jeden zapis wiadomości początkowej, tworzymy kilka zapisów uzupełniających w różnych typach rejestrów dla różnych właścicieli – zarówno dla nadawcy, jak i odbiorcy. Ale teraz każdy z rejestrów spada na indeks - w końcu w typowym przypadku będziemy chcieli zobaczyć tylko pierwszą stronę.
  • Unikalne rekordy
    Za każdym razem, gdy wysyłasz wiadomość w ramach konkretnego tematu, wystarczy sprawdzić, czy taki wpis już istnieje. Jeśli nie, dodaj to do naszego „słownika”.

W dalszej części artykułu będziemy rozmawiać implementacja partycjonowania w strukturę naszej bazy danych.

Źródło: www.habr.com

Dodaj komentarz