Marcin Chyłek Blog

Symfony - przyśpieszanie Propela z wykorzystaniem widoków (view) baz danych

Szybkie serwisy, zoptymalizowane pod względem zużycia pamięci i ilości odwołań do bazy – czasami mogą być kluczowym elementem w powodzeniu naszego projektu. Chciałbym przedstawić jedną z takich możliwości, czyli użycie w Symfony widoków baz danych. Przykład można zastosować w bazach: PostgreSQL, MySQL, Oracle, itd. Najważniejszym elementem jest to, czy baza danych obsługuje widoki.

Dla przykładu użyję tabeli użytkownik i grupa.

schema.yml

propel:

  groups:
    id:              { type: integer, required: true, primaryKey: true, autoincrement: true }
    owner_id:        { type: integer, foreignTable: users, foreignReference: id, index: true }
    last_user_id:    { type: integer, foreignTable: users, foreignReference: id, index: true }
    name:            { type: varchar, size: 80, index: true }
    routing_name:    { type: varchar, size: 80, index: true, uniq: true }
    description:     { type: varchar, size: 400 }
    created_at:      { type: timestamp }
    user_count:      { type: integer, index: true }
    position_rank:   { type: integer }

  users:
    id:              { type: integer, required: true, primaryKey: true, autoincrement: true }
    login:           { type: varchar, size: 15, index: true, uniq: true }
    password:        { type: varchar, size: 255 }
    name:            { type: varchar, size: 255 }
    created_at:      { type: timestamp }
    updated_at:      { type: timestamp }
    last_login_at:   { type: timestamp }
    last_request_at: { type: timestamp }
    group_id:        { type: integer, foreignTable: groups, foreignReference: id, index: true }

Załóżmy, że mamy template w którym pokazujemy 20 grup według ilości użytkowników, w której wyświetlamy: nazwę grupy wraz z linkiem do profilu grupy, właściciela grupy, ostatniego dodanego użytkownika do tej grupy i ilość użytkowników w grupie. Stosując klasyczne wykorzystanie modelów Propela, dostaniemy modele grup z zależnymi 2 modelami użytkowników – właściciel i ostatnio dodany użytkownik. Od razu widać że większość danych jest zbędna, a co z tym idzie wykorzystanie pamięci będzie większe i czas zwracania danych z bazy będzie dłuższy.

Definiowanie widoku (views) w schema

Aby rozdzielić pliki w którym są definicje tabel i widoki, warto utworzyć osobny plik w którym zapiszemy naszą definicje widoku.

config/views_schema.yml

propel:
  groups_view:
    _attributes:              { skipSQL: true, readOnly: true }
    id:                       { type: integer }
    name:                     { type: varchar }
    routing_name:             { type: varchar }
    user_count:               { type: integer }
    owner_name:               { type: varchar }
    last_user:                { type: varchar }

Kolejnym krokiem jest przygotowanie SQL z definicją naszego vidoku. Dobrym rozwiązaniem jest by każdy widok umieszczać w osobnym pliku i dodawać do sqldb.map, w celu zbudowania widoku z automatu (nie musimy już ręcznie wywoływać SQL).

Tworzymy plik sql

data/sql/views/groups_view.sql

CREATE OR REPLACE VIEW groups_view AS
  SELECT groups.id,
         groups.name ,
         groups.routing_name,
         groups.user_count,
         owner.login AS owner_name,
         last.login AS last_user
    FROM groups
    JOIN users owner ON owner.id = groups.owner_id
    JOIN users last ON last.id = groups.ast_user_id;

Dodajemy widok do sqldb.map

Edytujemy plik sqldb.map i dodajemy nową pozycję

data/sql/sdldb.map

views/groups_view.sql=propel

Kolejnym krokiem jest zbudowanie bazy danych, dla przykładu użyje polecenia, które tworzy modele, dodaje SQL do bazy i wczytuje przykładowe dane.

./symfony propel:build-all-load

Po wygenerowanie powstał model o nazwie GroupsView, teraz tylko nam zostaje dodanie metody, która będzie zwracać 20 grup z największą ilością użytkowników.

Edytujemy GroupsViewPeer.class.php

Dodajemy metodę

public static function getTopGroups( $limit = 20 )
{
  $c = new Criteria();
  $c->addDescendingOrderByColumn( GroupsViewPeer::USER_COUNT );
  $c->setLimit( $limit );

  return GroupsViewPeer::doSelect( $c );
}

Metoda ta zwraca 20 obiektów, w których mamy tylko i wyłącznie pola, które będą nam potrzebne do wyświetlenia.

Często na rożnych forach spotyka się opinie, że Symfony jest wolnym i zasobożernym frameworkiem, jeśli nie znamy możliwości, zasad jego działania jak i sposobów optymalizacji to niestety ale taka teoria dla pewnych osób jest prawdziwa. Wykorzystując możliwości baz, takie jak plsql, vidoki, triggery i funkcje można osiągnąć rewelacyjne efekty i obsługa milionowych tabel na “słabym” sprzęcie jest czymś realnym.

Kategoria: Bazy danych, MySQL, Oracle, PHP, PostgreSQL, Propel, Symfony | Marcin Chyłek | Komentarze: 9

Powrót do blogowania - podejście II

Po kilku latach przerwy w blogowaniu, w końcu “zmusiłem” się aby kontynuować. Co jakiś czas będę publikował informację, które mogą się w mniejszy czy większym stopniu przydać - przynajmniej mam takie zamiary, co z tego wyjdzie, to się okaże.

Udało mi się przywrócić posty ale problem mam z komentarzami, jeśli się uda to będą, jeśli nie to oczywiście nie będzie :P

Kategoria: Inne | Marcin Chyłek | Komentarze: 2