Harc a cache-sel - 1. rész

img

Egy új programot készítek szabadidőmben, ami már most - a tervezési fázisban - is nagyon bonyolultnak ígérkezik. Legalább van benne egy kis kihívás. Első körben megpróbáltam egy létező kód újraírását, bővítését, de rájöttem, hogy a saját logikám alapján gyorsabban fogok haladni. Sajnos, mire megszokok egy új logikát, addig rengeteg idő elmegy, ami nem frankó. Tehát marad a saját logika.

Alapként a jelenlegi motoromat akarom használni, csak kicsit tehermentesíteném az adatbázist. A jelenlegi motor működési elve tök egyszerű, van egy xy_menus tábla az adatbázisban, minden menüpontnak 3 vezérlőoszlopa:

  • file
  • type
  • link

A slug alapján lekérdezi az adatbázisból a megfelelő menüponthoz tartozó értékeket, majd megnézi, hogy van-e link érték. Ha talál a mezőben valamit, akkor automatikusan átirinyítja a felhasználót a linkre. A type mezőt hasonló tartalmak, pl szöveges tartalmak betöltésére használja, így ha ebben a mezőben mondjuk text-content értéket talál, akkor automatikusan betölti a szöveges tartalmat. Majd végül, ha a másik két mező üres, mepróbálja betölteni az adott filet.

Ez eddig szép is, de ami emögött van, arra nagyon nem vagyok büszke... Felesleges körökkel fut a program, rengeteg olyan feladatot végez el, amit nem kellene. Így most csak a logikát akarom megtartani, a sallangot kihajigálom és újratervezem a működést.

Első körben utána olvastam, hogy a neten, ki, mit ajánl cachelésre. Nem kell bonyolultnak lennie, a legtöbbször elég egy szimpla fájlalapú cache. Így, ha ezt jól bekötöm, az adatbázist nem is kell használnia a programnak, csak cache frissítéskor. Miért épp file alapú? A legtöbb programom osztott hoszton fut, ahol nincs Memcache(d), APC, XCache vagy Redis telepítve, így maradt két lehetőségem, fájl cache, vagy adatbázis cache. Esetleg még Cookie cache.

Miért nem adatbázis? Mert pont az adatbázisról akarom levenni a terhet. Lehetne használni SQLite-ot, de akkor 2 külön adatbázist kellene használni. Bonyolult és szerintem felesleges.

Miért nem Cookie? Erre egyszerű a válasz, csak kevesebb mennyiségű adatot tudok letárolni, és ráadásul csak olyan adatot, ami nem felhasználó érzékeny. Ráadásul lassítja a forgalmat, ha több adatot tárolok a kelleténél, mivel a böngésző az összes Cookiet elküldi, minden lekéréskor. Még bonyolultabb és még feleslegesebb.

Maradt a mindenhol elérhető (legalábbis elég sok helyen) file alapon működő cache. Jártam már úgy, hogy a hoszton az összes fájlkezelő művelet le volt tiltva, biztonsági okokból. Vagyis ezt mondta a rendszergizda. Kérdeztem tőle, hogy akkor hogyan töltsek fel filet a szerverre, ha nem lehet. Ő ezt nem tudja, de mások megoldották. Pislogtam nagyokat, hogy az összes filekezelő művelet le van tiltva, akkor mégis hogy? Tuti, hogy van valamilyen hátsó kapu, amivel ezt ki tudtam volna kerülni, de a templateket is cacheltük, több helyen is fájlfeltöltés volt. Annyit meg nem katunk érte, hogy érdemes legyen vele vesződni. Az ügyfelet ráeresztettem a rendszergizdára, és mondtam, hogy ez a ti harcotok, végül átrakták az oldalt egy másik tárhelyre.

Első mérkőzés - a megfelelő technika kiválasztása

Mivel terveim szerint a program főleg a lekérdezések eredményét fogja cache-be rakni, így első körben támogatnia kell tömbök letárolását is. Először úgy gondoltam, hogy simán php fileként fogom letárolni a tömböket, így csak include-olni kell majd őket. De ezzel megint csak a baj lenne, mert adatbázisba, letároláskor escape-elem a letárolandó infot, de így visszaolvasás után is folyamatosan vizslatni kellene a kódot, hogy a php file ne szakadjon össze. Túl sok nyűg van vele.

Következő ötlet, hogy ráküldök egy json_encode-ot. Ez nem is tűnik rossz ötletnek, de azért kicsit járjuk körbe a dolgokat. Ne ugorjunk fejest, időnk, mint a tenger. Kicsit olvasgattam a témában, hogy mások mit mondanak erre. Kiderült, hogy kisebb mennyiségű adatnál nagyon jó megoldás, de nagyobb mennyiségnél jelentősen romlik a teljesítmény. Tehát ezt az ötletet is dobhatom ki.

A json_encode "ellenségeként" mindenki a serialize-t emlegette, ami sokkal jobban teljesít nagyobb mennyiségű adatnál is.

Tehát jöhet a saját tesztelés is.

JSON encoded in 3.0994415283203E-5 seconds
PHP serialized in 5.5074691772461E-5 seconds

json_encode() was roughly 77.69% faster than serialize()

JSON decoded as object in 0.00017690658569336 seconds
JSON decoded as array in 0.0001530647277832 seconds
PHP unserialized in 4.6014785766602E-5 seconds

unserialize() was roughly 284.46% faster than json_decode($str, 0)
unserialize() was roughly 232.64% faster than json_decode($str, 1)

JSON decode+encode($str,0) in 0.00020790100097656 seconds
JSON decode+encode($str,1) in 0.00018405914306641 seconds

serialize+unserialize in 0.00010108947753906 seconds

serialize was roughly 105.66% faster than JSON as object
serialize was roughly 82.08% faster than JSON as array

Size of array: 110 elements
Size of serialized: 1948 chrs
Size of json: 1272 chrs

PHP Version: 5.5.9 (5.5.9-1ubuntu4.6)

Ez egy kisebb 110 elemből álló tömb volt.

Nézzünk egy nagyobbat.

JSON encoded in 0.014561891555786 seconds
PHP serialized in 0.043516874313354 seconds

json_encode() was roughly 198.84% faster than serialize()

JSON decoded as object in 0.087249994277954 seconds
JSON decoded as array in 0.10044312477112 seconds
PHP unserialized in 0.036978006362915 seconds

unserialize() was roughly 135.95% faster than json_decode($str, 0)
unserialize() was roughly 171.63% faster than json_decode($str, 1)

JSON decode+encode($str,1) in 0.1150050163269 seconds
JSON decode+encode($str,0) in 0.10181188583374 seconds

serialize+unserialize in 0.08049488067627 seconds

serialize was roughly 42.87% faster than JSON as array
serialize was roughly 26.48% faster than JSON as object

Size of array: 111110 elements
Size of serialized: 1955548 chrs
Size of json: 1277772 chrs

PHP Version: 5.5.9 (5.5.9-1ubuntu4.6)

A tesztelés alap kódja, ami csak a json_encode() és a serialize() közötti különbséget méri, StackOverflow-ról van. Persze kicsit bővítettem, hogy mindent össze tudjak hasonlítani. Mivel nekem nem csak encode-ra van szükségem, hanem az adatokat gyorsan kell decode-olni is. Ebből jól látszik, hogy a serialize() lassabban encode-ol, de visszahozza a lemaradását visszaolvasáskor.

Természetesen ez a lényeg. Az, mellékes szerintem, hogy a letárolt file mérete kicsit nagyobb, főleg nagyobb tömbök esetén. Nekem most sokkal fontosabb, hogy gyorsabb legyen a visszaolvasás.

Most próbálom a visszaolvasási időt még lejjebb szorítani, átnézem a kódot és megpróbálok minden felesleges műveletet kivenni belőle. De pont ez a legizgalmasabb része, hogy nem csak megírom a kódot, hanem folyamatosan próbálom fejleszteni is. Van is a fejemben néhány ötlet, hogy milyen műveleteken nyerhetek még néhány ezredet.

Szerintem ennek a bejegyzésnek itt lesz a vége, mert már így is hosszúra sikerült, tehát a második részt egy újabb bejegyzésbe teszem.