Marcin Chyłek Blog

Symfony - Propel Lazy Load

Lazy load (wzorzec projektowy) – w przypadku Propela oznaczenie atrybutu modelu lazyLoad powoduje, że zostanie on zwrócony w momencie jego jawnego wywołania. Wykorzystuje się to w przypadku jeśli nie potrzebujemy danego atrybutu (z powodu jego rozmiaru) lub chcemy wykorzystać w późniejszym etapie (w widoku). Najczęściej stosuje się to do typów text (longvarchar), blob, clob.

Definicja w schema.yml

schema.yml

documents:
  id:         { type: integer, required: true, primaryKey: true, autoincrement: true }
  created_at: { type: timestamp }
  name:       { type: varchar, size: 100 }
  content:    { type: longvarchar, lazyLoad: true }

Pobieramy obiekt documents, atrybut content ma ustawionym lazyLoad, zostaje on pominięty w zapytaniu SQL. Jeśli wywołamy metodę ->getContent(), zostanie wykonane dodatkowe zapytanie i pole zostanie zwrócone. Lazy load można wykorzystać w celu zoptymalizowania obiektów Propela, jednak trzeba pamiętać że w momencie żądania zwrócenia atrybutu zostanie wykonane dodatkowe zapytanie do bazy danych.

Kategoria: Bazy danych, PHP, Propel, Symfony | Marcin Chyłek | Komentarze: 0

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

Ruby on Rails (RoR) with Oracle

Na stronie ORACLE został opublikowany artykuł o połczeniu bazy ORACLE (do przykładu została użyta baza Oracle Database 10g XE) z Ruby on Rails. Artykuł zawiera instalację pod systemem Linux i Windows i sposób użycia.

Na liście znalazły się takie pakiety jak:

  • actionmailer - E-mail support
  • actionpack - Controller and View Framework
  • actionwebservice - Web Service Support
  • activerecord - Object Relational Mapping Layer
  • activesupport - Utility Classes Used by Rails
  • Rails - Ruby-based MVC Web Application Framework
  • Rake - Ruby Build Program

Źródło dokumentu: http://www.oracle.com/technology/pub/articles/saternos-ror-faq.html

Kategoria: Bazy danych, Oracle | Marcin Chyłek | Komentarze: 0

Wykryte błędy w bazach danych

Kilka dni temu natrafiłem na art dotyczący bugów w bazach danych, wykres mówi sam za siebie. Wielkim zdziwieniem jest to, ze MSSQLa w roku 2005 i 2006 nie ma na liście.

Źródło

Kategoria: Bazy danych | Marcin Chyłek | Komentarze: 0

Klucze i indeksy w bazie danych

Wiele osób zaczynających programowanie wykorzystują w swoich aplikacjach bazy danych. Przy projekcie struktury jak i strojeniu zapytań SQL zawsze stykają się z terminem indeksów i kluczy. Chciałbym w krótki sposób przedstawić i pokazać na przykładach wykorzystanie indeksów i kluczy w bazach danych i wyjaśnić dlaczego mają tak wielki wpływ na wydajność wykonania zapytań.

Klucze w bazie danych - zalążek teorii.

Głównymi zastosowaniami kluczy w bazie danych są:

  • poprawienie wydajności działania bazy danych - w operacjach DQL (Data Query Language), takich jak SELECT, gdzie na podstawie kluczy optymalizator bazy danych może dostosować najbardziej optymalny plan wykonania zapytania, co wpływa na szybkość zwracania rekordów jak i obciążenia serwera bazodanowego
  • zapewnienie automatycznej kontroli poprawności operacji przetwarzania danych - przy stosowaniu operacji DML (Data Manipulation Language), takich jak INSERT, UPDATE, DELETE, gdzie na poziomie bazy danych dane są weryfikowane czy aby relacja jest poprawna, czy dane się nie powtarzają, czy przedział jest odpowiedni, lub wywoływana jest dowolna metoda weryfikacji danych (trigger)

Indeksy

Indeks jest specjalną strukturą, wprowadzoną w celu przyspieszenia dostępu do danych. Indeks w bazie danych jest odzwierciedleniem spisu treści w książce.

Wykorzystuje się przy zapytaniach DQL (SELECT), które maja na celu wyszukiwanie odpowiednich wartości w bazie danych. Optymalizator najpierw przeszukuje indeks, który jest uporządkowany a następnie na podstawie indeksu odczytuje odpowiednie rekordy.

Definicja indeksu

CREATE INDEX name_index ON user (name);

Lub przez polecenei ALTER

ALTER TABLE user ADD INDEX (name);

Kiedy indeksy są zalecane?

Największy pożytek ze stosowania indeksów zależy w dużej mierze od selektywności kolumny lub kolumn do redukcji zbioru danych, innym przykładem zastosowania indeksów są kolumny które są często wymieniane w warunkach (WHERE) i złączeniach tabel.

Często w bazach danych spotyka się indeksy bazujące na funkcjach, przykładem może być funkcja UPPER(), dzięki której wydajność bazy danych polepsza się.

CREATE INDEX upper_index ON users (UPPER(name));

Indeks w operacjach DML powoduje spadek wydajności, ponieważ po każdej modyfikacji rekordu indeks jest odbudowywany.

Klucz podstawowy (Primary Key)

Zadaniem klucza podstawowego jest zapewnienie unikalności, zapewnienie że kolumny klucza nie zawierają wartości pustych, możliwości identyfikacji rekordu w bazie danych, jak i szybszego wyszukania interesującego nas rekordu.

Definicja klucza podstawowego

Klucz podstawowy można utworzyć podczas tworzenia tabeli lub modyfikacji tabeli.

Tworzenie tabeli (przykład dla MySQLa z AUTO_INCREMENT):

CREATE TABLE `user`
(
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL
)

Modyfikacja tabeli:

ALTER TABLE `user` ADD PRIMARY KEY (`id`);

Klucz obcy (Foreign Key)

Integracja referencyjna jest jedną z podstawowych cech klucza obcego, w której każda wartość klucza obcego (niepusta) musi odpowiadać jednej z wartości klucza podstawowego (PRIMARY KEY) lub klucza unikalnego (UNIQUE) innej lub tej samej tabeli. Przykładem takim może być użytkownik, który należy do jakiegoś systemu.

Więzy integralności

Klucze obce wymuszają więzy integralności:

  • REFERENCES - źródło klucza obcego (tabela i klucz podstawowy)
  • ON DELETE, ON UPDATE - określenie akcji jaka należy podjąć w chwili usunięcia lub modyfikacji klucza podstawowego
  • SET NULL zastąpienie wartości klucza obcego przez wartość NULL
  • SET DEFAULT zastąpienie wartości klucza obcego przez wartość domyślnie ustawioną podczas definiowania struktury tabeli
  • CASCADE modyfikacja pola klucza obcego na nową wartość pola klucza podstawowego lub usunięcie wszystkich rekordów zawierających odwołanie klucza obcego
  • NO ACTION tylko w przypadku modyfikacji niezmienianie wartości klucza obcego
  • RESTRICT nie dopuszczenie do zmiany

Do największych zalet więzów integralności zalicza się:

  • łatwość deklaracji i modyfikacji
  • jednakowe reguły dla wszystkich użytkowników i aplikacji
  • natychmiastowa informacja zwrotna dla użytkownika (w przypadku błędu)
  • elastyczność (włączane / wyłączane)
  • bardzo dobra wydajność

Przykłady zastosowań więzów integralności.

Dla przykładu użyje 2 tabelek (”user” i “system”) - przykład dla bazy MySQL (tabele musza zastać utworzone jako InnoDB)

CREATE TABLE `user`
(
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR( 100 ) NOT NULL
) ENGINE = innodb;
CREATE TABLE `system`
(
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR( 50 ) NOT NULL
) ENGINE = innodb;

Użytkownik musi zostać przypisany do systemu, więc wystarczy dodać dodatkowe pole do tabeli “user” w którym będzie wskazanie na tabele “system”

ALTER TABLE `user` ADD `system_id` INT NOT NULL;
ALTER TABLE `user` ADD INDEX (`system_id`);

Dodajemy kilka rekordów aby zobrazować przykłady wykorzystania relacji.

INSERT INTO `system` ( `id`, `name` ) VALUES ( 1, 'linux' );
INSERT INTO `system` ( `id`, `name` ) VALUES ( 2, 'windows' );
INSERT INTO `user` (`id`, `name`, `system_id`) VALUES (1, 'SongoQ', 1);
INSERT INTO `user` (`id`, `name`, `system_id`) VALUES (2, 'Marcin', 1);
INSERT INTO `user` (`id`, `name`, `system_id`) VALUES (3, 'Kasia', 2);
INSERT INTO `user` (`id`, `name`, `system_id`) VALUES (4, 'Pawel', 2);

Złączenie tabel da rezultat:

SELECT * FROM `user`, `system` WHERE `user`.`system_id` = `system`.`id`;
+----+--------+-----------+----+---------+
| id | name   | system_id | id | name    |
+----+--------+-----------+----+---------+
|  1 | SongoQ |         1 |  1 | linux   |
|  2 | Marcin |         1 |  1 | linux   |
|  3 | Monika |         2 |  2 | windows |
|  4 | Pawel  |         2 |  2 | windows |
+----+--------+-----------+----+---------+

Następnie przechodzimy do tworzenia relacji między tabelami.

Najczęściej wykorzystywane relacje w bazach danych

RESTRICT

ALTER TABLE `user` ADD CONSTRAINT `user_system_id_fk` FOREIGN KEY (`system_id`)
REFERENCES `system` (`id`);

Usunięcie rekordu z tabeli “system” do której odwołuje się rekord z tabeli “user” spowoduje błąd.

DELETE FROM `system` WHERE `id` = 1;

CASCADE - aktualizacja

ALTER TABLE `user` ADD CONSTRAINT `user_system_id_fk` FOREIGN KEY (`system_id`)
REFERENCES `system` (`id`) ON UPDATE CASCADE;

Zmiana warto??ci klucza podstawowego w tabeli “system” powoduje zmianę klucza obcego w tabeli “user”

UPDATE `system` SET `id` = 3 WHERE `id` = 1;
SELECT * FROM `user`, `system` WHERE `user`.`system_id` = `system`.`id`;
+----+--------+-----------+----+---------+
| id | name   | system_id | id | name    |
+----+--------+-----------+----+---------+
|  1 | SongoQ |         3 |  3 | linux   |
|  2 | Marcin |         3 |  3 | linux   |
|  3 | Monika |         2 |  2 | windows |
|  4 | Pawel  |         2 |  2 | windows |
+----+--------+-----------+----+---------+

CASCADE - usuwanie

ALTER TABLE `user` ADD CONSTRAINT `user_system_id_fk` FOREIGN KEY (`system_id`)
REFERENCES `system` (`id`) ON DELETE CASCADE;

Usunięcie rekordu z tabeli “system” powoduje usunięcie rekorduw z tabeli “user”w której klucz obcy odpowiada kluczowi podstawowemu.

DELETE FROM `system` WHERE `id` = 3;
SELECT * FROM `user`, `system` WHERE `user`.`system_id` = `system`.`id`;
+----+--------+-----------+----+---------+
| id | name   | system_id | id | name    |
+----+--------+-----------+----+---------+
|  3 | Monika |         2 |  2 | windows |
|  4 | Pawel  |         2 |  2 | windows |
+----+--------+-----------+----+---------+

Oczywiście ON DELETE i ON UPDATE można stosować razem.

Indeks unikalny (UNIQUE)

Indeks unikalny służy do wymuszenia na polu wartości unikalnych, czyli żadna wartość w nim wystąpująca nie może sie powtarzać. Indeks unikalny charakteryzuje się dobrą selektywnośca.

ALTER TABLE `users` ADD UNIQUE (`name`)

Indeks złożony

Indeksy złożone składają sie z wielu kolumn, gdzie kolumny mogą być zdefiniowane w dowolnej kolejności. Indeksy złożone są użyteczne, wtedy kiedy w zapytaniu SELECT w warunku WHERE występuje wiecej niż jedno odwołanie do pol tabeli.

ALTER TABLE `user` ADD UNIQUE `name` (`name`, `surname`)

Mam nadzieje, że ten tekst niektórym przybliżył pojęcia związane z kluczami i indeksami. Odnośnie szczegłów i większej liczby przykładów polecam ksiżeczkę z podstawami relacyjnych baz danych. Wydawnictwo Helion.pl ma kilka dobrych pozycji na ten temat.

Kategoria: Bazy danych | Marcin Chyłek | Komentarze: 3
« NowszeStarsze »