DB wrapper s využití souborové cache

PHP Ostatní DB wrapper s využití souborové cache

Při načítání opakujících souboru jsem testoval kombinaci načítání z databáze (v testu MySQL) a uložení do pomocného souboru. Jelikož se některé načtené data načítají pořád dokola po každém načtení stránky, je toto možné řešení. I proměnná $_SESSION má jisté limity.

Výhody tohoto řešení jsou především v cachování již výsledků. Tím spolu s, dnes již běžnými, SSD disky dávají možnosti použití větší rychlosti načítání při menším zatížení DB.

Samotná DB také používá vlastní cache, ale před tím testuje, data vyhledá, třídí a kontroluje zda se data nezměnili. To ale zabere jistý čas, který, jak prokazují výsledky testu, napomáhají souborové cache, která data pouze přečte. I proto byl test proveden 1000x za sebou.

K testování jsem použil zdejší PDO wrapper, který jsem si upravil na možnost načítání jak z DB tak ze souboru dle zadaných parametrů. Samozřejmě jde pouze o jednu funkci, ale je možné způsob praktikovat i na ostatní.

Soubory se ukládají do samostatné složky. Do této složky je samozřejmě nutno nastavit oprávnění zápisu. Do složky se budou ukládat soubory, kde název souboru bude vlastní SQL dotaz, který pro jedinečnost bude přetažen přes MD5() a přípona „.cache“:

$cacheSoubor = self::$cacheDir . md5($dotaz) . '.cache';

Aby se předešlo případným kolizím v MD5() - pravděpodobnost je opravdu, ale opravdu malá, je spolu s daty z DB, uložen i dotaz. Ten je při načítání ze souboru testován s původním dotazem a pokud se shodují, je považován za správný a odeslán.

Velké množství programů vytvořených v PHP, již cache využívá. Například fórum PHPBB, redakční systém PHP-Fusion, ale i Nette Framework a spousta dalších. Převážně ale uchovávají celý výsledný obsah HTML.

SQL dotaz je stejný, pokud půjde o dotaz DB:

Db:: dotazVse (' SELECT * FROM `clanky`');

Pokud budete chtít využít souborové cache, dotaz se lehce upraví:

Db:: dotazVse (' SELECT * FROM `clanky`', array(), TRUE);

Vlastní zdrojový kód

Zde jednotlivé základní funkce:

private static $cacheDir = './dbcache/';    // složka s dočasnými soubory


public static function dotazVse($dotaz, $parametry = Array(), $zCache = FALSE, $casChache = 30) {

        if ($zCache) {          //chceme nacitat z cache

            $cacheSoubor = self::$cacheDir . md5($dotaz) . '.cache';

            if (is_file($cacheSoubor) && (time() - filemtime($cacheSoubor)) < $casChache) { //test pritomnosti souboru a test doby ulozeni

                $vystupFile = self::getCache($cacheSoubor);

                if ($vystupFile[0] == $dotaz) { //kontrola shody samotneho dotazu - mozna kolize MD5

                    $vystup = $vystupFile[1];

                } else { // je kolize s MD5 - nacti z DB

                    $statement = self::$db->prepare($dotaz);
                    $statement->execute($parametry);
                    $vystup = $statement->fetchAll(PDO::FETCH_ASSOC);
                }

            } else { // soubor neni nebo je prilis stary - nacti z DB

                $navrat = self::$db->prepare($dotaz);
                $navrat->execute($parametry);
                $vystup = $navrat->fetchAll(PDO::FETCH_ASSOC);

                $vystupFile[] = $dotaz;
                $vystupFile[] = $vystup;
                self::setCache($cacheSoubor, $vystupFile);
            }

        } else { //nacti pouze z DB

            $statement = self::$db->prepare($dotaz);
            $statement->execute($parametry);
            $vystup = $statement->fetchAll(PDO::FETCH_ASSOC);

        }

        return $vystup;
}


// ulož do cache
private static function  setCache($soubor, $data) {
        $fh = fopen($soubor, 'w');
        fwrite($fh, json_encode($data, JSON_UNESCAPED_UNICODE));
        fclose($fh);
}

// čti z cache
private static function  getCache($soubor) {
        $fh = fopen($soubor, 'r');
        $nactenaData = fread($fh, filesize($soubor));
        $nactenaData = (array) json_decode($nactenaData);
        return $nactenaData;
}

Funkce dotazVse() má tyto parametry:

  • $dotaz - dotaz SQL (stejné jako původní)
  • $parametry - parametry dotazu (stejné jako původní)
  • $zCache - zda se má soubor pokusit načíst z cache (TRUE nebo FALSE), defaultně FALSE
  • $casChache - čas, kdy je soubor považován za aktuální (v sekundách), defaultně 30 sekund

Vlastní testovací program:

for ($c = 1; $c < 1001; $c++) {

    $start = microtime(1);
    $user = SDb::dotazVse('SELECT * FROM `test` LIMIT 1', array(), TRUE);
    $end = microtime(1);

    $time += number_format($end - $start, 8);

}

Funkce setCache() uloží soubor a funkce getCache() načte požadovaný soubor. Zde stojí snad jen za zmínku, že jsou soubory uloženy pomocí json_encode (respektive json_decode), aby byl jejich výstup vrácen jako pole. Pro podporu českého jazyka je využit parametr JSON_UNESCAPED_U­NICODE, který podporuje UTF-8. Na snadě bylo ještě řešení příkazem serialize(), ale ten - mimo jiné - neumožňuje české kódovaní - UTF-8 (připomínka Davida Čapka).

Při načítání normálního souboru (dotazVse() ) je navíc pouze jedna podmínka a to, jestli se mají data načíst z cache ( if ($zCache)). To je jediné zpoždění oproti normálnímu dotazu, kterých bude samozřejmě víc. Pokud je podmínka splněna, vytvoří se název požadovaného souboru, který je otestován v adresáři cache (zde /dbcache) a dále se testuje doba za kterou se soubor považuje za aktuální - defaultně 30 sekund. Pokud podmínka platí, soubor je načten z adresáře cache. Pokud ne, je načten z DB a následně se soubor uloží příp. nahradí novým.

Provedl jsem otestování rychlosti. V první řadě upozorňuji, že požadovaný program byl testován na hostingu (konkrétně na WEDOS) a ne na lokálním PC. Dále na WEDOS VPS serveru, ale výsledky byly stejné.

Vlastní test

Dotaz byl vykonán příkazem SQL různého typu (bigint, datetime, smallint i Varchar). Jeden samotný dotaz měl 14 SQL polí a průměrnou velikost 280 bytů. Samotný test se skládal z 3 typu podtestu a to:

  1. Neupravený původní PDO ovladač
  2. Upravený PDO ovladač, ale výsledek pouze z DB
  3. Upravený PDO ovladač, výsledek pouze z cache

Dále jsem u každého testu provedl postupně SQL dotaz s LIMIT 50, 30, 15, 10, 5, 3 a 1 a to vše opakoval 1000 krát s těmito výsledky:

Časy v sekundách/Počet dotazů 50 30 15 10 5 3 1
Původní PDO 0,465 0,361 0,283 0,262 0,231 0,224 0,201
Upravené PDO-z DB 0,477 0,367 0,283 0,280 0,249 0,228 0,210
Upravené PDO-z cache 0,950 0,590 0,331 0,213 0,121 0,085 0,068
Velikost cache (bytů) 12902 7824 3947 2624 1352 839 325
Poli v cache 700 420 210 140 70 42 14

Výsledné hodnoty převedené do grafu:

Výsledný graf naměřených hodnot

Závěr

Z výsledku vyplývá poměrně vysoká rychlost načítání dat ze souboru a to do maximálně 140 dotazů a maximální velikosti 2 500 bytů v souboru. Při větším souboru rychlost klesá a je výhodnější načítat data přímo z DB.

Z výsledných dat je zřejmé i velmi malé zatížení SQL dotazu, pokud se jedná původní čtení z DB, kterých je více a který téměř nezasáhne do celkového času. Toto by se mohlo hodit zejména pro weby, kde se loguje uživatel. Jeho ID se uloží do SESSION (případně cookie), ale detaily tohoto uživatele (poslední přihlášení, adresy, individuální nastavení uživatele, počet článků atd.), se dají takto velmi rychle a snadno ošetřit. Navíc se databáze nezatěžuje neustále stejnými dotazy, což se v případě většího počtu lidí může hodit.

Poznámka

Co se týče velikosti složky s cache – při použití příkazu register_shut­down_function('xxx') provádím zakončovací práce (časová kontrola změny emailu i změny hesla a podobně) a také příkazy na kontrolu a vyhodnocování chyb (o tom bych mohl napsat, pokud bude zájem, příště). Také je možné nastavit, že se jednou za den, hodinu, týden nebo měsíc adresář vymaže.


 

  Aktivity (1)

Článek pro vás napsal Pavel
Avatar

Jak se ti líbí článek?
Celkem (1 hlasů) :
4444 4


 


Miniatura
Předchozí článek
Matematické funkce v PHP
Miniatura
Všechny články v sekci
Ostatní tutoriály v PHP

 

 

Komentáře
Zobrazit starší komentáře (15)

Avatar
Richard
Člen
Avatar
Richard:

Může tu prosím napsat svůj názor někdo, kdo napsal svůj databázovej server, nebo má zkušenosti se škálováním?

Pavle, když pustíš 1000x za sebou jeden select, to není test.
To že je to určeno pro hostingy nebo vps není argument, vzhledem k tomu že si to "testoval" na jednom hostingu a jednom vps u stejného poskytovatele. Kdyby si to testoval u různých poskytovatelů, dostaneš různé výsledky a různé závěry.

Nevím co myslíš tím "tvoje specificke stroje, co maji 64gb pameti", hostingy, konkrétně wedos určitě má konfiguraci velice podobnou jako naše servery, nehledě na to že wedos má stroje s 384GB paměti (GB nebo gb, to je maličko rozdíl).

Souborová cache se používá, má svoje místo kde je vhodné ji použít, i my ji používáme, ale prostě ji nemůžeš nasadit jen tak bezmyšlenkovitě kde se ti zachce aniž bys měl relevantní data jestli je to správný krok nebo ne.

Editováno 29.7.2015 9:43
Odpovědět 29.7.2015 9:42
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Avatar
Richard
Člen
Avatar
Richard:

A dovolím si úryvek pm co mi přišla

Člověče, myslíš, že by mělo smysl jim tam něco psát? Vždyť benchmarky a scaling jsou věda a pro většinu smrtelníků daleko za hranicí pochopení. Děcka nemůžou pochopit, že 1x1000=1000 volání jsou hrušky a 1000x1=1000 volání jsou jablka.

Aspoň někdo :-)

Odpovědět 29.7.2015 13:58
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Avatar
Pavel
Redaktor
Avatar
Odpovídá na Richard
Pavel:

No jo no. Proste jste nejlepsi a vas nazor je jedinej spravnej.. vy dospeli

Asi bychom meli nechat, toto nikam nevede.. ty mas svoji pravdu a ja taky.. ostatni at se zaridi podle s eho rozumu...

Pavel

 
Odpovědět  +1 29.7.2015 14:04
Avatar
Richard
Člen
Avatar
Odpovídá na Pavel
Richard:

Tak pravda je jenom jedna, jsem přesvědčený že jí mám já, protože moje zkušenosti to potvrzují a názor kolegů databázistů ho utvrzuje.

Ale rád se nechám přesvědčit o tom že jí nemám, alespoň bych z toho taky něco měl, ale když nejsi schopný dodat data a jen argumentuješ jedním nerelevantním testem který nic neříká, nemůžeš se divit že si stojím za svým.

Odpovědět 29.7.2015 14:09
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Avatar
shaman
Člen
Avatar
Odpovídá na Pavel
shaman:

Ja pouzivam na shared hostingoch cacheovanie do suboru alebo databazy. Urcite to zrychli chod serveru. Nemam nejaku preferenciu ktore je lepsie, hlavne ze to pomaha. Ak by bol naozaj problem s rychlostou tak by som klienta presvedcil na prechod na VPS.
Okrem lepsieho hardwareu a CPU, tam uz nie som odkazany na neschopnych adminov hostingu a mozem si doinstalovat APC, memcached alebo redis.

Clanok mi zase raz potvrdil ze cacheovanie ma zmysel.

Odpovědět 29.7.2015 15:08
try {...} catch (Exception ignored) { echo " ¯\_(ツ)_/¯ "; }
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Richard
David Čápka:

Prostě sem buď jednou napiš svůj názor a dále se nehádej nebo proveď takové testy, které tvá tvrzení potvrdí. Jaký má smysl psát sem pořád dokola co říkají kolegové nebo co si ty myslíš? My cachujeme do souborů denně, já dokonce cachuji i do databáze. Buď poskytni něco co vyvrátí že to děláme, což se ti podaří asi těžko, nebo jednou napiš svůj názor a dále to nech být. Kdyby tu tímhle stylem diskutovali všichni tak to můžeme zavřít...

Odpovědět 29.7.2015 18:14
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Richard
Člen
Avatar
Odpovídá na David Čápka
Richard:

Základní otázka, abych věděl jestli vůbec má smysl nějak pokračovat:
Opravdu považuješ test v článku za relevantní, spolehlivý a dostačující?

Odpovědět 29.7.2015 18:46
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Richard
David Čápka:

Můžeš si povšimnout, že jsem sám psal k článku připomínku, že mi přijde, že v něm chybí důležité informace. Test mi dostatečný přijde, spolehlivý ne, to ale neznamená, že tu o tom budu několik dní diskutovat. Jaký to má smysl? Můj komentář tu je, článek je určitě přínosný tak jak je, ta cache takhle jak to je napsané fungovat bude. Prostě to nech být.

Odpovědět  ±0 29.7.2015 18:49
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
mkub
Redaktor
Avatar
mkub:

k tej suborovej cache este treba priratat aj reziu FS
som zvedavy na testy pri pouziti tychto FS:

  • NTFS
  • EXT3
  • EXT4
  • ZFS
  • XFS
  • BTRFS

kazdy z tychto filesystemov ma svoje specifika, su rozne vykonne a velmi mozu ovplyvnit koncove vyhodnotenie

a co sa tyka SSD, tak SSD by som nepouzil ako cache pre databazu, lebo sa castym zapisom a prepisom opotrebuvaju pamatove bunky
a co sa tyka HDD oproti dbcache, tak u HDD sa vykon rapidne straca v rezii daneho FS
vidim toto riesenie jedine pri cacheovani vysledkov pri pristupe na DB server cez siet, kedy je siet uzkym hrdlom a este ked sa k tomu zapoji aj tmpfs
ale ak je web a databaza na jednom stroji, neverim, ze by sa zlepsil vykon tymto sposobom
a k tej dbcache - kazda databaza ma ju inak implementovanu a tym aj odlisny vykon pri nacitani vysledkov z nej

dalsia otazka je, ze ci osetrujes aj ten pripad, ked sa idu zapisat udaje pomocou dotazu do databaze, ze ci tie udaje zapisujes ina na disk, alebo skutocne ich zapises do db, tu totiz sa moze stat, ked sa do db nezapisu, ze sa stane obsah zastaraly a nekonzistentny
a k tomu porovnavaciemu testu, bolo by fajn ho urobit aj u inych webhostingoch

tolko moj nazor k vyuzivaniu suborovej cache pri praci s databazou a myslim, ze aj DB ju pouziva, lebo nepristupuje prriamo k svojim suborom, ale cez suborovu/diskovu cache

 
Odpovědět  +1 23. února 5:33
Avatar
Richard
Člen
Avatar
Odpovídá na mkub
Richard:

+1, při přístupu přes síť a za určitých podmínek by něco podobnýho mohlo mít pozitivní efekt. Ale vzhledem k tomu, že tu bylo psáno že je to určené na webhosting tak tahle možnost až na vyjímky odpadá.

Opotřebení ssd na hostingu je každýmu jedno protože neopotřebovává svůj hw a škody co tím způsobí bude platit někdo jinej.

Po dlouhý době jsem si přečetl článek i diskusi a jsem opět v šoku z toho že to každých 30 sekund přepisuje soubory, že se mi tu někdo pokoušel namluvit že disk je rychlejší než paměť a že mě tady David seřval za to že nemam argumenty abych dokázal to co tvrdím i když to je naprosto přirozená věc a raději se přiklonil k verzi Pavla která popírá logiku a zdravej rozum a je podložená tím sranda testem kterej krásně obešel a ve výsledcích nereflektoval fungování a nevýhody toho řešení a nakonec že se tu dávalo do jedné roviny cachování šablon (etc) a výsledků databáze..

Ale možná je to můj fail - teď jsem si uvědomil že se nikde nepíše o sql cache a výsledky by tomu napovídaly, otázka - v testu byla vypnuta sql cache?

Odpovědět 23. února 8:30
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 25. Zobrazit vše