Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

2 Stron V   1 2 >  
Reply to this topicStart new topic
> Optymalizacja skryptu PHP
zbysiusp
post 13.03.2017, 14:20:19
Post #1





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Witam!

Na potrzeby wykresu Highcharts napisałem skrypt PHP, który pobiera dane z bazy MySQL i koduje je do formatu JSON (3 serie danych). Skrypt działa poprawnie, ale czas jego wykonania i pobrania wyniku do przeglądarki jest bardzo długi ok. 10 sekund co stwarza problemy przy wywoływaniu go co 1 minutę wraz z innymi skryptami - zawieszenie przeglądarki po ok. 1 godzinie. W przykładzie poniżej ograniczyłem wynik do 5 minut, ale w rzeczywistości pobierane są dane z miesiąca (43 tys. rekordów). Proszę o radę w jaki sposób zoptymalizować ten skrypt?

Oczekiwany format kodowania:
Wynik działania skrypu

Kod:
  1. $sth = mysql_query("SELECT czas_unix, wind_speed_average, windgust, wind_direction_average FROM tab_czujniki_2 WHERE czas_datetime >= '$minus_data' ORDER BY czas_datetime ASC") or die('Błąd zapytania');
  2.  
  3. $rows = array();
  4. $rows['name'] = 'wind_speed_average';
  5. $rows1 = array();
  6. $rows1['name'] = 'windgust';
  7. $rows2 = array();
  8. $rows2['name'] = 'wind_direction_average';
  9.  
  10. while($r = mysql_fetch_array($sth)){
  11. $rows['data'][] = array(1000*($r['czas_unix']),$r['wind_speed_average']);
  12. $rows1['data'][] = array(1000*($r['czas_unix']), $r['windgust']);
  13. $rows2['data'][] = array(1000*($r['czas_unix']), $r['wind_direction_average']);
  14. }
  15.  
  16. $result = array();
  17. array_push($result,$rows);
  18. array_push($result,$rows1);
  19. array_push($result,$rows2);
  20. print json_encode($result, JSON_NUMERIC_CHECK)


Pozdrawiam
Go to the top of the page
+Quote Post
daro0
post 13.03.2017, 17:20:56
Post #2





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


Masz założony indeks na pole czas_datetime?
Go to the top of the page
+Quote Post
zbysiusp
post 13.03.2017, 17:23:58
Post #3





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Cytat(daro0 @ 13.03.2017, 17:20:56 ) *
Masz założony indeks na pole czas_datetime?


Tak wszystkie rekordy są indeksowane.
Go to the top of the page
+Quote Post
qbson69
post 13.03.2017, 17:41:22
Post #4





Grupa: Zarejestrowani
Postów: 20
Pomógł: 7
Dołączył: 3.01.2016

Ostrzeżenie: (0%)
-----


Problemem chyba nie jest zapytanie, ale duża ilość danych, może trzeba ograniczyć ilość rysowanych punktów na wykresie i pobierać np. co dziesiąty punkt?
Go to the top of the page
+Quote Post
zbysiusp
post 13.03.2017, 17:55:09
Post #5





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Cytat(qbson69 @ 13.03.2017, 17:41:22 ) *
Problemem chyba nie jest zapytanie, ale duża ilość danych, może trzeba ograniczyć ilość rysowanych punktów na wykresie i pobierać np. co dziesiąty punkt?


Ilość pobieranych danych na pewno ma znaczenie, ale wydaje mi się że nie tylko. Ograniczyłem zakres danych na wykresie do 1 dnia (1440 rekordów), a czas wykonania skryptu nadal jest długi ok. 5 sekund.
Go to the top of the page
+Quote Post
viking
post 13.03.2017, 18:54:49
Post #6





Grupa: Zarejestrowani
Postów: 6 380
Pomógł: 1116
Dołączył: 30.08.2006

Ostrzeżenie: (0%)
-----


Sprawdź w jakimś narzędziu typu heidisql ile samo zapytanie się wykonuje, zobacz explain, dołóż indeks ewentualnie. Skoro i tak wszędzie jest mnożenie to dlaczego nie robisz tego raz od razu na bazie? array_push też można pominąć i od razu tworzyć tablicę właściwą.


--------------------
Go to the top of the page
+Quote Post
zbysiusp
post 13.03.2017, 19:56:57
Post #7





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Cytat(viking @ 13.03.2017, 18:54:49 ) *
Sprawdź w jakimś narzędziu typu heidisql ile samo zapytanie się wykonuje, zobacz explain, dołóż indeks ewentualnie. Skoro i tak wszędzie jest mnożenie to dlaczego nie robisz tego raz od razu na bazie? array_push też można pominąć i od razu tworzyć tablicę właściwą.


Dziękuję. Jak pominąć array_push?
Go to the top of the page
+Quote Post
phpion
post 13.03.2017, 22:57:46
Post #8





Grupa: Moderatorzy
Postów: 6 072
Pomógł: 861
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Też obstawiałbym, że problemem jest ilość zwracanych danych. Nawet jeśli samo zapytanie korzysta z indeksów to zwrócenie 43k danych musi zająć trochę czasu. Dla pewności pokaż explain zapytania. Możesz rownież stawiać sobie proste benchmarki typu die('już') w newralgicznych miejscach (wykonanie zapytania, zbudowanie tablicy, kodowanie do JSON) i zobaczysz co tak naprawdę spowalnia.
Go to the top of the page
+Quote Post
zbysiusp
post 13.03.2017, 23:21:09
Post #9





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Cytat(phpion @ 13.03.2017, 22:57:46 ) *
Też obstawiałbym, że problemem jest ilość zwracanych danych. Nawet jeśli samo zapytanie korzysta z indeksów to zwrócenie 43k danych musi zająć trochę czasu. Dla pewności pokaż explain zapytania. Możesz rownież stawiać sobie proste benchmarki typu die('już') w newralgicznych miejscach (wykonanie zapytania, zbudowanie tablicy, kodowanie do JSON) i zobaczysz co tak naprawdę spowalnia.


Dziękuję. W PHP nie poruszam się jeszcze biegle. Rozumiem, że skrypt napisany jest prawidłowo. Nie wiem jak pokazać explain zapytania i postawić benchmarki. Czy mógłbyś na przykładzie mojego skryptu to objaśnić?

Ten post edytował zbysiusp 13.03.2017, 23:21:33
Go to the top of the page
+Quote Post
phpion
post 14.03.2017, 07:11:02
Post #10





Grupa: Moderatorzy
Postów: 6 072
Pomógł: 861
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




1. Explain select czas_unix... i wklej tutaj wynik.
2. Co do tych pseudo benchmarków to zatrzymuj skrypt w określonych miejscach, np. po linii z mysql_query i zobacz, który fragment kodu zabiera najwiecej czasu.
Go to the top of the page
+Quote Post
zbysiusp
post 14.03.2017, 07:51:16
Post #11





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Cytat(phpion @ 14.03.2017, 07:11:02 ) *
1. Explain select czas_unix... i wklej tutaj wynik.
2. Co do tych pseudo benchmarków to zatrzymuj skrypt w określonych miejscach, np. po linii z mysql_query i zobacz, który fragment kodu zabiera najwiecej czasu.


Czas wykonania zapytania w odniesieniu do pobrania wszystkich rekordów z bazy danych jest zależny od obciążenia serwera, ale nie dłuższy niż 2 sekundy (EXPLAIN).

czas zapytania 1
czas zapytania 2

Nie wiem co począć z EXPLAIN:
EXPLAIN
Analizator EXPLAIN

Zwolnienie może być na etapie selekcji rekordów do wyświetlenia. Sekcja kodu przygotowująca przedział czasowy do pobrania rekordów może być nieoptymalnie napisana (kod poniżej) Nie mam pomysłu jak to napisać wydajniej:
  1. $wynik_data = mysql_query("SELECT czas_datetime FROM tab_czujniki_2 ORDER BY czas_datetime DESC LIMIT 1");
  2.  
  3. if(mysql_num_rows($wynik_data) > 0) {
  4. $r = mysql_fetch_assoc($wynik_data);
  5. $datan = $r['czas_datetime'];
  6. $daten = new DateTime($data);
  7. $daten->modify('-49 day');
  8. $minus_data = $daten->format("Y-m-d H:i:00");
  9. }
  10.  
  11.  
  12. $sth = mysql_query("SELECT czas_unix, wind_speed_average, windgust, wind_direction_average FROM tab_czujniki_2 WHERE czas_datetime >= '$minus_data' ORDER BY czas_datetime ASC") or die('Błąd zapytania');


Dziękuję za pochylenie się nad tematem i pomoc.
Pozdrawiam
Go to the top of the page
+Quote Post
daro0
post 14.03.2017, 07:57:23
Post #12





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


Jaki to jest problem napisać sobie prostą klasę profilera i użyć jej w celu dokonania pomiarów, w określonych miejscach? Zresztą wyjdą ciekawe wyniki, bo nie tylko samo zapytanie zajmie czas, to jeszcze operacje na tablicach na tych 40 - 50k pobranych danych, json_encode też się wykona w jakimś czasie. A potem można sobie łatwo jeszcze sprawdzić ile czasu zajmie wyświetlenie tego w oknie przeglądarki w postacji JSON a ile przy uzyciu var_dump albo print_r.
Go to the top of the page
+Quote Post
phpion
post 14.03.2017, 08:28:25
Post #13





Grupa: Moderatorzy
Postów: 6 072
Pomógł: 861
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Tutaj widać, że zapytanie nie korzysta z żadnego indeksu. W kolumnie key masz NULL.
Go to the top of the page
+Quote Post
Pyton_000
post 14.03.2017, 08:53:35
Post #14





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Bo z czego miałby brać indeks skoro nie ma warunku?

@zbysiusp explain musisz dokleić do zapytania z warunkami. A najlepiej zobacz czy masz indeks na `czas_datetime`
Go to the top of the page
+Quote Post
zbysiusp
post 14.03.2017, 09:32:48
Post #15





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Cytat(phpion @ 14.03.2017, 08:28:25 ) *
Tutaj widać, że zapytanie nie korzysta z żadnego indeksu. W kolumnie key masz NULL.

Cytat(Pyton_000 @ 14.03.2017, 08:53:35 ) *
Bo z czego miałby brać indeks skoro nie ma warunku?

@zbysiusp explain musisz dokleić do zapytania z warunkami. A najlepiej zobacz czy masz indeks na `czas_datetime`


Bardzo dziękuję za wszystkie rady, ale proszę o wyrozumiałość z racji braku biegłości w php (początkujący). Jak powinno wyglądać przyporządkowanie indeksu do 'czas_datetime' lub innego pola i zapytanie EXPLAIN w moim przypadku. W zapytaniu jest warunek WHERE określający zakres pobieranych rekordów.

Tabela w mojej bazie danych jest uzupełniana co 1 minutę o jeden rekord i odczytywana również co jedną minutę. Doczytałem trochę o indeksach. Po pracy spróbuję go przyporządkować do kolumny 'czas_datetime' i przetestować. Tylko nie wiem jak powinno wyglądać zapytanie EXPLAIN z tym indeksem.

Ten post edytował zbysiusp 14.03.2017, 09:59:47
Go to the top of the page
+Quote Post
Lion
post 14.03.2017, 09:37:00
Post #16





Grupa: Zarejestrowani
Postów: 148
Pomógł: 14
Dołączył: 23.02.2013

Ostrzeżenie: (0%)
-----


Jeśli masz już indeks na kolumnie z czasem, a zapytanie nadal działa powoli to może warto zastanowić się nad partycjonowaniem tabeli po miesiącach.


--------------------
Go to the top of the page
+Quote Post
zbysiusp
post 14.03.2017, 19:57:25
Post #17





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Założyłem indeks na kolumnie 'czas_datetime'. W jaki sposób ma to usprawnić działanie mojego skryptu? Pomóżcie proszę.
Indeks założony
Zapytanie wykonane z powodzeniem:
EXPLAIN
Analiza EXPLAIN:
Explain Analyzer
Profil szczegółowy:
Profil szczegółowy
Czas wykonania skryptu praktycznie bez zmian:
Czas wykonania skryptu

Ten post edytował zbysiusp 14.03.2017, 22:39:48
Go to the top of the page
+Quote Post
daro0
post 15.03.2017, 11:40:52
Post #18





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


A zdajesz sobie sprawę z tego co w takim przypadku, kiedy np. pobierasz te 40k rekordów wypluwa profiler?

https://s16.postimg.org/nl90e5alh/profiler_1.png
https://s1.postimg.org/892sgpz7z/profiler_2.png

Test na 800k rekordów z losowymi danymi w bazie, profiler frameworka Kohana 3.3. Oprócz zapytań z bazy dałem benchmark na tych operacjach na array w PHP oraz na konwesji do JSON. W tym przypadku ponad 1s zajmuje pobranie danych z bazy i ponad 1s same operacje w PHP i konwersja do JSON. A i jeszcze zanim te dane zostaną wyplute na ekran to też trochę czasu minie.

Daje to wiele do myślenia. To dlatego przy listowaniu danych stosuje się paginację albo wczytuje tylko pewną ilość danych a przy scrollowaniu strony w dół AJAX-em wczytuje kolejne partie danych. Albo w ogóle pewne dane wczytuje AJAX-em z jakimś tam loaderem który każe czekać a nie coś takiego, że zanim się w ogóle strona wyświetli to trzeba czekać nie wiadomo ile.

I nic tu nie dało założenie tego indeksu.
Go to the top of the page
+Quote Post
zbysiusp
post 17.03.2017, 15:44:19
Post #19





Grupa: Zarejestrowani
Postów: 65
Pomógł: 0
Dołączył: 11.07.2009

Ostrzeżenie: (0%)
-----


Witam.
Bardzo dziękuję Wszystkim za rady, sugestie i testy. Wykorzystam je na pewno w rozwiązywaniu problemu.
Pozdrawiam

Cytat(viking @ 13.03.2017, 18:54:49 ) *
Sprawdź w jakimś narzędziu typu heidisql ile samo zapytanie się wykonuje, zobacz explain, dołóż indeks ewentualnie. Skoro i tak wszędzie jest mnożenie to dlaczego nie robisz tego raz od razu na bazie? array_push też można pominąć i od razu tworzyć tablicę właściwą.

Witam.

Przerzucenie mnożenia na poziom bazy danych (przy zapisie rekordu) było przełomowe dla czasu wykonywania się skryptu, który skrócił się z ok. 7-8 sek. do 2 sek. Bardzo dziękuję. Klikam pomógł bardzo!
Jakbyś mógł jeszcze napisać jak pominąć array_push i od razu tworzyć tablicę wyjściową czas skróciłby się jeszcze najpewniej jeszcze bardziej smile.gif

Pozdrawiam
Go to the top of the page
+Quote Post
viking
post 17.03.2017, 16:31:37
Post #20





Grupa: Zarejestrowani
Postów: 6 380
Pomógł: 1116
Dołączył: 30.08.2006

Ostrzeżenie: (0%)
-----


Robisz tutaj 3 klucze stałe więc możesz od razu do tablicy

  1. $result = array();
  2. $result[]['name'] = 'wind_speed_average';
  3. $result[]['name'] = 'windgust';
  4. $result[]['name'] = 'wind_direction_average';
  5.  
  6. // w pętli
  7. $result[0]['data'][] = [1,2];
  8. $result[1]['data'][] = [3,4];
  9. $result[2]['data'][] = [5,6];


--------------------
Go to the top of the page
+Quote Post

2 Stron V   1 2 >
Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 10.08.2025 - 18:08