IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

DB wrapper s využitia súborové cache

Pri načítaní opakujúcich súboru som testoval kombináciu načítanie z databázy (v teste MySQL) a uloženie do pomocného súboru. Keďže sa niektoré načítané dáta načítajú stále dookola po každom načítaní stránky, je toto možné riešenie. Aj premenná $ _SESSION má isté limity.

Výhody tohto riešenia sú predovšetkým v cachovanie už výsledkov. Tým spolu s, dnes už bežnými, SSD disky dávajú možnosti použitia väčšej rýchlosti načítavania pri menšom zaťažení DB.

Samotná DB tiež používa vlastnú cache, ale pred tým testuje, dáta vyhľadá, triedi a kontroluje či sa dáta nezmenili. To ale zaberie istý čas, ktorý, ako dokazujú výsledky testu, napomáhajú súborové cache, ktoré dáta iba prečíta. Aj preto bol test vykonaný 1000x za sebou.

K testovaniu som použil tunajšie PDO wrapper, ktorý som si upravil na možnosť načítanie tak z DB tak zo súboru podľa zadaných parametrov. Samozrejme ide iba o jednu funkciu, ale je možné spôsob praktizovať aj na ostatných.

Súbory sa ukladajú do samostatnej zložky. Do tejto zložky je samozrejme nutné nastaviť povolenia zápisu. Do zložky sa budú ukladať súbory, kde názov súboru bude vlastný SQL dotaz, ktorý pre jedinečnosť bude natiahnutý cez MD5 () a prípona ".cache":

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

Aby sa predišlo prípadným kolíziám v MD5 () - pravdepodobnosť je naozaj, ale naozaj malá, je spolu s dátami z DB, uložený aj dotaz. Ten je pri načítaní zo súboru testovaný s pôvodným dotazom a ak sa zhodujú, je považovaný za správny a odoslaný.

Veľké množstvo programov vytvorených v PHP, už cache využíva. Napríklad fórum PHPBB, redakčný systém PHP-Fusion, ale aj Nette Framework a množstvo ďalších. Prevažne ale uchovávajú celý výsledný obsah HTML.

SQL dotaz je rovnaký, ak pôjde o otázku DB:

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

Ak budete chcieť využiť súborové cache, otázka sa ľahko upraví:

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

Vlastný zdrojový kód

Tu jednotlivé základné funkcie:

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;
}

Funkcia dotazVse () má tieto parametre:

  • $ Pripomienky - dotaz SQL (rovnaké ako pôvodné)
  • $ Parametre - parametre dotazu (rovnaké ako pôvodné)
  • $ ZCache - či sa má súbor pokúsiť načítať z cache (TRUE alebo FALSE), defaultne FALSE
  • $ CasChache - čas, kedy je súbor považovaný za aktuálnu (v sekundách), defaultne 30 sekúnd

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);

}

Funkcia setCache () uloží súbor a funkcie getCache () načíta požadovaný súbor. Tu stojí snáď len za zmienku, že sú súbory uložené pomocou json_encode (respektíve json_decode), aby bol ich výstup vrátený ako pole. Pre podporu slovenského jazyka je využitý parameter JSON_UNESCAPED_U­NICODE, ktorý podporuje UTF-8. Na snáď bolo ešte riešenie príkazom serialize (), ale ten - okrem iného - neumožňuje českej kódovania - UTF-8 (pripomienka Davida Čapka).

Pri načítaní normálneho súboru (dotazVse ()) je navyše len jedna podmienka a to, či sa majú dáta načítať z cache (if ($ zCache)). To je jediné meškanie oproti normálnemu dotazu, ktorých bude samozrejme viac. Ak je podmienka splnená, vytvorí sa názov požadovaného súboru, ktorý je otestovaný v adresári cache (tu / dbcache) a ďalej sa testuje doba za ktorú sa súbor považuje za aktuálne - defaultne 30 sekúnd. Ak podmienka platí, súbor je načítaný z adresára cache. Ak nie, je načítaný z DB a následne sa súbor uloží príp. nahradí novým.

Vykonal som otestovanie rýchlosti. V prvom rade upozorňujem, že požadovaný program bol testovaný na hostingu (konkrétne na WEDOS) a nie na lokálnom PC. Ďalej na WEDOS VPS serveri, ale výsledky boli rovnaké.

Vlastný test

Otázka bol vykonaný príkazom SQL rôzneho typu (bigint, datetime, smallint i Varchar). Jeden samotný dotaz mal 14 SQL polí a priemernú veľkosť 280 bytov. Samotný test sa skladal z 3 typu podtesty a to:

  1. Neupravený pôvodnej PDO ovládač
  2. Upravený PDO ovládač, ale výsledok iba z DB
  3. Upravený PDO ovládač, výsledok iba z cache

Ďalej som u každého testu vykonal postupne SQL dotaz s LIMIT 50, 30, 15, 10, 5, 3 a 1 a to všetko opakoval 1000 krát s týmito výsledkami:

Časy v sekundách / Počet otázok 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
Veľkosť cache (bytov) 12902 7824 3947 2624 1352 839 325
Poli v cache 700 420 210 140 70 42 14
Výsledné hodnoty prenesené do grafu:
Výsledný graf nameraných hodnôt - Ostatné tutoriály v PHP

Záver

Z výsledku vyplýva pomerne vysoká rýchlosť načítanie dát zo súboru a to do maximálne 140 otázok a maximálnej veľkosti 2 500 bytov v súbore. Pri väčšom súboru rýchlosť klesá a je výhodnejšie načítavať dáta priamo z DB.

Z výsledných dát je zrejmé aj veľmi malé zaťaženie SQL dotazu, ak ide pôvodné čítanie z DB, ktorých je viac a ktorý takmer nezasiahne do celkového času. Toto by sa mohlo hodiť najmä pre weby, kde sa loguje užívateľ. Jeho ID sa uloží do SESSION (prípadne cookie), ale detaily tohto používateľa (posledný prihlásenie, adresy, individuálne nastavenie používateľa, počet článkov atď.), Sa dajú takto veľmi rýchlo a ľahko ošetriť. Naviac sa databázy nezaťažuje neustále rovnakými otázky, čo sa v prípade väčšieho počtu ľudí môže hodiť.

Poznámka

Čo sa týka veľkosti zložky s cache - pri použití príkazu register_shut­down_function ( 'xxx') vykonávam zakončovací práce (časová kontrola zmeny emailu aj zmeny hesla a podobne) a tiež príkazy na kontrolu a vyhodnocovanie chýb (o tom by som mohol napísať, ak bude záujem, nabudúce). Tiež je možné nastaviť, že sa raz za deň, hodinu, týždeň alebo mesiac adresár vymaže.


 

Všetky články v sekcii
Ostatné tutoriály v PHP
Článok pre vás napísal Pavel
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
...
Aktivity