Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Duże zużycie ram przy skrypcie wywołanym w konsoli
Forum PHP.pl > Forum > PHP
athabus
Mam taki problem z pogranicza php.

W skrócie na serwerze stoi aplikacja webowa z API. Na tym samym serwerze wywołuję skrypt php w konsoli, który używa tego api do aktualizacji pewnych rzeczy.

Cała filozofia skryptu wygląda tak:
  1. $items=pobierzDane();
  2.  
  3. foreach ($items as item){
  4. $api->updateItem($item)
  5. }


Wszystko działa ładnie, ale... problem pojawia się, gdy jest bardzo dużo elementów do aktualizacji (kilkadziesiąt tysięcy wywołań api w pętli). Po prostu z czasem rośnie zużycie ram i wszystko zaczyna działać coraz wolniej i wolniej. Np. przez pierwsze 2-4 minuty mam aktualizacje 2-5 elementów na sekundę, a np. w 20 minucie działania skryptu jest to 1 element na 2 sekundy.

Jest jakiś sposób aby tą pamięć jakoś uwolnić z poziomu skryptu konsolowego? Nie wiem ogólnie nawet o co dokładnie pytam - chodzi mi o coś takiego aby zwolnić wszystkie zasoby i iść dalej. Ogólnie myślicie, że problemem jest coś w skrypcie konsolowym czy jednak po prostu bardzo duża ilość zapytań do API powoduje taki efekt? Wydaje mi się, że problem leży raczej w skrypcie konsolowym, bo api za każdym razem jest wywoływane i po wywołaniu w teorii pamięć powinna się zwalniać.

Jeśli w czymś może to pomóc to samo api pochodzi ze skryptu Prestashop i chodzi o aktualizację danych produktów, kombinacji etc.
nospor
1)
A czy to API nie posiada metody, ktora pozwala wyslac do update wiecej niz jedno item naraz?

2)
Jesli pobierasz kilkadziesiat tysiecy elementow na raz do tablicy, to juz samo to moze poleciec po ramie. Nie masz mozliwosci zczytywania danych z bazy po rekordzie czyli bez korzystania z bufora?
athabus
Ad1. Niestety nie posiada. Co gorsza to jest API, gdzie aktualizacja to pobranie danego rekordu i potem odesłanie wyniku zmodyfikowanego, czyli update to minimum 2 zapytania do API. Często nawet więcej niż 2 bo rekordy są powiązane i trzeba podobnie updatować te rekordy - ilość zapytań rośnie więc bardzo szybko.

Ad2. W tym przypadku z zapytania powstaje tablica dość mała. Rekordów jest dużo, ale raczej lekkie dane. W omawianym przypadku to po prostu lista użytkowników z podstawowymi danymi, czyli email, login, hasło itp. To raczej nie kłopot w tym konkretnym przypadku. Mówiąc o ramie to mam na myśli tutaj localhosta z 8gb ramu lub serwer vps z 4gb ramu, więc raczej jest to coś "grubszego". Na localhoście jak włączyłem sobie podgląd zużycia ramu, to wygląda to tak, że na początku jest ok, ale z czasem ram jest wykorzystany w 100% i zaczyna się orka po procesorze.

Oczywiście wystarczy przerwać skrypt i puścić go od nowa - i znów przez pierwsze 2-3 minuty działa sprawnie i stopniowo zwalnia.

Na razie po prostu stosuję dzielenie danych na kilka partii, ale z czysto "naukowej" ciekawości szukam lepszego rozwiązania. W perspektywie mam do napisania skrypt, który wywoła api w pętli jakieś... milion razy i chciałbym to zrobić w jednym wywołaniu bez dzielenia na kawałki.
Crozin
Zapewne w pobierzDane bądź updateItem (tak na 90% to właśnie tu jest problem) dochodzi do jakiegoś wycieku pamięci. Jednak bez podglądu kodu możemy sobie jedynie gdybać.
Pyton_000
A nie możesz sobie napisać raw sql które zrobią to co chcesz?
athabus
Cytat(Crozin @ 25.04.2017, 13:13:27 ) *
Zapewne w pobierzDane bądź updateItem (tak na 90% to właśnie tu jest problem) dochodzi do jakiegoś wycieku pamięci. Jednak bez podglądu kodu możemy sobie jedynie gdybać.


Też to podejrzewam, ale zastanawiałem się czy nie ma jakiegoś fixa typu wywołanie "freeMemory()", który by to naprawił. Ale coś czuję, że nie ma.

Kodu nie wklejam, bo oczywiście w rzeczywistości jest bardziej skomplikowany i trudno byłoby dojść bez testów co jest nie tak.


Cytat(Pyton_000 @ 25.04.2017, 13:34:30 ) *
A nie możesz sobie napisać raw sql które zrobią to co chcesz?


Tak mogę, ale to też się wiąże z pewną niedogodnością. Taki raw SQL po aktualizacji może dać nieoczekiwane efekty uboczne. Np. zmiana pewnej cechy obiektu wywołuje efekt w postaci zmian w kilku innych miejscach - np. zmieniasz nazwę produktu co powoduje update cache atrybutów dla wyszukiwarki itp. Po aktualizacji to się może zmienić i coś tam jeszcze dojść. Używając API wiem, że dany obiekt zostanie zmodyfikowany wraz z "przyległościami" nawet jeśli w międzyczasie coś się zmieni po aktualizacji, a tak po każdej aktualizacji trzeba by przeglądać kod i szukać różnic.
Crozin
1. PHP sam w sobie robi takie "freeMemory()" - poczytaj o GC. Nie zmienia to jednak faktu, że nadal możesz doprowadzać czy to do wycieków pamięci czy poprzez zachowywanie (w jakiś zapewne niebezpośredni sposócool.gif referencji do jakiś obiektów uniemożliwiać GC-owi zwolnienie zasobów zajmowanych przez nie.
2. Już samo wykorzystanie foreach'a może negatywnie wpływać na zużycie pamięci.
3. Domyślam się, że prawdziwy kod jest sporawy, ale bez pokazania go nie ma możliwości zasugerowania jakiegokolwiek sensownego rozwiązania.
athabus
Dzięki Crozin - Garbage Collector znam, ale chyba faktycznie sprowadza się do tego, że za bardzo na nim tutaj polegam. Spróbuję w wolnej chwili kod zmodyfikować - zacznę od ręcznego pousuwania zmiennych etc. Ale tak na szybko wczoraj zrobiłem reserach w Google i jest sporo tematów. Wygląda na to, że tak jak piszesz i trzeba się głębiej wgryźć w kod i sprawdzić kilka tropów, które przewijały się w dyskusjach na stack overflow.

Dzięki za podpowiedzi - w wolnej chwili przyjrzę się problemowi głębiej. Jak coś znajdę to napiszę w czym u mnie był kłopot.
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.