Nu scrieți niciodată soluția de cache

O altă modalitate de a accelera lucrul cu baza de date este să memorezi în cache obiectele pe care le-am solicitat deja mai devreme.

Important! Nu scrieți niciodată propria soluție de cache. Această sarcină are atât de multe capcane la care nu ai visat niciodată.

Problema 1 - golirea memoriei cache . Uneori apar evenimente când un obiect trebuie eliminat din cache sau actualizat în cache. Singura modalitate de a face acest lucru în mod competent este să treceți toate cererile către baza de date prin motorul cache. În caz contrar, de fiecare dată va trebui să spuneți în mod explicit cache-ului care obiecte din el ar trebui să fie șterse sau actualizate.

Problema 2 - lipsa memoriei . Memorarea în cache pare o idee grozavă până când descoperiți că obiectele din memorie ocupă mult spațiu. Aveți nevoie de zeci de gigaocteți suplimentari de memorie pentru ca cache-ul aplicației server să funcționeze eficient.

Și deoarece există întotdeauna o lipsă de memorie, este nevoie de o strategie eficientă pentru ștergerea obiectelor din cache. Acesta este oarecum similar cu colectorul de gunoi din Java. Și după cum vă amintiți, de zeci de ani, cele mai bune minți au inventat diverse moduri de a marca obiectele pe generații etc.

Problema 3 - strategii diferite . După cum arată practica, diferite strategii de stocare și actualizare în cache sunt eficiente pentru diferite obiecte. Un sistem eficient de stocare în cache nu poate face o singură strategie pentru toate obiectele.

Problema 4 - Stocarea eficientă a . Nu puteți stoca doar obiecte în cache. Obiectele conțin prea des referințe la alte obiecte și așa mai departe. În acest ritm, nu veți avea nevoie de un colector de gunoi: pur și simplu nu va avea nimic de eliminat.

Prin urmare, în loc să stocați obiectele în sine, uneori este mult mai eficient să stocați valorile câmpurilor lor primitive. Și sisteme pentru construirea rapidă a obiectelor pe baza acestora.

Ca rezultat, veți obține un întreg DBMS virtual în memorie, care ar trebui să funcționeze rapid și să consume puțină memorie.

Memorarea în cache a bazei de date

Pe lângă stocarea în cache direct într-un program Java, stocarea în cache este adesea organizată direct în baza de date.

Există patru abordări mari:

Prima abordare este denormalizarea bazei de date . Serverul SQL stochează datele în memorie diferit de modul în care sunt stocate în tabele.

Când datele sunt stocate pe disc în tabele, de foarte multe ori dezvoltatorii încearcă să evite pe cât posibil duplicarea datelor - acest proces se numește normalizarea bazei de date. Deci, pentru a accelera lucrul cu datele din memorie, se efectuează procesul invers - denormalizarea bazei de date. O grămadă de tabele înrudite pot fi deja stocate într-o formă combinată - sub formă de tabele uriașe etc.

A doua abordare este stocarea în cache a interogărilor . Și rezultatele interogării.

SGBD vede că de foarte multe ori îi vin aceleași solicitări sau similare. Apoi pur și simplu începe să memoreze aceste solicitări și răspunsurile lor. Dar, în același timp, trebuie să vă asigurați că rândurile care s-au modificat în baza de date sunt eliminate din cache în timp util.

Această abordare poate fi foarte eficientă pentru o ființă umană care poate analiza interogările și poate ajuta DBMS să descopere cum să le memoreze cel mai bine.

A treia abordare este o bază de date în memorie .

O altă abordare frecvent utilizată. O altă bază de date este plasată între server și SGBD, care stochează toate datele sale doar în memorie. Se mai numește și In-Memory-DB. Dacă aveți mai multe servere diferite care accesează aceeași bază de date, atunci folosind In-Memory-DB puteți organiza stocarea în cache în funcție de tipul unui anumit server.

Exemplu:

Abordarea 4 - cluster de baze de date . Mai multe baze numai pentru citire.

O altă soluție este utilizarea unui cluster: mai multe SGBD-uri de același tip conțin date identice. În același timp, puteți citi date din toate bazele de date și puteți scrie doar într-una singură. Care este apoi sincronizat cu restul bazelor de date.

Aceasta este o soluție foarte bună deoarece este ușor de configurat și funcționează în practică. De obicei, pentru o solicitare către baza de date de modificare a datelor, la aceasta vin 10-100 de solicitări de citire a datelor.

Tipuri de cache în Hibernate

Hibernate acceptă trei niveluri de stocare în cache:

  • Memorarea în cache la nivel de sesiune (Sesiune)
  • Memorarea în cache la nivelul SessionFactory
  • Memorarea în cache a cererilor (și a rezultatelor acestora)

Puteți încerca să reprezentați acest sistem sub forma unei astfel de figuri:

Cel mai simplu tip de cache (numit și primul nivel cache ) este implementat la nivelul sesiunii Hibernate. Hibernate folosește întotdeauna acest cache în mod implicit și nu poate fi dezactivat .

Să luăm imediat în considerare următorul exemplu:

Employee director1 = session.get(Employee.class, 4);
Employee director2 = session.get(Employee.class, 4);

assertTrue(director1 == director2);

Poate părea că aici vor fi executate două interogări către baza de date, dar nu este așa. După prima solicitare către baza de date, obiectul Employee va fi stocat în cache. Și dacă interogați din nou obiectul în aceeași sesiune, Hibernate va returna același obiect Java.

Același obiect înseamnă că și referințele la obiect vor fi identice. Este într-adevăr același obiect.

Metodele save() , update() , saveOrUpdate() , load() , get() , list() , iterate() și scroll() vor folosi întotdeauna primul nivel cache. De fapt, nu mai este nimic de adăugat.

Memorarea în cache de al doilea nivel

Dacă primul nivel cache este legat de obiectul sesiune, atunci cache-ul de al doilea nivel este legat de obiectul sesiune.SessionFactory. Ceea ce înseamnă că vizibilitatea obiectelor din acest cache este mult mai largă decât în ​​cache-ul de prim nivel.

Exemplu:

Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();

Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();

assertTrue(director1 != director2);
assertTrue(director1.equals(director2));

În acest exemplu, două interogări vor fi făcute în baza de date. Hibernate va returna obiecte identice, dar nu va fi același obiect - vor avea referințe diferite.

Memorarea în cache de al doilea nivel este dezactivată în mod implicit . Prin urmare, avem două interogări către baza de date în loc de una.

Pentru a-l activa, trebuie să scrieți următoarele linii în fișierul hibernate.cfg.xml:

<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletEhCacheProvider"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>

După activarea memoriei cache de nivel al doilea, comportamentul Hibernate se va schimba puțin:

Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();

Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();

assertTrue(director1 == director2);

Numai după toate aceste manipulări va fi activată cache-ul de al doilea nivel, iar în exemplul de mai sus, va fi executată o singură interogare la baza de date.