Caching
A gyorsítótár felgyorsítja az alkalmazást azáltal, hogy az egyszer már nehezen lekérdezett adatokat tárolja a későbbi felhasználásra. Megmutatjuk neked:
- Hogyan használjuk a gyorsítótárat?
- Hogyan változtathatja meg a gyorsítótár tárolását
- Hogyan kell megfelelően érvényteleníteni a gyorsítótárat
A gyorsítótár használata nagyon egyszerű a Nette-ben, miközben nagyon fejlett gyorsítótárazási igényeket is lefed. Teljesítményre és 100%-os tartósságra tervezték. Alapvetően a leggyakoribb háttértárolókhoz talál adaptereket. Lehetővé teszi a címkén alapuló érvénytelenítést, a gyorsítótár stampedivédelmet, az időbeli lejáratot stb.
Telepítés
Töltse le és telepítse a csomagot a Composer segítségével:
composer require nette/caching
Alapvető használat
A gyorsítótárral végzett munka középpontjában a Nette\Caching\Cache objektum áll. Létrehozzuk a
példányát, és paraméterként átadjuk a konstruktornak az úgynevezett tárolót. Ami egy olyan objektum, amely azt a helyet
képviseli, ahol az adatokat fizikailag tárolni fogjuk (adatbázis, Memcached, fájlok a lemezen, …). A tároló objektumot a
Nette\Caching\Storage
típusú függőségi injektálással kapjuk meg a függőségi injektálással történő átadással.
Minden lényeges dolgot megtudhat a Storage (tárolás) című részben.
A 3.0-ás verzióban az interfész még a I
prefix, so the name was
Nette\Caching\IStorage
. Emellett a Cache
osztály konstansait nagybetűvel írták, így például
Cache::EXPIRE
helyett Cache::Expire
.
A következő példákban tegyük fel, hogy van egy Cache
aliasunk és egy tárolónk a $storage
változóban.
use Nette\Caching\Cache;
$storage = /* ... */; // a Nette\Caching\Storage példánya
A gyorsítótár tulajdonképpen egy kulcs-érték tároló, tehát az adatokat kulcsok alatt olvassuk és írjuk, mint az asszociatív tömböknél. Az alkalmazások több független részből állnak, és ha ezek mind egy tárolót használnának (ötletként: egy könyvtárat a lemezen), előbb-utóbb kulcsütközésre kerülne sor. A Nette keretrendszer úgy oldja meg a problémát, hogy a teljes tárhelyet névterekre (alkönyvtárakra) osztja. Így a program minden egyes része a saját, egyedi névvel ellátott tárhelyét használja, és nem fordulhat elő ütközés.
A névtér nevét a Cache osztály konstruktorának második paramétereként adjuk meg:
$cache = new Cache($storage, 'Full Html Pages');
Most már használhatjuk a $cache
objektumot a gyorsítótárból való olvasáshoz és íráshoz. Mindkettőhöz
a load()
metódust használjuk. Az első argumentum a kulcs, a második pedig a PHP callback, amelyet akkor hívunk
meg, ha a kulcsot nem találjuk a gyorsítótárban. A callback generál egy értéket, visszaadja azt, és a gyorsítótárba
helyezi:
$value = $cache->load($key, function () use ($key) {
$computedValue = /* ... */; // nehéz számítások
return $computedValue;
});
Ha a második paraméter nincs megadva: $value = $cache->load($key)
, a null
visszaadja, ha az elem
nincs a gyorsítótárban.
A nagyszerű dolog az, hogy bármilyen szerializálható struktúrát lehet gyorsítótárba helyezni, nem csak stringeket. És ugyanez vonatkozik a kulcsokra is.
Az elemet a remove()
metódus segítségével töröljük a gyorsítótárból:
$cache->remove($key);
Egy elemet a $cache->save($key, $value, array $dependencies = [])
módszerrel is gyorsítótárba helyezhet.
A fenti, load()
módszer azonban előnyösebb.
Memoization
A memoizálás egy függvény vagy metódus eredményének gyorsítótárazását jelenti, hogy legközelebb is használhassa azt, ahelyett, hogy újra és újra kiszámolná ugyanazt a dolgot.
A módszereket és függvényeket a call(callable $callback, ...$args)
segítségével lehet memoizálni:
$result = $cache->call('gethostbyaddr', $ip);
A gethostbyaddr()
függvényt csak egyszer hívjuk meg a $ip
minden egyes paraméterére, és a
következő alkalommal a gyorsítótárból származó értéket kapjuk vissza.
Lehetőség van arra is, hogy egy memoizált burkolatot hozzunk létre egy metódushoz vagy függvényhez, amelyet később hívhatunk meg:
function factorial($num)
{
return /* ... */;
}
$memoizedFactorial = $cache->wrap('factorial');
$result = $memoizedFactorial(5); // megszámolja.
$result = $memoizedFactorial(5); // visszaadja a cache-ből
Lejárat és érvénytelenítés
A gyorsítótárazással foglalkozni kell azzal a kérdéssel, hogy a korábban elmentett adatok egy része idővel érvénytelenné válik. A Nette keretrendszer biztosít egy mechanizmust, amellyel az adatok érvényességét korlátozni lehet, és az adatok ellenőrzött módon törölhetők (“érvényteleníthetők”, a keretrendszer terminológiáját használva).
Az adatok érvényességét a mentéskor a save()
módszer harmadik paraméterével lehet beállítani, pl:
$cache->save($key, $value, [
$cache::Expire => '20 minutes',
]);
Vagy a $dependencies
paraméter használatával, amelyet hivatkozással adunk át a load()
metódus
visszahívásának, pl:
$value = $cache->load($key, function (&$dependencies) {
$dependencies[Cache::Expire] = '20 minutes';
return /* ... */;
});
Vagy a load()
módszer 3. paraméterének használatával, pl:
$value = $cache->load($key, function () {
return ...;
}, [Cache::Expire => '20 minutes']);
A következő példákban a második változatot és így a $dependencies
változó létezését
feltételezzük.
Lejárat
A legegyszerűbb lejárat az időkorlát. Íme, hogyan lehet 20 percig érvényes adatokat gyorsítótárba helyezni:
// elfogadja a másodpercek számát vagy a UNIX időbélyeget is.
$dependencies[Cache::Expire] = '20 minutes';
Ha minden egyes olvasással meg akarjuk hosszabbítani az érvényességi időt, akkor ez így is megvalósítható, de vigyázat, ez növeli a cache overheadet:
$dependencies[Cache::Sliding] = true;
A praktikus lehetőség, hogy egy adott fájl vagy több fájl közül az egyik módosításakor az adatok érvényessége lejárjon. Ez például az ilyen fájlok feldolgozásából származó adatok gyorsítótárazására használható. Abszolút elérési utak használata.
$dependencies[Cache::Files] = '/path/to/data.yaml';
// vagy
$dependencies[Cache::Files] = ['/path/to/data1.yaml', '/path/to/data2.yaml'];
A gyorsítótár egy elemét akkor hagyhatjuk lejárni, amikor egy másik elem (vagy több másik közül valamelyik) lejár.
Ezt akkor használhatjuk, ha a teljes HTML-oldalt és annak más kulcsok alatt lévő töredékeit is gyorsítótárba helyezzük.
Amint a részlet megváltozik, az egész oldal érvénytelenné válik. Ha olyan kulcsok alatt tárolt töredékeink vannak, mint
a frag1
és a frag2
, akkor használjuk:
$dependencies[Cache::Items] = ['frag1', 'frag2'];
A lejáratot egyéni függvényekkel vagy statikus metódusokkal is szabályozhatjuk, amelyek mindig az olvasáskor döntenek
arról, hogy az elem még érvényes-e még. Például hagyhatjuk, hogy az elem lejárjon, amikor a PHP verziója megváltozik.
Létrehozunk egy függvényt, amely összehasonlítja az aktuális verziót a paraméterrel, és mentéskor hozzáadunk egy
tömböt a következő formában [function name, ...arguments]
a függőségekhez:
function checkPhpVersion($ver): bool
{
return $ver === PHP_VERSION_ID;
}
$dependencies[Cache::Callbacks] = [
['checkPhpVersion', PHP_VERSION_ID] // lejár, ha checkPhpVersion(...) === false
];
Természetesen minden kritérium kombinálható. A gyorsítótár akkor jár le, ha legalább egy kritérium nem teljesül.
$dependencies[Cache::Expire] = '20 minutes';
$dependencies[Cache::Files] = '/path/to/data.yaml';
Érvénytelenítés címkékkel
A címkék nagyon hasznos érvénytelenítési eszköz. A cache-ben tárolt minden egyes elemhez hozzárendelhetünk egy listát címkékből, amelyek tetszőleges karakterláncok. Tegyük fel például, hogy van egy HTML-oldalunk egy cikkel és kommentekkel, amelyet gyorsítótárba szeretnénk helyezni. Tehát címkéket adunk meg a gyorsítótárba mentéskor:
$dependencies[Cache::Tags] = ["article/$articleId", "comments/$articleId"];
Most pedig térjünk át az adminisztrációra. Itt van egy űrlapunk a cikkek szerkesztéséhez. A cikk adatbázisba
mentésével együtt hívjuk meg a clean()
parancsot, amely címkék szerint törli a gyorsítótárazott
elemeket:
$cache->clean([
$cache::Tags => ["article/$articleId"],
]);
Hasonlóképpen, egy új megjegyzés hozzáadása (vagy egy megjegyzés szerkesztése) helyén nem felejtjük el érvényteleníteni a vonatkozó taget:
$cache->clean([
$cache::Tags => ["comments/$articleId"],
]);
Mit értünk el? Azt, hogy a HTML-cache-ünk érvénytelenítve (törölve) lesz, amikor a cikk vagy a hozzászólások
változnak. Egy ID = 10 azonosítóval rendelkező cikk szerkesztésekor a article/10
címke érvénytelenítése
kikényszerül, és a címkét tartalmazó HTML-oldal törlődik a gyorsítótárból. Ugyanez történik akkor is, ha új
megjegyzést illesztünk be az adott cikk alá.
A címkékhez Journal szükséges.
Érvénytelenítés prioritás szerint
A gyorsítótárban lévő egyes elemek prioritását beállíthatjuk, és lehetőségünk lesz arra, hogy ellenőrzött módon töröljük őket, amikor például a gyorsítótár meghalad egy bizonyos méretet:
$dependencies[Cache::Priority] = 50;
Töröljük az összes olyan elemet, amelynek prioritása 100 vagy annál kisebb:
$cache->clean([
$cache::Priority => 100,
]);
A prioritásokhoz úgynevezett Naplóra van szükség.
Cache törlése
A Cache::All
paraméter mindent töröl:
$cache->clean([
$cache::All => true,
]);
Bulk Reading
A gyorsítótárba történő tömeges olvasáshoz és íráshoz a bulkLoad()
módszert használjuk, ahol
átadunk egy kulcsokból álló tömböt, és megkapunk egy értékekből álló tömböt:
$values = $cache->bulkLoad($keys);
A bulkLoad()
módszer a load()
módszerhez hasonlóan működik a második visszahívási
paraméterrel, amelynek a generált elem kulcsát adjuk át:
$values = $cache->bulkLoad($keys, function ($key, &$dependencies) {
$computedValue = /* ... */; // nehéz számítások
return $computedValue;
});
PSR-16 használatával
A Nette Cache PSR-16 interfésszel való használatához használhatja a PsrCacheAdapter
. Ez lehetővé teszi a
Nette Cache és bármely olyan kód vagy könyvtár közötti zökkenőmentes integrációt, amely PSR-16 kompatibilis
gyorsítótárat vár el.
$psrCache = new Nette\Bridges\Psr\PsrCacheAdapter($storage);
Most már használhatja a $psrCache
oldalt PSR-16 gyorsítótárként:
$psrCache->set('key', 'value', 3600); // 1 órán át tárolja az értéket
$value = $psrCache->get('key', 'default');
Az adapter támogatja a PSR-16-ban definiált összes módszert, beleértve a getMultiple()
,
setMultiple()
és deleteMultiple()
módszereket.
Kimeneti gyorsítótárazás
A kimenet nagyon elegánsan rögzíthető és gyorsítótárazható:
if ($capture = $cache->capture($key)) {
echo ... // néhány adat kiírása
$capture->end(); // a kimenet elmentése a gyorsítótárba
}
Abban az esetben, ha a kimenet már a gyorsítótárban van, a capture()
metódus kiírja azt, és visszaadja a
null
címet, így a feltétel nem kerül végrehajtásra. Ellenkező esetben elkezdi pufferelni a kimenetet, és
visszaadja a $capture
objektumot, amelynek segítségével végül elmentjük az adatokat a gyorsítótárba.
A 3.0-s verzióban a metódus neve $cache->start()
volt.
Tárolás a Latte-ban
A Latte sablonokban a gyorsítótárazás nagyon egyszerű, csak csomagoljuk be a
sablon egy részét címkékkel. {cache}...{/cache}
. A gyorsítótár automatikusan érvénytelenítésre kerül,
amikor a forrássablon megváltozik (beleértve a {cache}
címkékben szereplő sablonokat is). A
{cache}
címkék egymásba ágyazhatók, és amikor egy egymásba ágyazott blokk érvénytelenítésre kerül
(például egy címkével), a szülő blokk is érvénytelenítésre kerül.
A címkében meg lehet adni azokat a kulcsokat, amelyekhez a gyorsítótárat kötni kell (itt a $id
változót),
és be lehet állítani a lejárati és érvénytelenítési címkéket.
{cache $id, expire: '20 minutes', tags: [tag1, tag2]}
...
{/cache}
Minden paraméter opcionális, tehát nem kell megadni a lejáratot, a címkéket vagy a kulcsokat.
A gyorsítótár használata a if
segítségével feltételekhez is köthető – a tartalom ekkor csak akkor
kerül gyorsítótárba, ha a feltétel teljesül:
{cache $id, if: !$form->isSubmitted()}
{$form}
{/cache}
Tárolók
A tároló egy olyan objektum, amely az adatok fizikai tárolási helyét jelöli. Használhatunk adatbázist, Memcached kiszolgálót, vagy a legelérhetőbb tárolókat, amelyek a lemezen lévő fájlok.
Tárolás | Leírás |
---|---|
FileStorage | alapértelmezett tároló a lemezen lévő fájlokba történő mentéssel |
MemcachedStorage | a Memcached szervert használja. |
MemoryStorage | az adatok ideiglenesen a memóriában vannak. |
SQLiteStorage | az adatok SQLite adatbázisban tárolódnak |
DevNullStorage | az adatok nincsenek tárolva – tesztelési célokra. |
A tárolási objektumot a Nette\Caching\Storage
típus függőségi injektálással történő
átadásával kapja meg. Alapértelmezés szerint a Nette egy FileStorage objektumot biztosít, amely az adatokat az ideiglenes fájlok könyvtárának
cache
almappájában tárolja .
A tárolót a konfigurációban megváltoztathatja:
services:
cache.storage: Nette\Caching\Storages\DevNullStorage
FileStorage
A gyorsítótárat a lemezen lévő fájlokra írja. A tároló Nette\Caching\Storages\FileStorage
nagyon jól
optimalizált a teljesítményre, és mindenekelőtt biztosítja a műveletek teljes atomicitását. Mit jelent ez? Azt, hogy a
gyorsítótár használatakor nem fordulhat elő, hogy olyan fájlt olvassunk, amelyet egy másik szál még nem írt ki teljesen,
vagy hogy valaki “kezünk alatt” törölné azt. A gyorsítótár használata tehát teljesen biztonságos.
Ez a tároló rendelkezik egy fontos beépített funkcióval is, amely megakadályozza a CPU-használat extrém mértékű növekedését, amikor a gyorsítótár törlődik vagy hideg (azaz nem jön létre). Ez a cache stampede megelőzése. Előfordul, hogy egy pillanatban több egyidejű kérés is van, amelyek ugyanazt a dolgot akarják a gyorsítótárból (pl. egy drága SQL-lekérdezés eredményét), és mivel az nincs gyorsítótárban, minden folyamat ugyanazt az SQL-lekérdezést kezdi végrehajtani. A processzorterhelés megsokszorozódik, és még az is előfordulhat, hogy egyetlen szál sem tud válaszolni az időkorláton belül, a gyorsítótár nem jön létre, és az alkalmazás összeomlik. Szerencsére a Nette-ben a gyorsítótár úgy működik, hogy ha egy elemre több egyidejű kérés érkezik, akkor azt csak az első szál generálja, a többiek várnak, majd használják a generált eredményt.
Példa egy FileStorage létrehozására:
// a tároló a lemezen lévő '/path/to/temp' könyvtár lesz.
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp');
MemcachedStorage
A Memcached kiszolgáló egy nagy teljesítményű elosztott tárolórendszer, amelynek
adaptere a Nette\Caching\Storages\MemcachedStorage
. A konfigurációban adja meg az IP-címet és a portot, ha az
eltér a szabványos 11211-től.
PHP-bővítményt igényel memcached
.
services:
cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')
MemoryStorage
Nette\Caching\Storages\MemoryStorage
egy olyan tároló, amely az adatokat egy PHP tömbben tárolja, és így a
kérés befejezésekor elveszik.
SQLiteStorage
Az SQLite adatbázis és az adapter Nette\Caching\Storages\SQLiteStorage
lehetőséget kínál a lemezen lévő
egyetlen fájlban történő gyorsítótárazásra. A konfiguráció megadja ennek a fájlnak az elérési útvonalát.
Szükséges a pdo
és a pdo_sqlite
PHP-bővítményeket.
services:
cache.storage: Nette\Caching\Storages\SQLiteStorage('%tempDir%/cache.db')
DevNullStorage
A tárolás egy speciális megvalósítása a Nette\Caching\Storages\DevNullStorage
, amely valójában
egyáltalán nem tárol adatokat. Ezért alkalmas tesztelésre, ha ki akarjuk küszöbölni a gyorsítótár hatását.
A gyorsítótár használata a kódban
A gyorsítótárazást a kódban kétféleképpen használhatjuk. Az első az, hogy a tárolási objektumot függőségi injektálással átadva megkapja, majd
létrehoz egy objektumot Cache
:
use Nette;
class ClassOne
{
private Nette\Caching\Cache $cache;
public function __construct(Nette\Caching\Storage $storage)
{
$this->cache = new Nette\Caching\Cache($storage, 'my-namespace');
}
}
A második mód az, hogy megkapja a tárolási objektumot Cache
:
class ClassTwo
{
public function __construct(
private Nette\Caching\Cache $cache,
) {
}
}
A Cache
objektum ezután közvetlenül a konfigurációban jön létre a következőképpen:
services:
- ClassTwo( Nette\Caching\Cache(namespace: 'my-namespace') )
Folyóirat
A Nette a címkéket és prioritásokat egy úgynevezett naplóban tárolja. Ehhez alapértelmezés szerint az SQLite és a
journal.s3db
fájlt használja, és a pdo
és a pdo_sqlite
kiterjesztések
szükségesek.
A naplót a konfigurációban megváltoztathatja:
services:
cache.journal: MyJournal
DI szolgáltatások
Ezek a szolgáltatások hozzáadódnak a DI konténerhez:
Név | Típus | Leírás |
---|---|---|
cache.journal |
Nette\Caching\Storages\Journal | journal |
cache.storage |
Nette\Caching\Storage | repository |
Cache kikapcsolása
A gyorsítótár kikapcsolásának egyik módja az alkalmazásban a tároló DevNullStorage értékre állítása:
services:
cache.storage: Nette\Caching\Storages\DevNullStorage
Ez a beállítás nem befolyásolja a Latte vagy a DI konténer sablonjainak gyorsítótárazását, mivel ezek a könyvtárak nem használják a nette/caching szolgáltatásait, és önállóan kezelik a gyorsítótárukat. Ráadásul a gyorsítótárukat nem kell kikapcsolni fejlesztési módban.