cronjob php script & memory
ik maak gebruik van een cronjob on elke 2 uur een bestand te downloaden (csv, of zip)
dit bestand wordt uitgepakt indien nodig, en dan het csv bestand in stukken gehakt (indien nodig)
vervolgens importeer ik de csv op basis van mijn eigen database.
de import bevat voor elke regel een SELECT en indien er een resultaat is, enkele simpele berekeningen en een UPDATE achteraan.
Nou heb ik het probleem dat elke 2 uur m'n CPU omhoog vliegt en ook ALLE geheugen waardoor er op dat moment t/m 5 minuten ofzo vrijwel geen verkeer mogelijk is.
Op dat moment kan ik nieteens met ssh inloggen.
Als ik wel ingelogt ben kan ik op dat moment nieteens 'top' uitvoeren (linux server) dat er geen geheugen beschikbaar is.
Dus mijn vraag is eigenlijk:
-hoe weet ik hoeveel geheugen die cronjobs gebruiken
-hoe kan ik een memory limit geven
ik heb een simpele VPS 2x duo core cpu's en 4GB geheugen
Ik ben bang dat het niet veel uitmaak dat ik upgrade naar 4 CPU en 8GB geheugen?
Ik hoor graag van jullie!
Ik zou eerder kijken waarom er veel geheugen wordt gebruikt. Kijk eens naar memory_get_peak_usage()
thanks, maar.. op welke momenten moet ik dit doen dan?
Aan het begin, halverwege aan het einde?
Het script geeft geen output, en ik draai t dmv nohup
Ik kan het 'loggen'
Ik zit te denken om t te loggen met timestamps naar een file
begin, voor en na download, voor/na uitpak, einde
zou ik ook bij elke regel moeten loggen?
het gaat in principe maar om een csv bestand van max. 16mb
want ik hak ze in stukken ook.
kan ik niet gewoon een max. memory geven, alleen dan is de vraag hoe bepaal ik het limiet het beste
Gewijzigd op 05/03/2017 19:13:53 door Dennis WhoCares
Je kan de memorywaarde prima loggen in een txt-file. Als er veel geheugen wordt gebruikt denk ik vaak aan een infinite loop.
ik zal eens een beetje gaan loggen. Per script een eigen file en daarna kan ik hier snel ff een grafiek oid van maken :)
Ik had daarnet wel weer een piek, maar dat bleek CPU te zijn op een process van mysql
"/usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir............"
Alleen zegt het verder ook niet veel
De CPU voor dit process sprong van 45 seconden naar 120%
Maar ik wil dit zsm goed oplossen omdat ik in toekomst nog meer van dit soort imports zal gaan draaien..
Er is geen 'oneindige' loop in m'n cron scripts.. Dat weet ik 100% zeker, maar sommige kunnen wat langer draaien dan andere, door o.a. downloaden van grote csv of zip file, het 'splitten' van de file(s) om sneller te kunnen verwerken enz.
Ga daarom per onderdeel de performance loggen.
Mysql kan prima met LOAD DATE INFILE een csv bestand inlezen.
Kun je in een tijdelijke tabel pompen.
En daarna zou ik 1x query doen die met een join tussen de doeltabel en de tijdelijke tabel de zaak update.
Klink nu namelijk of je 1000 SELECT query's afvuurt en een berg updates. Waarbij je mogelijk steeds meer PHP variabelen aanmaakt om je geheugen te vullen.
Dan lijken 2 query's me efficiënter
Dat, en een borkend mysql proces geeft ook te denken over de efficientie van je queries, indexes, en mogelijk je mysql configuratie. Kijk hier dus ook zeker naar.
ik ga nu memory logs invoeren en dan weet ik rond 8:30 wellicht iets meer.
Om meer toelichting te geven @Ivo en Ben over de import.
ik heb verschillende csv files, met daarbij allemaal hun eigen kolommen
Een oplossing daarvoor had ik als volgt: een 'settings' file voor de import waarbij ik het volgende gedefineerd heb:
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
$settings = array('import_file' => 'local_path_to_the_csv_file',
'import_colomn_0_to => mijn_database_kolom',
import_colomn_1_to => mijn_database_kolom2',
import_colomn_2_to => '',
import_colomn_3_to => '',
'import_colomn_4_to' => 'mijn_database_kolom3',
...
...
'import_colomn_31_to' => 'mijn_database_kolom4',
'import_uniqueField' => 'mijn_database_kolom_3');
'import_colomn_0_to => mijn_database_kolom',
import_colomn_1_to => mijn_database_kolom2',
import_colomn_2_to => '',
import_colomn_3_to => '',
'import_colomn_4_to' => 'mijn_database_kolom3',
...
...
'import_colomn_31_to' => 'mijn_database_kolom4',
'import_uniqueField' => 'mijn_database_kolom_3');
Hierna volgt vanuit PHP nogmaals een nohup commando dat m'n import script triggered.
Voor elke regel in de csv file, controlleer ik of ik wel alle data heb (op basis van de 'settings' file) die ik nodig heb (het kan weleens wezen dat de csv geen goede opmaak heeft en niet alle kolommen mee komen.
Daarna normaliseer ik de waardes zoals bedragen, voorraad enz
Daarna doe ik een SELECT op import_uniqueField
als er een result is, doe ik deze updaten.
En aan het einde altijd een die(); gewoon voor het geval dat.
@Ivo,
aangezien de CSV files allemaal anders zijn, zou ik deze 'tijdelijke' tabel eerst kunnen creeren (dynamisch of 1 standaard grote tabel maken, met dezelfde veldnamen als m'n settings file, maar dan ... t/m import_colomn_100_to (als voorbeeld)) en dan alles erin zetten
Zou dat inderdaad beter zijn?
vervolgens 1 lange SELECT dmv JOIN om de relatie te leggen tussen import_uniqueField?
Ik moet alsnog alle updates 1 voor 1 doen, omdat voorraad en prijzen nogal wat berekeningen nodig heeft, dus houd ik altijd nog duizenden update queries, + uiteraard maar 1 grote SELECT query
Wellicht een goed idee
Dit is alsnog wel per import file, omdat ze allen anders zijn. En andere 'settings' hebben.
@Ben, de queries zijn simpel: SELECT field_id FROM table WHERE field_id = $cvsRow[$settings['import_uniqueField']]
Aan wat voor configuratie zat je te denken eigenlijk? Ik heb geen aparte configuratie toegepast op de mysql service.
als er een result is, doe ik deze updaten."
Dus als uniqueField = "abc123" niet bestaat, doe je niets en anders doe je een update query?
Dat is dus gelijk aan
UPDATE tabel SET .... WHERE uniqueField="abc123"
als die niet gevonden is, doet zo'n query namelijk precies niets.
Toevoeging op 06/03/2017 09:08:34:
LOAD DATA INFILE kan heel snel een tabel vullen. Met alle kolommen 1 op 1 matchend, of alleen kolom 1, 5 en 23 uit je file.
Je kunt er zelfs nog een bewerking op doen op het moment van importeren:
LOAD DATA INFILE 'file.txt'
INTO TABLE t1
(column1, @var1)
SET column2 = @var1/100;
Maar ook opties als
LOAD DATA INFILE 'file.txt'
INTO TABLE t1
(column1, column2)
SET column3 = CURRENT_TIMESTAMP;
of
LOAD DATA INFILE 'file.txt'
INTO TABLE t1
(column1, @dummy, column2, @dummy, column3);
waarbij dan dus kolom 2 en 4 uit file.txt eigenlijk gewoon overgeslagen worden.
En wat voor soort berekeingen heb je nodig die zo moeilijk zijn mbt de prijs?
Aangezien fgetcsv() een array retourneert, zou je het verschil tussen twee CSV-bestanden bijvoorbeeld kunnen vinden met array_diff().
Bijkomend voordeel is dat je dan ook nog het opschonen van de CSV-bestanden kunt beperken tot de nieuwe of gewijzigde data als je eerst alle overeenkomsten tussen de oorspronkelijke CSV's weggooit.
Code (php)
1
diff --unchanged-line-format= --old-line-format= --new-line-format='%L' oldfile.csv newestfile.csv > result.csv
Dat levert je in result.csv die nieuw toegevoegde regels
en de regels met verschillen.
NB: dit werkt niet per se als de volgorde van de regels kan verschillen. Maar dat is op zich niet erg, want dan krijg je er hooguit wat meer te verwerken.
Inderdaad zou een diff check functie ook al een hoop schelen, hoewel de csv opmaak nogal verschilt per 'aanbieder'.
Vooral de laatste comment van Ivo moet ik eens naar kijken ! Enorm bedankt daarvoor!
Deze moet ik eens goed testen
De prijzen zijn dynamisch op basis van marges,percentages en 'gemonitorde' concurrentie en als basis de inkoop prijs per hoeveelheid uit de csv welke vaak veranderd.
Ik kijk de logs later ook nog even na, heb even wat anders te doen (werk) dus geen tijd voor prive ;-)
Ik ga een pvp maken voor het verbeteren van de 'prepare import' scripts en importscript
- diff csv file => previous == NEW csv file containing only changes
-- run import if needed at all
- only run update query for import, as only changes are being
Zou ik hierbij alsnog gebruik moeten maken van een 'tijdelijke' tabel ?
Ik denk dat dit al een hele verbetering is qua performance, enorm bedankt voor jullie reacties en tips, ik neem ze zeker allemaal in overweging.
De performance van de gehele server is laatste dagen al aan het verbeteren door ssh en ftp connecties alleen vanuit NL toe te staan, en daarnaast nog fail2ban geïnstalleerd en geconfigureerd. (vanwege vele 'hack-attampts')
ik zou graag nog ietwat hulp willen over de diff command :)
door --new-line-format='%L' raken sommige 'records' uit elkaar getrokken.
1 regel dat vele extra enters ontvangt.
met %l krijg ik alles maar op 1 regel... heb je hier meer ervaring mee ?
Ow die heb ik al opgelost met diff -w -B ......
Is het ook mogelijk om nog 'alleen' de verwijderde regels te vinden?
Gewijzigd op 07/03/2017 12:36:53 door Dennis WhoCares
http://www.lmgtfy.com/?q=diff+only+show+removed+lines
here we go...
hahaa ;-) Nee is goed, thanks! ik hoopte dat je dit toevallig wist :)
Thanks ik ga zowiezo hiermee aan de slag nu. Scheelt een hele hoop tijd met imports ;-)
Heb je echt alle informatie nodig die je ophaalt? Heb je ook middelen die ervoor zorgen dat er geen informatie gewijzigd kan worden tijdens het uitvoeren van deze batch (denk bijvoorbeeld aan een transactie)?
Queries in een loop zijn meestal ook een "red flag".
ik moet idd ook even onderzoeken hoe ik de mysql server beter kan configureren, ben er wel achter dat de mysql service de boel laat hangen... wellicht dat ik toch wel een memory upgrade moet doen.
Voor de update-import haal ik nu geen info meer op, maar het enige wat ik voorheen ophaalde was: id,item_id,price,stock
Deze script(s) zijn de enige die de data kan/mag 'updaten' eindgebruikers kunnen dit niet.
Van alle velden in de csv files gebruik ik ook eigenlijk maar 3 velden.
En ik update er 5
Wat bedoel je met "red flag", ik begrijp dat op moment dat ik een record aanpas, iemand anders niet precies op datzelfde moment dezelfde record zou mogen/kunnen aanpassen, maar in dit geval is dit ook niet mogelijk.
Gewijzigd op 07/03/2017 15:06:59 door Dennis WhoCares
Dennis WhoCares op 07/03/2017 14:02:54:
Doorgaans zal de oorzaak niet bij de MySQL service liggen en is er weinig winst te halen met beter configureren. Probeer toch te analyseren, uit te zoeken, waar het probleem ontstaat. Ik lees dat je met nohup werkt en hoeveel processsen onstaan er dan met nohup? Honderden? Het inlezen van een csv of txt is peanuts voor MySQL zeker als je het met LOAD INFILE doet. Er is met deze tooling enorm veel mogelijk qua logica, zie ook de tips an Ivo. Ik vrees toch dat je programmatuur de oorzaak is van de hangup. Hoeveel records moet je inlezen per file?ik moet idd ook even onderzoeken hoe ik de mysql server beter kan configureren, ben er wel achter dat de mysql service de boel laat hangen... wellicht dat ik toch wel een memory upgrade moet doen.
Ook wanneer de gegevens verspreid of verrijkt moeten worden is het zinvol om eerst in te lezen in een platte tabel en aan de hand daarvan door te werken met updates en/of inserts om te verspreiden of te verrijken. Misschien kan je dan zelfs de php stap eruit werken. Misschien gaan er massa's onnodige gegevens (Selects) van MySQL naar php en wordt de MySQL server daar een beetje moe van ;)
Zorg voor logging in je proces, met timestamps zodat je kan meten. Schrijf de logging naar een /var/log/xxx.log file zodat MySQL daar niet voor belast wordt. Zet eens een tail -f op die logfile om te checken.
Gewijzigd op 07/03/2017 17:02:51 door John D