Adăugați Caching la un strat de acces la date

Paginile web dinamice sunt minunate; puteți să vă adaptați pagina rezultată utilizatorului, să arătați activitatea altor utilizatori, să oferiți clienților dvs. diferite produse pe baza istoricului lor de navigare și așa mai departe. Dar cu cât este mai dinamic un site web, cu atât mai multe interogări de bază de date pe care probabil că trebuie să le efectuați. Din păcate, aceste interogări de bază de date consumă cea mai mare parte a timpului de funcționare.

În acest tutorial, voi demonstra o modalitate de îmbunătățire a performanței, fără a rula alte întrebări inutile. Vom dezvolta un sistem de cache de interogare pentru stratul nostru de date cu costuri reduse de programare și de implementare.

1. Stratul de acces la date

Adăugarea unui strat de cache în mod transparent unei aplicații este adesea dificilă din cauza designului intern. Cu limbile orientate pe obiecte (cum ar fi PHP 5) este mult mai ușor, dar poate fi încă complicat de designul slab.

În acest tutorial, setăm punctul nostru de pornire într-o aplicație care execută accesul la baza sa de date printr-o clasă centralizată, din care toate modelele de date moștenesc metodele de bază de acces la baza de date. Scheletul pentru această clasă de pornire arată astfel:

 clasa model_Model static protejat $ DB = null; funcția __construct ()  funcția protejată doStatement ($ query)  funcția protejată quoteString ($ value) 

Să o implementăm pas cu pas. În primul rând, constructorul care va folosi biblioteca PDO pentru a interfața cu baza de date:

 funcția __construct () // conectați la DB dacă este necesar dacă (is_null (self :: $ DB)) $ dsn = app_AppConfig :: getDSN (); $ db_user = aplicațieAppConfig :: getDBUser (); $ db_pass = app_AppConfig :: getDBPassword (); auto :: $ DB = nou DOP ($ dsn, $ db_user, $ db_pass); auto :: $ DB-> setAttribute (PDO :: ATTR_ERRMODE, PDO :: ERRMODE_EXCEPTION); 

Ne conectăm la baza de date utilizând biblioteca PDO. Pentru acreditările bazei de date folosesc o clasă statică numită "app_AppConfig" care centralizează informațiile de configurare ale aplicației.

Pentru a stoca conexiunea bazei de date, folosim un atribut static ($ DB). Utilizăm un atribut static pentru a împărtăși aceeași conexiune cu toate instanțele "model_model" și, din acest motiv, codul de conectare este protejat cu un if (nu vrem să ne conectăm de mai multe ori).

În ultima linie a constructorului am setat modelul de eroare de excepție pentru PDO. În acest model, pentru fiecare eroare identificată de PDO, aceasta aruncă o excepție (clasa PDOException) în loc să returneze valorile de eroare. Aceasta este o problemă de gust, dar restul codului poate fi păstrat mai curat cu modelul excepțional, ceea ce este bun pentru acest tutorial.

Executarea interogărilor poate fi foarte complexă, dar în această clasă am luat o abordare simplă cu o singură metodă doStatement ():

 funcția protejată doStatement ($ query) $ st = auto :: $ DB-> interogare ($ query); dacă ($ st-> columnCount ()> 0) întoarcere $ st-> fetchAll (PDO :: FETCH_ASSOC);  altceva return array (); 

Această metodă execută interogarea și returnează o matrice asociativă cu întregul set de rezultate (dacă există). Rețineți că folosim conexiunea statică (self :: $ DB). Rețineți, de asemenea, că această metodă este protejată. Acest lucru se datorează faptului că nu vrem ca utilizatorul să execute interogări arbitrare. În schimb, vom oferi modele concrete utilizatorilor. Vom vedea mai târziu, dar înainte de a pune în aplicare ultima metodă:

 funcția protejată quoteString (valoare $) return self :: $ DB-> quote (valoare $, DOP :: PARAM_STR); 

Clasa "model_model" este o clasă foarte simplă, dar convenabilă pentru întinderea datelor. Deși este simplu (poate fi îmbunătățit cu funcții avansate, cum ar fi instrucțiunile pregătite dacă doriți), acesta face lucrurile de bază pentru noi.

Pentru a finaliza partea de configurare a aplicației noastre, scrieți clasa statică "app_Config":

 class app_AppConfig funcția publică statică getDSN () return "mysql: host = localhost; dbname = test";  funcția publică statică getDbUser () return "test";  funcția publică statică getDbPassword () return "MyTest"; 

Așa cum am menționat anterior, vom oferi modele concrete pentru a accesa baza de date. Ca un exemplu mic, vom folosi această schemă simplă: un tabel de documente și un index inversat pentru a căuta dacă un document conține un cuvânt dat sau nu:

 CREATE TABLE documente (cheia primară id integer, proprietar varchar (40) nu null, server_location varchar (250) nu null); CREATE TABLE cuvinte (cuvânt char (30), doc_id integer nu documente de referință null (id), KEY PRIMARY (cuvânt, doc_id))

Din clasa de acces de date de bază (model_Model), derivăm cât mai multe clase, după cum este necesar, prin proiectarea datelor aplicației noastre. În acest exemplu, putem deduce aceste două clase auto-explicative:

 class_Index extinde model_Model funcția publică getWord ($ word) return $ this-> doStatement ("SELECT doc_id FROM CUVINTE WHERE cuvânt =". $ this-> quoteString ($ word));  class model_Documents extinde model_Model public function get ($ id) return $ this-> doStatement ("SELECT * FROM documents WHEREcoche"); var_dump ($ cuvinte);

Rezultatul pentru acest exemplu ar putea arăta similar cu cel obținut (evident, depinde de datele dvs. reale):

 Array (1) ["doc_id"] => șir (4) "4630" [1] => ) "4635" [2] => șir (1) ["doc_id"] => șir (4) ) "4922" [4] => array (1) ["doc_id"] => șir (4) "5373" 

Ceea ce am scris este arătat în următoarea diagramă a clasei UML:

2. Planificarea schemei noastre de memorare în cache

Când lucrurile încep să se prăbușească în serverul dvs. de bază de date, este timpul să faceți o pauză și să luați în considerare optimizarea stratului de date. După ce ați optimizat interogările dvs., adăugând indexurile adecvate etc., cea de-a doua mișcare este încercarea de a evita interogările inutile: de ce faceți aceeași solicitare la baza de date pentru fiecare solicitare a utilizatorului, dacă aceste date nu se schimbă cu greu?

Cu o organizație de clasă bine planificată și bine decuplată, putem adăuga un strat suplimentar aplicației noastre aproape fără costuri de programare. În acest caz, vom extinde clasa "model_model" pentru a adăuga cache transparent în stratul de bază de date.

Bazele Caching

Deoarece știm că avem nevoie de un sistem de memorare a cache-urilor, să ne concentrăm asupra acelei probleme particulare și, după ce vom fi soluționați, vom fi integrați în modelul nostru de date. Pentru moment, nu vom gândi în termeni de interogări SQL. Este ușor să abrogeți puțin și să construiți o schemă destul de generală.

Cea mai simplă schemă de cache constă în perechi [chei, date], unde cheia identifică datele reale pe care dorim să le stocăm. Această schemă nu este nouă, de fapt, este analogă cu array-urile asociative ale PHP și o folosim tot timpul.

Așadar vom avea nevoie de o modalitate de a stoca o pereche, de ao citi și de ao șterge. Asta e suficient pentru a construi interfața noastră pentru ajutoarele cache:

 interfața cache_CacheHelper funcția get (cheia $); funcția pune (cheia $, $ date); funcția ștergeți (tasta $); 

Interfața este destul de ușoară: metoda get obține o valoare, având în vedere cheia de identificare a acesteia, setul de metode pune (sau actualizează) valoarea pentru o anumită cheie și metoda ștergerii o șterge.

Având în vedere această interfață, este timpul să implementăm primul nostru modul de cache real. Dar înainte de a face acest lucru, vom alege metoda de stocare a datelor.

Sistemul de stocare de bază

Decizia de a construi o interfață comună (cum ar fi cache_CacheHelper) pentru asistenții de caching ne va permite să le implementăm aproape de fiecare magazie. Dar pe partea de sus pe ce sistem de stocare? Există multe dintre ele pe care le putem folosi: memorie partajată, fișiere, servere memcached sau chiar baze de date SQLite.

Adesea subestimate, fișierele DBM sunt perfecte pentru sistemul nostru de caching și le vom folosi în acest tutorial.

Fișierele DBM funcționează necorespunzător pe perechi (chei, date) și o fac foarte rapid datorită organizației interne B-tree. Ele fac, de asemenea, controlul accesului pentru noi: nu avem nevoie să ne facem griji cu privire la blocarea memoriei cache înainte de a scrie (cum va trebui să facem și în cazul altor sisteme de stocare); DBM o face pentru noi.

Fișierele DBM nu sunt conduse de servere scumpe, își fac munca în interiorul unei librării ușoare de pe partea clientului care accesează local la fișierul real care stochează datele. De fapt, acestea sunt de fapt o familie de formate de fișiere, toate cu același API de bază pentru accesul (cheie, date). Unele dintre ele permit chei repetate, altele sunt constante și nu permit scrierea după închiderea fișierului pentru prima dată (cdb) etc.. Puteți citi mai multe despre aceasta pe http://www.php.net/manual/en/dba.requirements.php

Aproape fiecare sistem UNIX instalează un tip sau mai multe dintre aceste biblioteci (probabil Berkeley DB sau GNU dbm). Pentru acest exemplu, vom folosi formatul "db4" (format Sleepycat DB4: http://www.sleepycat.com). Am constatat că această bibliotecă este adesea preinstalată, dar puteți utiliza oricare ar fi biblioteca dorită (cu excepția cdb-ului, desigur: dorim să scriem pe fișier). De fapt, puteți trece această decizie în clasa "app_AppConfig" și să o adaptați pentru fiecare proiect pe care îl faceți.

Cu PHP, avem două variante de tratare a fișierelor DBM: extensia "dba" (http://php.net/manual/en/book.dba.php) sau modulul "PEAR :: DBA" (http: /pear.php.net/package/DBA). Vom folosi extensia "dba", care probabil că ați instalat deja în sistemul dvs..

Așteaptă un minut, avem de-a face cu SQL și seturile de rezultate!

Fișierele DBM lucrează cu șiruri de caractere pentru valori și cheie, dar problema noastră este să stocăm seturi de rezultate SQL (care pot varia destul de mult în structură). Cum am reușit să le convertim de la o lume la alta?

Ei bine, pentru chei, este foarte ușor deoarece șirul de interogare SQL real identifică foarte bine un set de date. Putem folosi digestul MD5 al șirului de interogare pentru a scurta cheia. Pentru valori, este mai complicat, dar aliații dvs. sunt serialize () / unserialize () funcțiile PHP, care pot fi folosite pentru a converti de la array la șir și invers.

Vom vedea cum funcționează toate acestea în secțiunea următoare.

3. Caching static

În primul nostru exemplu, vom aborda cel mai simplu mod de a efectua cache: cache pentru valori statice. Vom scrie o clasă numită "cache_DBM" care va implementa interfața "cache_CacheHelper", la fel:

 clasa cache_DBM implementează cache_CacheHelper protejat $ dbm = null; funcția __construct ($ cache_file = null) $ this-> dbm = dba_popen ($ cache_file, "c", "db4"); dacă (! $ this-> dbm) aruncați o nouă excepție ("$ cache_file: Nu se poate deschide fișierul cache");  function get ($ cheie) $ data = dba_fetch ($ cheie, $ this-> dbm); dacă ($ data! == false) returnați $ date;  return null;  funcția pusă ($ key, $ data) if (! dba_replace ($ key, $ data, $ this-> dbm)) arunca o excepție nouă ("$ key: Nu se poate stoca");  ștergeți funcția ($ key) if (! dba_delete ($ key, $ this-> dbm)) aruncați o nouă excepție ("cheia $: nu s-a putut șterge"); 

Această clasă este foarte ușor: o mapare între interfața noastră și funcțiile dba. În constructor, fișierul dat este deschis,
iar handle-ul returnat este stocat în obiect pentru al folosi în celelalte metode.

Un exemplu simplu de utilizare:

 $ cache = cache_DBM nou ("/tmp/my_first_cache.dbm"); $ cache-> put ("key1", "prima mea valoare"); echo $ cache-> get ("key1"); $ Cache-> șterge ( "key1"); $ data = $ cache-> get ("cheie1"); dacă (is_null ($ data)) echo "\ nDreptul a fost șters corect!"; 

Mai jos, veți găsi ceea ce am făcut aici, exprimat ca o diagramă a clasei UML:

Acum, să adăugăm sistemul de caching modelului nostru de date. Am fi putut schimba clasa "model_model" pentru a adăuga cache la fiecare dintre clasele sale derivate. Dar, dacă am fi făcut-o, am fi pierdut flexibilitatea de a aloca caracteristica de caching doar pentru anumite modele și cred că aceasta este o parte importantă a jobului nostru.

Așa că vom crea o altă clasă, numită "model_StaticCache", care va extinde "model_Model" și va adăuga funcționalitatea cache. Să începem cu scheletul:

 clasa model_StaticCache se extinde model_Model static protejat $ cache = array (); protejat $ model_name = null; funcția __construct ()  funcția protejată doStatement ($ query) 

În constructor, sunăm mai întâi constructorul părinte pentru a vă conecta la baza de date. Apoi, vom crea și stoca, în mod static, un obiect "cache_DBM" (dacă nu este creat în altă parte). Stocăm o instanță pentru fiecare nume de clasă derivată, deoarece folosim câte un fișier DBM pentru fiecare dintre ele. În acest scop, folosim matricea statică "$ cache".

 funcția __construct () părinte :: __ construct (); $ this-> model_name = get_class ($ this); dacă (! isset (auto :: $ cache [$ this-> model_name])) $ cache_dir = app_AppConfig :: getCacheDir (); auto :: $ cache [$ this-> model_name] = nou cache_DBM ($ cache_dir. $ this-> model_name); 

Pentru a determina directorul în care trebuie să scriem fișierele cache, am folosit din nou clasa de configurare a aplicației: "app_AppConfig".

Și acum: metoda doStatement (). Logica pentru această metodă este: să convertiți instrucțiunea SQL la o cheie validă, să căutați cheia în memoria cache, dacă găsiți returnați valoarea. Dacă nu este găsit, executați-o în baza de date, stocați rezultatul și returnați-l:

 funcția protejată doStatement ($ interogare) $ key = md5 ($ query); $ date = auto :: $ cache [$ this-> model_name] -> obține (cheia $); dacă (! is_null ($ data)) întoarcere unserialize ($ date);  $ data = părinte :: doStatement ($ interogare); auto :: $ cache [$ this-> MODEL_NAME] -> put ($ cheie, serializarea (date) $); returnați date $; 

Există încă două lucruri demne de remarcat. Mai întâi, folosim MD5-ul interogării ca cheie. De fapt, nu este necesar, deoarece biblioteca DBM care se bazează acceptă chei de dimensiune arbitrară, dar pare mai bine să scurtezi cheia oricum. Dacă utilizați instrucțiuni pregătite, nu uitați să concatenați valorile reale la șirul de interogare pentru a crea cheia!

Odată ce modelul "model_StaticCache" este creat, modificarea unui model concret pentru utilizarea sa este trivială, trebuie doar să modificați clauza "extinde" în declarația de clasă:

 clasa model_Documents extinde model_StaticCache 

Și asta e tot, magia se termină! Documentul "model_document" va efectua o singură interogare pentru fiecare document de recuperat. Dar o putem face mai bine.

4. Expirarea memoriei cache

În prima noastră abordare, odată ce o interogare este stocată în memoria cache, aceasta rămâne valabilă pentru totdeauna până când apar două lucruri: ștergem cheia în mod explicit sau deconectăm fișierul DBM.

Cu toate acestea, această abordare este valabilă doar pentru câteva modele de date ale aplicației noastre: datele statice (cum ar fi opțiunile de meniu și astfel de lucruri). Datele normale din aplicația noastră vor fi probabil mai dinamice decât acestea.

Gândiți-vă la un tabel care conține produsele pe care le vindem în pagina noastră web. Este puțin probabil să se schimbe fiecare minut, dar există șansa ca aceste date să se schimbe (adăugând produse noi, schimbând prețurile de vânzare etc.). Avem nevoie de o modalitate de a implementa cache-ul, dar avem o modalitate de a reacționa la schimbările de date.

O abordare a acestei probleme este stabilirea unui timp de expirare a datelor stocate în memoria cache. Atunci când stocăm date noi în memoria cache, setăm o fereastră de timp în care aceste date vor fi valide. După această perioadă, datele vor fi citite din baza de date din nou și stocate în memoria cache pentru o altă perioadă de timp.

Ca și înainte, putem crea o altă clasă derivată din "model_model" cu această funcționalitate. De data aceasta, o vom numi "model_ExpiringCache". Scheletul este similar cu "model_StaticCache":

 clasa model_ExpiringCache se extinde model_Model static protejat $ cache = array (); protejat $ model_name = null; protejat $ expirație = 0; funcția __construct ()  funcția protejată doStatement ($ query) 

În această clasă am introdus un nou atribut: expirare $. Acesta va stoca fereastra de timp configurată pentru date valide. Am stabilit această valoare în constructor, restul constructorului este același ca în "model_StaticCache":

 funcția __construct () părinte :: __ construct (); $ this-> model_name = get_class ($ this); dacă (! isset (auto :: $ cache [$ this-> model_name])) $ cache_dir = app_AppConfig :: getCacheDir (); auto :: $ cache [$ this-> model_name] = nou cache_DBM ($ cache_dir. $ this-> model_name);  $ this-> expiration = 3600; // 1 oră 

Cea mai mare parte a postului vine în dosar. Fișierele DBM nu au nici o cale internă de a controla expirarea datelor, așa că trebuie să implementăm propriile noastre. O vom face prin stocarea unor tablouri, ca aceasta:

 array ("time" => 1250443188, "data" => (datele reale))

Acest tip de matrice este ceea ce serializăm și stocăm în memoria cache. Cheia "timp" reprezintă timpul de modificare a datelor din memoria cache, iar "datele" reprezintă datele reale pe care dorim să le stocăm. În timpul citirii, dacă constatăm existența cheii, comparăm timpul de creație stocat cu ora curentă și returnăm datele dacă nu a expirat.

 funcția protejată doStatement ($ interogare) $ key = md5 ($ query); $ now = time (); $ date = auto :: $ cache [$ this-> model_name] -> obține (cheia $); dacă (! is_null ($ data)) $ data = neserializează ($ date); dacă ($ data ['time'] + $ this-> expirare> $ acum) return $ data ['data']; 

Dacă cheia nu există sau a expirat, vom continua executarea interogării și stocarea noului set de rezultate în memoria cache înainte de ao returna.

 $ data = părinte :: doStatement ($ query); auto $ :: cache [$ this-> model_name] -> put ($ cheie, serialize (array ("data" => $ date, "time" => $ now))); returnați date $; 

Simplu!

Acum, să convertim modelul "model_Index" la un model cu memoria cache existentă. Așa cum se întâmplă, cu "model_Documents", trebuie doar să modificăm declarația clasei și să schimbăm clauza "extinde":

 class_Documents extinde modelul_ExpiringCache 

Despre timpul de expirare ... trebuie să se facă unele considerații. Folosim un timp de expirare constant (1 oră = 3600 secunde), de dragul simplității și pentru că nu vrem să modificăm restul codului nostru. Dar, îl putem modifica cu ușurință în multe moduri pentru a ne permite să folosim diferite durate de expirare, câte unul pentru fiecare model. După aceea vom vedea cum.

Diagrama de clasă pentru toată munca noastră este următoarea:

5. Expirare diferită

În fiecare proiect, sunt sigur că veți avea timp de expirare diferit pentru aproape fiecare model: de la câteva minute la ore sau chiar zile.

Dacă am putea avea un timp de expirare diferit pentru fiecare model, ar fi perfect ... dar așteaptă! Putem să o facem cu ușurință!

Abordarea cea mai directă este de a adăuga un argument constructorului, astfel încât noul constructor pentru "model_ExpiringCache" va fi acesta:

 funcția __construct ($ expiration = 3600) părinte :: __ construct (); $ this-> expirație = $ expirație; ...

Apoi, dacă vrem un model cu o durată de expirare de 1 zi (1 zi = 24 ore = 1.440 minute = 86.400 secunde), putem realiza astfel:

 class_Index extinde model_ExpiringCache function __construct () parent :: __ construct (86400);  ...

Și asta e tot. Cu toate acestea, dezavantajul este că trebuie să modificăm toate modelele de date.

Un alt mod de a face acest lucru este delegarea sarcinii la "app_AppConfig":

 clasa app_AppConfig ... funcția statică publică getExpirationTime ($ model_name) comutator ($ model_name) caz "model_Index": retur 86400; ... implicit: retur 3600; 

Apoi adăugați apelul la această nouă metodă pe constructorul "model_ExpiringCache", după cum urmează:

 funcția __construct () părinte :: __ construct (); $ this-> model_name = get_class ($ this); $ this-> expiration = app_AppConfig :: getExpirationTime ($ this-> model_name); ...

Această metodă recentă ne permite să facem lucruri fanteziste, cum ar fi utilizarea unor valori de expirare diferite pentru medii de producție sau de dezvoltare într-un mod mai centralizat. Oricum, poți alege a ta.

În UML, proiectul total arată astfel:

6. Unele avertismente

Există câteva interogări care nu pot fi stocate în cache. Cele mai evidente sunt modificarea interogărilor ca INSERT, DELETE sau UPDATE. Aceste interogări trebuie să ajungă la serverul bazei de date.

Dar chiar și cu interogările SELECT, există anumite circumstanțe în care un sistem de memorare cache poate crea probleme. Aruncați o privire la o interogare ca aceasta:

 SELECT * FROM bannere WHERE zona = "home" ORDER BY rand () LIMIT 10

Această interogare selectează în mod aleator 10 bannere pentru zona "home" a site-ului nostru. Acest lucru este destinat să genereze mișcări în bannerele afișate în casa noastră, dar dacă am cache această interogare, utilizatorul nu va vedea nici o mișcare, până la expirarea datelor din memoria cache.

Funcția rand () nu este deterministă (deoarece nu este acum () sau altele); astfel încât va reveni la o valoare diferită pentru fiecare execuție. Dacă îl stocăm în memoria cache, vom îngheța doar unul din aceste rezultate pentru toată perioada de cache și, prin urmare, vom rupe funcționalitatea.

Dar, printr-o simplă re-factoring, putem obține beneficiile caching-ului și arată pseudo-aleatorie:

 class_Banners extinde model_ExpiringCache funcția publică getRandom (zona $) $ random_number = rand (1,50); $ banners = $ this-> doStatement ("SELECT * FROM bannere WHERE zone =". $ this-> quoteString ($ zone) ") $ random_number = $ random_number ORDER BY rand () LIMIT 10"); returnează bannere $;  ...

Ceea ce facem aici este de a cache cincizeci diferite configurații bannere aleatorii, și le selectați aleatoriu. Cele 50 de produse SELECT vor arăta astfel:

 SELECT * FROM bannere WHERE zona = "home" ȘI 1 = 1 ORDER BY rand () LIMIT 10 SELECT * FROM bannere WHERE zone = "home" ȘI 2 = 2 ORDER BY rand () LIMIT 10 ... SELECT * FROM bannere WHERE zone = "home" ȘI 50 = 50 ORDER BY rand () LIMIT 10

Am adăugat o condiție constantă pentru selectare, care nu are niciun cost pentru serverul de baze de date, dar face 50 de chei diferite pentru sistemul de caching. Un utilizator va trebui să încarce pagina de cincizeci de ori pentru a vedea toate configurațiile diferite ale bannerului; astfel încât efectul dinamic este atins. Costul este de cincizeci de interogări la baza de date pentru a prelua memoria cache.

7. Un indicator de referință

Ce beneficii ne putem aștepta de la noul nostru sistem de cache?

În primul rând, trebuie spus că, în performanțele prime, uneori noua noastră implementare va fi mai lentă decât interogările bazei de date, mai ales cu interogări foarte simple și bine optimizate. Dar pentru acele interogări care se reunesc, cache-ul DBM va rula mai repede.

Totuși, problema pe care am rezolvat-o nu este o performanță brută. Nu veți avea niciodată un server de baze de date de rezervă pentru testele dvs. în producție. Probabil veți avea un server cu volum mare de lucru. În această situație, chiar și cea mai rapidă interogare poate să înceapă încet, însă cu schema noastră de cache, nu utilizăm nici serverul și, de fapt, reducem volumul de lucru. Astfel, creșterea reală a performanței va avea loc sub forma mai multor petiții pe secundă.

Într-un site web pe care îl dezvolt în prezent, am făcut un simplu punct de referință pentru a înțelege beneficiile caching-ului. Serverul este modest: rulează Ubuntu 8.10 care rulează pe un AMD Athlon 64 X2 5600+, cu 2 GB de RAM și un hard disk PATA vechi. Sistemul rulează Apahce și MySQL 5.0, care vine cu distribuția Ubuntu fără niciun tuning.

Testul a fost acela de a rula programul de referință Apache (ab) cu 1, 5 și 10 clienți concurenți încărcând o pagină de 1000 de ori de pe site-ul meu de dezvoltare. Pagina actuală a fost un detaliu al produsului care conține nu mai puțin de 20 de interogări: conținutul meniului, detalii despre produs, produse recomandate, bannere etc..

Rezultatele fără cache au fost de 4,35 p / s pentru 1 client, 8,25 pentru 5 clienți și 8,29 pentru 10 clienți. Cu cache-ul (expirare diferită), rezultatele au fost 25,55 p / s cu 1 client, 49,01 pentru 5 clienți și 48,74 pentru 10 clienți.

Gândurile finale

V-am arătat o modalitate ușoară de a introduce cache-ul în modelul dvs. de date. Desigur, există o mulțime de alternative, dar aceasta este doar o alegere pe care o aveți.

Am folosit fișiere DBM locale pentru a stoca datele, dar există și alternative mai rapide pe care le-ați putea considera că le explorați. Unele idei pentru viitor: folosind APC-ul apc_store () functioneaza ca sistem de stocare subiacent, memorie partajata pentru datele cu adevarat critice, folosind memcached, etc.

Sper că v-ați bucurat de acest tutorial la fel de mult ca și cum l-am scris. Caching fericit!

Cod