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 |

9 komentarzy »

  1. Z doświadczenia w wykorzystywaniu widoków na bazie postgresql mogę także potwierdzić że zastosowanie widoków - co za tym idzie ograniczenie ilości wyciąganych danych, zdecydowanie przyśpiesza działanie strony.

    Do tego widoki umożliwiają nam tworzenie zaawansowanych zapytań oraz operowaniu na zwracanych danych poprzez prosty model. A wyobraźcie sobie tworzenie, Criteria z JOIN na kilku tabelach, do tego grupowanie etc. :)

    Heh co do opinii że symfony samo w sobie jest wolne także słyszałem, to tutaj zgodzę się z Marcinem że wina leży po stronie nieumiejętnego operowania ORM’em.

    Komentarz - autor: Whisller — 2009-02-08 @ 22:57

  2. Rozumiem, że wszyscy iście prawdziwie doświadczeni programiści twierdzą, że Symfony jest demonem szybkości? Zastosowanie widoków, buforowanie, rozsądne wykorzystanie ORMa… naprawdę nie wiedziałem, że bez Symfony nie jest to możliwe…

    Komentarz - autor: sobstel - niedoświadczony programista — 2009-02-09 @ 00:39

  3. sobstel - Widzę, że nie zrozumiałeś mojego posta. Nie chodziło mi o żadne porównianie, uważam, że jeśli któś z czegoś korzysta to powinien znać możliwości jak i utrudnienia. To ze ORM’y akurat są jakie są (nie mowie tutaj tylko o PHP) to wiadomo, że wydajnośc trzeba zdobywać trochę “kombinując”

    Komentarz - autor: admin — 2009-02-09 @ 00:51

  4. @sobstel: ORM pomaga w tworzeniu i utrzymaniu aplikacji. Jeżeli więc istnieją sposoby na przyspieszenie działania to jak najbardziej należy to robić… Ja widoki stosuje zazwyczaj do raportowania i sprawdzaja się znakomicie.

    Komentarz - autor: Kowalikus — 2009-02-19 @ 09:14

  5. Widoki w mysql nie przyspieszaja niczego ani o jote.
    Lepiej jest uzywac rozwiazania podobnego do tego:
    SELECT * FROM
    (
    SELECT *
    FROM costam c
    JOIN coinnego i ON c.id = i.id
    ) AS tabelka

    Komentarz - autor: Maciej — 2009-03-01 @ 11:18

  6. Widok nic Ci nie przyspieszy (mowiac o bazie), ale widokiem dla Propela zawezasz ilosc pol jakie musi wyciagnac + nie ma zaleznych obiektow dla propela, wiec mniejsza pamiec zostanie wykorzystana. Nie chodzilo ze widok sam w sobie jest lepszy, lecz o propela. Co do twojego przykladu to raczej nie ma sensu bo i tak zracasz wszystkie pola. Przeczytaj jeszcze raz moj art i zwroc uwage na to, ze w widoku nie wyciagam wszystkich pol z zlaczonych tabel.

    Komentarz - autor: admin — 2009-03-01 @ 12:34

  7. zgadzam sie.
    wg mnie widoki po prostu upraszczaja cala sprawe.

    w sumie nie ma roznicy miedzy wyzej podanym kodem a odpowiednim kodem poza tym ze:
    - widok moze byc cachowany
    - wiekszosc RDBS przechowuje widok w zoptymalizowanej formie, w przypadku zapytania jak powyzej server bd musi optymalizowac zapytanie za kazdym razem (co w przypadku niektorych zlozonych zapytan moze byc kosztowne), zwlaszcza jesli to samo zapytanie jest wykonywane tysiace razy na sekunde - zysk jest wtedy znaczny

    Komentarz - autor: charlie_edinburgh_napier — 2009-03-03 @ 19:31

  8. A czy np idzie w takim widoku umieścić kilka zapytań do DB ??

    Pozdrawiam

    Komentarz - autor: jarre1987 — 2009-04-17 @ 13:02

  9. [...] w przykładzie z optymalizacją Propela dobrym nawykiem jest rozdzielenie różnego typu struktur na osobne pliki schema.yml. Główną [...]

    Pingback - autor: Symfony, Propel, PostgreSQL - Multi database (obsługa wielu baz danych w aplikacji) | Marcin Chyłek Blog — 2009-05-28 @ 00:48

Kanał RSS z komentarzami do tego wpisu. Adres URL dla TrackBacków

Dodaj komentarz