Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Wydajność
Forum PHP.pl > Forum > PHP > Object-oriented programming
angerthor
Witam,

Jestem bardziej programistą Javy, jak PHP, dlatego nie jest pewien jaki sposób pisania kodu jest najbardziej wydajny.

Załóżmy, że mam obiekt komentarz, który w konstruktorze łączy się z bazą danych i pobiera z niej wszystkie wartości odnośnie danego komentarza, typu np. id_komentarz, tresc, autor no i id_artykul, czy id artykulu w bazie do którgo komentarz się odnosi.

I teraz, aby wydajnie pobierać dalsze informację odnośnie tego artykułu z poziomu klasy komentarz, mam jako pole pustą zmienną, która jest inicjalizowana raz, kiedy na obiekcie pola komentarz wywoła się metoda chcąca pobrać dodatkowe info odnośnie komentarza, np. getArticleTitle();. Inicjalizacja czyli wywołanie konstruktora z parametrem id, który łączy się z bazą danych i pobiera dane artykułu.

Zastanawiam się tylko, czy to jest dobre rozwiązanie pod względem wydajności i bezpieczeństwa.

Dzięki za wszelkie opinie
Crozin
1. Takiej metody jak Comment::getArticleTitle() w ogóle nie powinno* być. Możesz utworzyć Comment::getArticle(), która zwróci obiekt artykułu powiązanego z danym komentarzem, a ten z kolei może udostępniać metodę Article::getTitle().
2. Generalnie próbujesz utworzyć ORMa - może warto by zapoznać się z już istniejącymi? Doctrine2 czerpie garściami z Hibernate, którego może już znasz - nie powinieneś mieć problemów ze zrozumieniem jego działania. Zresztą może po prostu warto użyć owego Doctrine (czy też innego ORMa) zamiast pisać od podstaw własnego?
3. Takie "późne pobranie" danych dot. artykułu, dopiero w momencie gdy są rzeczywiście potrzebne (tutaj: przy wywołaniu metody Comment::getArticle()) nazwa się Lazy Loading - i jest to świetne rozwiązanie gdy np. pobieramy 50 komentarzy przyporządkowanych do różnych artykułów, ale tylko w przypadku jednego komentarza chcemy uzyskać dostęp do artykułu powiązanego z nim.

* Jeżeli już, to powinna być tzw. metodą-proxy, tj.:
  1. public function getArticleTitle() {
  2. return $this->getArticle()->getTitle();
  3. }
Jednak w tym przypadku stworzenie takiej metody wydaje się być kompletnie bezpodstawne i niepotrzebne.
angerthor
Rozumiem, jest jeszcze jedna możliwa opcja - "ściągnięcie sobie potrzebnych danych sql'owym joinem i zapisanie jako pola obiektu komentarza". W tym przypadku, zamiast tworzyć cały obiekt Artykuł (który będzie musiał pobrać z bazy wiele niepotrzebnych z punktu widzenia komentarza danych), tworzymy tylko kilka pól w klasie Comment i na nich zapisujemy potrzebne dane artykułu.

Który sposób jest wydajniejszy w przypadku gdy, chcemy dla komentarza znać tylko tytuł artykułu ? Czy złożone zapytania sqlowe pobierające z kilku tabel są bardzo mało wydajne ?
Crozin
Nie, danych dot. artykułu nie powinieneś pakować bezpośrednio do obiektu komentarza - w końcu to komentarz, nie artykuł. Możesz oczywiście pobrać część danych dot. artykułu (np. tylko jego tytuł), ale nawet wtedy powinieneś dla artykułu utworzyć nowy obiekt.

Cytat
Czy złożone zapytania sqlowe pobierające z kilku tabel są bardzo mało wydajne ?
To zależy. Zapytania z JOINami są oczywiście bardziej obciążające, ale z reguły są lżejsze niż N zapytań. W końcu JOINy to esencja relacyjnych baz danych.
angerthor
Mam kolejne pytanie, również związane z wydajnością.

Otóż użytkownicy, za różne działania otrzymują punkty (m.in.) za komentowanie. Aby obliczyć aktualny stan punktów trzeba pobrać dane z kilku tabel.

I teraz chciałbym wyświetlać kilku użytkowników o największej liczbie punktów. Najprostszym rozwiązaniem byłoby utworzenie obiektów wszystkich użytkowników ( w konstruktorze są obliczane punkty) i sprawdzenie który ma najwięcej.

Jednak stworzenie obiektów dla kilkudziesięciu tysięcy użytkowników za każdym nowym odświeżeniem strony nie jest zbyt wydajne. Drugim sposobem jest stworzenie w tabeli user komorki punkty i napisanie skryput, ktory bedzie co jakis czas, np 15 min tworzyl te wszystkie obiekty obliczal punkty i wpisywal je do konkretnej komorki. Dzieki temu, gdy pozniej bedziemy chcieli wyswietlic najbardziej aktywnych, wystarczy, iz pobierzemy z tabeli ta jedna komorke z punktami.

Ma to wg was sens ?
Crozin
Nie ma.

Skoro punkty są przyznawane przy jakiś akcjach: dodanie komentarza, dodanie materiału, dodanie aktualności, dodanie galerii itp. i odejmowane w momencie gdy na przykład moderator usunie komentarz to po prostu po dodaniu komentarza zwiększasz użytkownikowi ilość punktów. Przy usunięciu zmniejszasz:

  1. START TRANSACTION;
  2. INSERT INTO comment VALUES (...);
  3. UPDATE member SET points = points + :newCommentPoints WHERE id = :id;
  4. COMMIT;
Jeżeli ilość przyznawanych punktów ma być zawsze stała dla danego zasobu i jesteś pewien, że nie będziesz w niedalekiej przyszłości zmieniał tej wartości sensownym wydaje się utworzenie wyzwalacza, który zajmie się drugim zapytaniem:
  1. CREATE TRIGGER increasePointsAfterNewComment AFTER INSERT ON comment FOR EACH ROW BEGIN
  2. UPDATE member SET points = points + 3 WHERE id = NEW.member_id;
  3. END;


PS. Tutaj akurat w przykładzie MySQL, ale każdy liczący się RDBMS również ma takie funkcje delikatnie różniące się swoją składnią.
phpion
Ale kombinujesz... Ja bym to zrobił tak:
1. Dodał dodatkową kolumnę do tabeli użytkowników przechowującą punkty.
2. Napisał triggery, które będą aktualizowały tą wartość w momencie np. dodawania artykułów (czegoś, za co są punkty).
3. Pobierał z bazy dane odpowiednio posortowane i na ich podstawie tworzył obiekty w PHP.
Quadina
Jak dla mnie optymalnym rozwiązaniem będzie stworzenie dodatkowej kolumny z punktami i odswierzanie jej tylko wtedy kiedy zachodzi zmiana - tak jak piszesz, np. dodanie nowego komentarza. Wtedy nie będziesz musiał obciążać systemu co 15 minut liczeniem, oraz będziesz miał w miarę aktualne dane w kolumnie. Możesz też np. raz na dzień w nocy przeprowadzać synchronizację punktów robiąc obliczenie dla wszystkich użytkowników.
dr4ko
Tak naprawdę, to "optymalność" rozwiązania zależy tylko i wyłącznie od potrzeb. Bo optymalnie, pod kątem ewentualnych statystyk/rankingów, byłoby stworzenie oddzielnej tabeli z kolumnami o przykładowych nazwach: user_id, points_count, action_type gdzie action_type to typ zdarzenia, za które przyznano punkty. W ten sposób można pobrać nie tylko sumaryczną ilość punktów danego użytkownika ale też ilość punktów za daną aktywność. No ale chyba trochę odbiegłem od tematu.

Cytat
Najprostszym rozwiązaniem byłoby utworzenie obiektów wszystkich użytkowników ( w konstruktorze są obliczane punkty) i sprawdzenie który ma najwięcej.


Pod żadnym pozorem tego nie rób. Za pomocą jednego zapytania sql jesteś w stanie znaleźć id użytkownika z największą ilością punktów i tylko dla niego potem utworzyć obiekt.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.