Na vývojářské konferenci Applu WWDC23 byla představena řada novinek (cz): brýle Apple Vision Pro, MacBook Air 15” s čipem M2, Mac Studio s čipem M2 Max nebo M2 Ultra, Mac Pro s čipem M2 Ultra, iOS 17, iPadOS 17, macOS Sonoma, watchOS 10, …
Chystá se poslední jarní Virtuální Bastlírna. Nachystejte si ledové kávy, mojita a vodní chladiče a pojďte se se strahovskými bastlíři pobavit o technice a bastlení! Ptáte se, co mají bastlíři za novinky? Například se ukázalo, že OLED s SSD1306 ve skutečnosti nejsou nutně jen černobílé. Vyšla také nová verze KiCADu včetně betaverze pluginu pro tvorbu databázových knihoven pro KiCAD v InvenTree a na internetu se objevil USB
… více »6. červen je dnem za skutečný internet (neboli Světový den IPv6). Již tradiční příležitost urgovat svého ISP, kdy zavede do sítě IPv6, ale také příležitost šířit osvětu i mezi netechnické uživatele. V současnosti má IPv6 v ČR jen cca 20 % uživatelů (podle statistik společností Akamai a Google).
Festival plný workshopů, interaktivních činností a především nadšených a zvídavých lidí Maker Faire Prague 2023 proběhne o víkendu 10. a 11. června na Výstavišti Praha.
Byla vydána verze 8.18 z Debianu vycházející linuxové distribuce DietPi pro (nejenom) jednodeskové počítače. Přehled novinek v poznámkách k vydání.
Projekty Blink a Blinkenlights dospěly do verze 1.0. Jedná se o x86-64-linux emulátor a jeho TUI nadstavbu sloužící jako debugger. Blink je v porovnání s qemu-x86_64 menší a rychlejší.
Bylo potvrzeno, že Debian 12 s kódovým jménem Bookworm vyjde v tuto sobotu 10. června.
Byla vydána nová verze 2023.2 linuxové distribuce navržené pro digitální forenzní analýzu a penetrační testování Kali Linux (Wikipedie). Přehled novinek se seznamem nových nástrojů v oficiálním oznámení. Zdůraznit lze předpřipravené obrazy pro Hyper-V nebo to, že ve výchozím prostředí Xfce bylo PulseAudio nahrazeno multimediálním serverem PipeWire.
Tento týden byla vydána nová verze 1.52 webového prohlížeče Brave (Wikipedie, GitHub). Postavena je na Chromiu 114. Z novinek lze vypíchnout možnost povolit vertikální karty (vertical tabs). Také bylo představeno Brave Search API k vyhledávači Brave Search.
Matthias Clasen z Red Hatu oznámil v diskusním listu vývojářů Fedora Linuxu, že tým Red Hat Display Systems se zaměří na Wayland a podporu HDR na Linuxu a přestane spravovat RPM balíčky pro LibreOffice. V další major verzi RHELu už LibreOffice nebude. Pokud se nenajde správce balíčků pro Fedora Linux, zůstane pouze LibreOffice ve Flatpaku.
Řešení dotazu:
Trošku teorie:
Většinou je lepší použít méně selektů než více. Pokud například pracuješ s velkými objemy dat a použiješ z nej jen pár záznamů, potom je ale lepší použít více selektů.
V tvém případě procházíš stejně všechny data a všechny je budeš zpracovávat, proto použij jeden selekt a pouze projekce (vybre jen ty sloupce, které potřebuješ).
Dále použij mysql_* pro procházení dat:
$res = mysql_query('select ...') while(($row = mysql_fetch_array($res)) { }Tady je hezký příklad: http://www.php.net/manual/en/function.mysql-fetch-array.php
SELECT
. Už proto, že kdyby během zpracování každého z těch 3000 mohlo docházet k modifikacím seznamu uživatelů, mohl by v datech vzniknout zmatek. Také by se mohlo případně stát, že by se to "niečo" provedlo jen pro některé uživatele a pro jiné ne.
Uvědom si, že každý SELECT
se kompiluje do pseudokódu a teprve ten se provádí. Pokud se dá příkaz napsat jako jeden SELECT
, je to k databázi mnohem šetrnější a výkonnější. Navíc celá akce proběhne atomicky.
Systémové prostředky by to zahltit nemělo, protože PHP si data z databáze odebírá postupně jak potřebuje. Obvykle bývá nastaven limit paměti pro proměnné v PHP na 32 MB, ale dá se zpracovat i select se stovkami MB.
Spíš bych se ale zamyslel nad tím: Co vlastně s těmi daty potom děláš? Neukládáš je po modifikaci opět do databáze? To bych v cyklu rozhodně nedělal, ale nechal bych to udělat přímo tu databázi.
dotaz na nějakejch 15kB a výsledek ne o moc víc... + len krátke textové veci, mená, emaily, čísla, dátumyMůže ve výsledku znamenat i více než 0.3 MiB.
Budu ho normálně zpracovávat řádku po řádceJestli myslíš fetch_xxx, tak to na věci nic nemění, že data jsou v result setu načtena všechna.
mysql_unbuffered_query()
nedělá to, že by neukládala výsledek na straně klienta, ona ho jen nepřipravuje pro použití v PHP (nedělá z něj typy PHP) a umožňuje zpracování, hned jakmile už nějaká data dojdou. To znamená, že můžete získat rychlost, díky paralelnímu zpracování a ušetřit nějakou paměť díky „nepřekládání dat“, pokud budete dostatečně rychle zpracovávat (což ale v PHP asi těžko půjde předběhnout takový typ dotazu.), ale ty data na straně klienta uložena jsou, jen je hned odebíráte a nejsou tak velká (zvláště pokud je tam spousta smallint, tinyint apod.).mysqli_result::free()
, mysqli_stmt::free_result()
nebo i třeba imagedestroy()
uvolňují prostě paměť ihned, řekněme mimo vyhledávací loop GC.
//pripojení k db
echo memory_get_usage(),"\n";
//$q=mysql_query("SELECT * FROM invoices");
$q=mysql_unbuffered_query("SELECT * FROM invoices"); \\ LIMIT 4000");
sleep(2);
echo memory_get_usage(),"\n";
$s=0;
while($r=mysql_fetch_row($q))
{
$s=$s+$r[0];
//echo memory_get_usage(),", ";
}
echo memory_get_usage(),"\n";
echo memory_get_peak_usage(),"\n";
exit();
Výsledky s normálním dotazem:
685984 4825840 4825840 44789896 44796976s buffered s limitem 4000
686000 1688464 1688464 11289208 11296256výsledky s unbuffered query
685800 695904 695904 696632 716056unbuffered s limitem 4000
686008 689696 689696 690256 715968unbuffered s limitem 20
685816 695920 695920 696648 715216Myslím, že tato data Tvoje tvrzení prostě vyvrací: spotřeba paměti je při unbuffered query defakto identická při 20 i 18000 záznamech. Navíc, kdyby to bylo tak, jak tvrdíš, tak by se data během sleepu načetly a spotřebovali paměť, tam ale k žádnému nárůstu paměti nedochází, k tomu dochází až u buffered query během cyklu. Proto je evidentní, že ani buffered query netahá všechna data hned, pouze je na straně klienta cachuje v okamžiku, kdy jsou poprve přečtena, aby umožnil pohyb zpět po resultsetu (samozřejmě tahá data po "paketech", proto je spotřeba paměti během cyklu vždy pár kroku stejná a pak skočí).
memory_get_usage()
a nebo se použije v PHP něco jako:
$pid = getmypid(); exec("ps -eorss,pid | grep $pid", $output); echo IntVal(trim($output[0])) * 1024,"\n";Takže je blbost co jsem psal v 1. odstavci a o čem jsem byl přesvědčen (nevím proč) a musel jsem se podívat i do zdrojáků PHP-ka a to i do starších verzí (4.x), ale nenašel jsem nic co by odpovídalo tomu co jsem psal, PHP funkce
mysql_unbuffered_query()
prostě a jednoduše odpovídá C API mysql_use_result().Conected and selected DB Exec ps: 6680576 Start mysql_query() Selected Exec ps: 41750528 Selected after sleep(2) Exec ps: 41750528 Fetched all rows, php sum(id): 16200090000 Exec ps: 41750528 After mysql_free_result Exec ps: 6680576 After mysql_close Exec ps: 6680576U
mysql_buffered_query()
to nabýšení na 40MiB nebylo.No generovat tabulku v databázi bych fakt nedooporučoval. Logika v databázi, to ano, ale view? To se bude tak obtížně spravovat, že fuj.Zrovna jsem to otestoval v SQLite na 300 000 záznamech. Vygenerování celé tabulky jedním selectem do jednoho řetězce bylo asi 3x rychlejší, než klasické procházení výsledků selectu cyklem. Možná se to obtížněji spravuje, ale databáze udělá view rychleji než PHP.
foreach($dbh->query("SELECT value as val FROM pokus;") as $row) { echo 'trtd'.$row['val'].'/td/tr'."\n"; }Skládání řádek v SQL (12 sekund):
foreach($dbh->query("SELECT 'trtd'||value||'/td/tr\n' as val FROM pokus;") as $row) { echo $row['val']; }Použití pole (9 sekund):
$out=array(); foreach($dbh->query("SELECT value as val FROM pokus;") as $row) { $out[]='trtd'.$row['val'].'/td/tr'; } echo implode("\n",$out);Použití řetězce (9 sekund):
$out=''; foreach($dbh->query("SELECT value as val FROM pokus;") as $row) { $out.='trtd'.$row['val'].'/td/tr'."\n"; } echo $out;Skládání řádek, kombinace s polem (9 sekund):
$out=array(); foreach($dbh->query("SELECT 'trtd'||value||'/td/tr' as val FROM pokus;") as $row) { $out[]=$row['val']; } echo implode("\n",$out);Agregace v SQL (5 sekund):
foreach($dbh->query("SELECT group_concat('trtd'||value||'/td/tr','\n') as val FROM pokus;") as $row) { echo $row['val']."\n"; }Agregace v SQL bez výstupu echo (2 sekundy):
foreach($dbh->query("SELECT group_concat('trtd'||value||'/td/tr','\n') as val FROM pokus;") as $row) { $line=$row['val']."\n"; }To poslední měření jsem uvedl proto, aby bylo vidět, jak je echo líné. Znaky
tr
a td
znamenají <tr>
a <td>
, ale nechtělo se mi to nahrazovat entitami v celém příspěvku. Kdo chce, domyslí si.
Skládání s polem máš naprosto zbytečně v cyklu, do implode můžeš hodit už ten query.Asi mi něco uniká. Myslíš něco takového?
$result=$dbh->query("SELECT 'trtd'||value||'/td/tr' as val FROM pokus;",PDO::FETCH_COLUMN)); echo implode("\n",$result->fetchAll());Zkusím to odladit, až budu na původním počítači. V této podobě by to mohlo být i rychlé. Ohledně údržby kódu: Někteří vývojáři tvrdí, že SQL umí všechno. Dá i nedá se s tím souhlasit, někdy je to za cenu velkých obstrukcí. Souhlasím, že je dobré dělat aplikace jako vícevrstvé, kód se tím často zpřehlední. V uvedeném případě mi však připadá přidání jedné funkce do SQL a ubrání z PHP jako jednoduchá úprava. Vychází mi však efektivnější. Můžu ještě zkusit porovnat i paměťové nároky jednotlivých řešení.
implode($dbh->query("SELECT 'trtd'||value||'/td/tr' as val FROM pokus;",PDO::FETCH_COLUMN, "\n")
ale to se omlouvám, zas jsem zapoměl na "debilitu" phpka, kde sice něco se tváří jako pole/iterátor, ale to ještě neznamená, že se to dá jako pole nebo iterátor používat, takže todle máš pravdu, to nejde.
Zkusit to s FetchAll je zajímavý, jestli je to fetchAll tedy napsané v něčem rozumějším než PHP, jinak to bude stejné.
V tý udržovatelnosti IMHO nejde až tak o to, že se to udělá jednou, ale o to, že např. za tejden přijde šéf, že chce, aby si mohl člověk tu tabulky "skinovat". V tu chvíli začne db řešení dělat problémy, protože tam se takový věci implantujou daleko hůř.
$result=$dbh->query("SELECT 'trtd'||value||'/td/tr' as val FROM pokus;",PDO::FETCH_COLUMN,0); echo implode("\n",$result->fetchAll());Je to asi nejrychlejší řešení z těch, které dělají výstupní agregaci mimo SQL. Zároveň nevypadá příliš složitě, mělo by být obecně použitelné. Předpokládám, že metoda fetchAll() je napsána v C nebo něčem podobném. Raději použiji takovou funkci než abych iteroval ve skriptu. Beru to od tebe jako dobrý nápad na rozumný kompromis. A že to má 2 řádky místo zamýšlené jedné? To mě vůbec netrápí. Zpracování skriptu v polovičním čase pro mne znamená zpracování dvojnásobného počtu požadavků klientů nebo pronajaté jedno procesorové jádro místo dvou. V případě některých aplikací to může být docela důležité. Jak už bylo řečeno, cokoliv je rychlejší než PHP. Tím cokoliv můžou být nejen databáze, ale i vestavěné funkce PHP, které obvykle v PHP napsány nejsou.
Tiskni
Sdílej: