Compresia datelor în Apache Ignite. Experiența Sberbank

Compresia datelor în Apache Ignite. Experiența SberbankCând lucrăm cu volume mari de date, problema lipsei de spațiu pe discuri poate deveni acută. O modalitate de a rezolva această problemă este comprimarea, care ne permite să creștem capacitatea de stocare pe același echipament. În acest articol, vom explora cum funcționează comprimarea datelor în Apache Ignite. Vom discuta doar metodele de comprimare implementate în produs. Alte metode de comprimare a datelor (prin rețea, în memorie), atât implementate, cât și neimplementate, vor fi excluse.

Așadar, cu modul de persistență activat, în urma modificării datelor din cache-uri, Ignite începe să scrie pe disc:

  1. Conținutul cache-urilor
  2. Jurnalul scrierii anticipative (Write Ahead Log, în continuare doar WAL)

Pentru comprimarea WAL, există de ceva timp un mecanism numit compresie WAL. În versiunea recent lansată Apache Ignite 2.8 au fost introduse încă două mecanisme care permit comprimarea datelor pe disc, și anume compresia paginii de disc pentru comprimarea conținutului cache-urilor și compresia instantaneelor paginilor WAL pentru comprimarea unor înregistrări din WAL. Mai multe detalii despre aceste trei mecanisme mai jos.

Compresia paginii de disc

Cum funcționează

Pentru început, să discutăm foarte pe scurt despre cum Ignite stochează datele. Stocarea se realizează prin memorie pe pagini. Dimensiunea paginii este stabilită la pornirea nodului și nu poate fi schimbată în etapele ulterioare; dimensiunea paginii trebuie să fie o putere a lui 2 și să fie multiplă de dimensiunea blocului sistemului de fișiere. Pagini sunt încărcate în RAM din disc pe măsură ce este necesar, dimensiunea datelor de pe disc poate depăși volumul de RAM alocat. În cazul în care RAM nu are suficient spațiu pentru a încărca o pagină din disc, paginile vechi, deja neutilizate, vor fi înlocuite din RAM.

Pe disc, datele sunt stocate în următoarea formă: pentru fiecare partiție a fiecărei grupuri de cache se creează un fișier separat, în acest fișier, paginile sunt aranjate una după alta, în ordinea crescătoare a indexului. Identificatorul complet al paginii conține identificatorul grupului de cache, numărul partiției și indexul paginii în fișier. Astfel, pe baza identificatorului complet al paginii, putem determina fără ambiguitate fișierul și offset-ul din fișier pentru fiecare pagină. Detalii suplimentare despre structura memoriei pe pagini pot fi citite în articolul de pe Apache Ignite Wiki: Ignite Persistent Store — sub capotă.

Mecanismul de comprimare a paginilor de disc, așa cum sugerează numele, funcționează la nivel de pagină. Atunci când acest mecanism este activat, lucrul cu datele în RAM se face așa cum este, fără nicio comprimare, dar în momentul salvării paginilor din RAM pe disc, se aplică comprimarea acestora.

Dar comprimarea fiecărei pagini individual nu este suficientă pentru a rezolva problema; trebuie cumva să reducem dimensiunea fișierelor finale cu date. Dacă dimensiunea paginii nu mai este fixă, nu putem scrie paginile într-un fișier una după alta, deoarece acest lucru poate crea o serie de probleme:

  • Nu vom putea calcula offset-ul la care se află pagina în fișier folosind indexul paginii.
  • Nu este clar ce să facem cu paginile care nu se află la sfârșitul fișierului și își schimbă dimensiunea. Dacă dimensiunea paginii se micșorează, locul pe care l-a eliberat se pierde. Dacă dimensiunea paginii crește, trebuie să căutăm un nou loc în fișier pentru aceasta.
  • Dacă pagina se va deplasa cu un număr de octeți care nu este multiplu de dimensiunea blocului sistemului de fișiere, pentru a o citi sau salva va trebui să atingem un bloc de sistem de fișiere în plus, ceea ce poate duce la degradarea performanței.

Pentru a nu rezolva aceste probleme la nivelul său, comprimarea paginilor de disc în Apache Ignite utilizează un mecanism de sistem de fișiere numit fișiere sparse. Un fișier sparse este un fișier în care unele regiuni umplute cu zerouri pot fi marcate ca „găuri”. Astfel, blocurile de sistem de fișiere pentru stocarea acestor găuri nu vor fi alocate, ceea ce duce la economisirea de spațiu pe disc.

Este logic că pentru a elibera un bloc de sistem de fișiere, dimensiunea găurii trebuie să fie mai mare sau egală cu dimensiunea blocului de sistem de fișiere, ceea ce impune o limitare suplimentară asupra dimensiunii paginii în Apache Ignite: pentru ca comprimarea să aibă un efect, dimensiunea paginii trebuie să fie strict mai mare decât dimensiunea blocului de sistem de fișiere. Dacă dimensiunea paginii este egală cu dimensiunea blocului, nu vom putea elibera niciun bloc, deoarece pentru a elibera un singur bloc, pagina comprimată trebuie să ocupe 0 octeți. Dacă dimensiunea paginii este egală cu dimensiunea a 2 sau 4 blocuri, putem elibera cel puțin un bloc dacă pagina noastră se comprimă cu cel puțin 50% sau 75%, respectiv.

Astfel, descrierea finală a funcționării mecanismului: Atunci când se scrie o pagină pe disc, se încearcă comprimarea acesteia. Dacă dimensiunea paginii comprimate permite eliberarea unui sau mai multor blocuri din sistemul de fișiere, pagina este scrisă în format comprimat, iar în locul blocurilor eliberate se creează un "gol" (se execută un apel de sistem) fallocate() cu flagul „punch hole”). Dacă dimensiunea paginii comprimate nu permite eliberarea blocurilor, pagina este salvată așa cum este, în format necomprimat. Toate offseturile paginilor sunt considerate la fel ca și fără compresie, prin înmulțirea indexului paginii cu dimensiunea paginii. Nicio relocare a paginilor nu este necesară din partea utilizatorului. Offseturile paginilor, ca și în cazul fără compresie, se aliniază la limitele blocurilor din sistemul de fișiere.

Compresia datelor în Apache Ignite. Experiența Sberbank

În implementarea actuală, Ignite poate lucra cu fișiere sparse doar pe sistemul de operare Linux, astfel încât compresia paginilor pe disc poate fi activată doar atunci când se folosește Ignite pe acest sistem de operare.

Algoritmii de compresie care pot fi utilizați pentru compresia paginilor pe disc: ZSTD, LZ4, Snappy. În plus, există un mod de lucru (SKIP_GARBAGE), în care doar spațiile neutilizate în pagină sunt eliminate fără aplicarea compresiei asupra datelor rămase, ceea ce permite reducerea sarcinii pe CPU comparativ cu algoritmii menționați anterior.

Influența asupra performanței

Din păcate, nu am efectuat măsurători reale ale performanței în medii reale, deoarece nu se preconizează utilizarea acestui mecanism în producție, dar putem specula teoretic unde vom pierde și unde vom câștiga.

Pentru aceasta, trebuie să ne amintim cum se realizează citirea și scrierea paginilor la accesarea acestora:

  • În timpul operației de citire, întâi se caută în RAM, dacă căutarea eșuează, pagina este încărcată în RAM din disc de către același fir de execuție care efectuează citirea.
  • În timpul operației de scriere, pagina din RAM este marcată ca murdară, iar salvarea fizică a paginii pe disc nu se efectuează imediat de către firul care execută scrierea. Toate paginile murdare sunt salvate pe disc ulterior, în timpul procesului de checkpoint de către fire separate.

Astfel, influența asupra operațiunilor de citire:

  • Pozitivă (disk IO), datorită reducerii numărului de blocuri citite din sistemul de fișiere.
  • Negativ (CPU), din cauza sarcinii suplimentare necesare sistemului de operare pentru a lucra cu fișiere sparse. De asemenea, este posibil să apară implicit operații IO suplimentare pentru salvarea unei structuri mai complexe a fișierului sparse (din păcate, nu sunt familiarizat cu toate detaliile funcționării fișierelor sparse).
  • Negativ (CPU), din cauza necesității de decomprimare a paginilor.
  • Nu există influențe asupra operațiunilor de scriere.
  • Influența asupra procesului de checkpoint (aici totul este similar operațiunilor de citire):
  • Pozitiv (disk IO), datorită reducerii numărului de blocuri scrise în sistemul de fișiere.
  • Negativ (CPU, posibil disk IO), din cauza lucrului cu fișierele sparse.
  • Negativ (CPU), din cauza necesității de comprimare a paginilor.

Care dintre cele două talere va cântări mai mult? Totul depinde foarte mult de mediu, dar mă inclin să cred că compresia paginilor disk va duce mai degrabă la o degradare a performanței în majoritatea sistemelor. Cu atât mai mult cu cât testele pe alte RDBMS care folosesc o abordare similară cu fișierele sparse arată o scădere a performanței atunci când compresia este activată.

Cum să activăm și să configurăm

Așa cum am menționat mai sus, versiunea minimă Apache Ignite care suportă compresia paginilor disk: 2.8 și este susținută doar de sistemul de operare Linux. Activarea și configurarea se face astfel:

  • În class-path trebuie să fie modulele ignite-compression. Prin default se află în distribuția Apache Ignite în directorul libs/optional și nu este inclus în class-path. Poți pur și simplu să muți directorul cu un nivel mai sus în libs și astfel la rularea prin ignite.sh va fi inclus automat.
  • Persistența trebuie să fie activată (se activează prin DataRegionConfiguration.setPersistenceEnabled(true)).
  • Dimensiunea paginii trebuie să fie mai mare decât dimensiunea blocului sistemului de fișiere (poate fi specificată prin DataStorageConfiguration.setPageSize() ).
  • Pentru fiecare cache al cărui date trebuie comprimate, este necesar în configurație să se configureze metoda de compresie și (opțional) nivelul de compresie (metodele CacheConfiguration.setDiskPageCompression(), CacheConfiguration.setDiskPageCompressionLevel()).

Compresia WAL

Cum funcționează

Ce este WAL și de ce este necesar? Pe scurt: este un jurnal în care sunt înregistrate toate evenimentele care, în final, afectează stocarea paginilor. Acesta este, în primul rând, necesar pentru a permite recuperarea în caz de cădere. Orice operație, înainte de a predа controlul utilizatorului, trebuie să înregistreze mai întâi un eveniment în WAL, astfel încât, în cazul unei prăbușiri, să se poată reproduce din jurnal și să se recupereze toate operațiile pentru care utilizatorul a primit un răspuns de succes, chiar dacă aceste operații nu au reușit să se reflecte în stocarea paginilor pe disc (s-a menționat mai sus că înregistrarea efectivă în stocarea paginilor se face într-un proces numit „checkpoint” cu o întârziere prin fluxuri separate).

Înscrierile în WAL sunt împărțite în logice și fizice. Înscrierile logice sunt cheile și valorile în sine. Înscrierile fizice reflectă modificările paginilor în stocarea paginilor. Dacă înscrierile logice pot fi utile și în alte situații, înscrierile fizice sunt necesare doar pentru recuperare în caz de cădere și sunt necesare doar înscrierile de la ultimul checkpoint de succes. Aici nu ne vom adânci în detalii și nu vom explica de ce funcționează așa, dar cei interesați pot consulta articolul menționat anterior pe Apache Ignite Wiki: Ignite Persistent Store — sub capotă.

Adesea, o înscriere logică corespunde mai multor înscrieri fizice. De exemplu, o operație put într-un cache afectează mai multe pagini din memoria paginilor (pagina cu datele propriu-zise, paginile cu indecși, paginile cu listele libere). În anumite teste sintetice, am constatat că înscrierile fizice ocupau până la 90% din volumul fișierului WAL. Acestea sunt necesare pentru o perioadă foarte scurtă de timp (din default, intervalul între checkpoint-uri este de 3 minute). Ar fi logic să scăpăm de aceste date după ce își pierd relevanța. Exact aceasta este ceea ce face mecanismul de compactare WAL, eliminând înscrierile fizice și comprimând cu zip înscrierile logice rămase, dimensiunea fișierului fiind astfel redusă semnificativ (uneori de zeci de ori).

WAL fizic constă din mai multe segmente (în mod implicit 10) de dimensiuni fixe (în mod implicit 64Mb), care sunt rescrise în mod circular. Odată ce segmentul curent este complet, următorul segment îi este alocat, iar segmentul completat este copiat în arhivă printr-un flux separat. Compresia WAL funcționează deja cu segmentele arhivate. De asemenea, printr-un flux separat, acesta urmărește finalizarea punctului de control și începe compresia pe segmentele arhivate pentru care înregistrările fizice nu mai sunt necesare.

Compresia datelor în Apache Ignite. Experiența Sberbank

Influența asupra performanței

Deoarece compresia WAL funcționează printr-un flux separat, nu ar trebui să aibă un impact direct asupra operațiunilor în desfășurare. Totuși, aceasta generează o sarcină suplimentară pe CPU (compresie) și pe disc (citirea fiecărui segment WAL din arhivă și scrierea segmentelor comprimate), astfel încât, dacă sistemul funcționează la limita capacităților, va duce la o degradare a performanței.

Cum să activăm și să configurăm

Activarea compresiei WAL se poate face prin intermediul proprietății WalCompactionEnabled în DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). De asemenea, prin metoda DataStorageConfiguration.setWalCompactionLevel() se poate stabili nivelul de compresie, dacă valoarea implicită (BEST_SPEED) nu este satisfăcătoare.

Compresia instantaneelor paginilor WAL

Cum funcționează

Anterioară, am stabilit deja că în înregistrările WAL se împart în logice și fizice. La fiecare modificare a fiecărei pagini în memoria paginii se formează o înregistrare fizică WAL. Înregistrările fizice, la rândul lor, se împart în 2 subtipuri: înregistrarea instantanee a paginii și înregistrarea delta. De fiecare dată când schimbăm ceva pe pagină și o transformăm dintr-o stare curată într-una murdară, se salvează o copie completă a acestei pagini în WAL (înregistrarea instantanee a paginii — snapshot page). Chiar dacă am schimbat doar un byte, în WAL se va salva o înregistrare de aproximativ dimensiunea paginii. Dacă schimbăm ceva pe o pagină deja murdară, atunci în WAL se formează o înregistrare delta, care reflectă doar modificările față de starea anterioară a paginii, dar nu întreaga pagină. Deoarece resetarea stării paginilor de la murdar la curat se efectuează în timpul checkpoint-ului, imediat după începerea checkpoint-ului, practic toate înregistrările fizice vor consta doar din instantanee ale paginilor (deoarece toate paginile sunt curățate imediat după începerea checkpoint-ului), apoi, pe măsură ce ne apropiem de următorul checkpoint, proporția înregistrărilor delta începe să crească și din nou se resetează la începutul următorului checkpoint. Măsurătorile efectuate pe unele teste sintetic au arătat că proporția instantaneelor paginilor în volumul total al înregistrărilor fizice ajunge la 90%.

Ideea compresiei instantanee a paginii WAL constă în comprimarea instantaneelor paginilor folosind deja un instrument disponibil pentru comprimarea paginilor (vezi comprimarea paginilor de disc). În acest caz, înregistrările WAL sunt salvate secvențial în mod append-only și nu este necesară asocierea înregistrărilor cu limitele blocurilor sistemului de fișiere, astfel că, spre deosebire de mecanismul de comprimare a paginilor de disc, nu avem nevoie de fișiere sparse, astfel încât acest mecanism va funcționa nu doar pe sistemele de operare Linux. În plus, nu ne mai pasă cât de mult am reușit să comprinăm pagina. Chiar dacă am eliberat 1 byte, acesta este deja un rezultat pozitiv și putem salva în WAL datele comprimate, spre deosebire de comprimarea paginilor de disc, unde salvăm pagina comprimată doar dacă am eliberat mai mult de 1 bloc din sistemul de fișiere.

Pagini — date bine comprimate, ponderea lor în volumul total al WAL este foarte mare, astfel, păstrând formatul fișierului WAL, putem obține o reducere semnificativă a dimensiunii acestuia. Compresia, inclusiv a înregistrărilor logice, ar necesita modificarea formatului și pierderea compatibilității, de exemplu, pentru consumatorii externi care ar putea fi interesați de înregistrările logice, fără a aduce o reducere semnificativă a volumului fișierului.

La fel ca pentru compresia paginilor de disc, pentru compresia paginilor snapshot WAL pot fi utilizate algoritmi de compresie ZSTD, LZ4, Snappy, precum și modul SKIP_GARBAGE.

Influența asupra performanței

După cum se poate observa, activarea directă a compresiei paginilor snapshot WAL afectează doar firele care scriu date în memoria paginilor, adică acele fire care modifică datele în cache. Citirea din WAL a înregistrărilor fizice se face doar o singură dată, în momentul ridicării nodului după o cădere (și doar în cazul căderii în timpul checkpoint-ului).

Aceasta afectează firele care modifică datele în următorul mod: obținem un efect negativ (CPU) din cauza necesității de a compresa pagina înainte de a o scrie pe disc și un efect pozitiv (disk IO) din reducerea cantității de date scrise. Astfel, totul este simplu: dacă performanța sistemului este limitată de CPU, obținem o mică degradare; dacă este limitată de I/O-ul de disc, obținem un avantaj.

Indirect, reducerea dimensiunii WAL influențează de asemenea (pozitiv) firele care arhivează segmentele WAL și firele de compactare WAL.

Testele reale de performanță în mediu nostru cu date sintetice au arătat o mică creștere (prin 10%-15% a crescut throughput-ul, prin 10%-15% a scăzut latența).

Cum să activăm și să configurăm

Versiunea minimă de Apache Ignite: 2.8. Activarea și configurarea se efectuează în următorul mod:

  • În class-path trebuie să fie modulele ignite-compression. Prin default se află în distribuția Apache Ignite în directorul libs/optional și nu este inclus în class-path. Poți pur și simplu să muți directorul cu un nivel mai sus în libs și astfel la rularea prin ignite.sh va fi inclus automat.
  • Persistența trebuie să fie activată (se activează prin DataRegionConfiguration.setPersistenceEnabled(true)).
  • Trebuie să fie stabilit modul de compresie folosind metoda DataStorageConfiguration.setWalPageCompression(), în mod implicit compresia este dezactivată (modul DISABLED).
  • Opțional, se poate stabili gradul de compresie folosind metoda DataStorageConfiguration.setWalPageCompression(), valorile permise pentru fiecare dintre moduri pot fi găsite în javadoc pentru metodă.

Concluzie

Mecanismele de compresie a datelor discutate în Apache Ignite pot fi utilizate independent unul de celălalt, dar sunt acceptate și orice combinații ale acestora. Înțelegerea principiilor lor de funcționare va permite să determinați cât de bine se potrivesc nevoilor dumneavoastră în mediul în care lucrați și ce va trebui să sacrificați pentru a le utiliza. Compresia paginii de disc este destinată comprimării stocării principale și poate oferi un grad mediu de compresie. Compresia instantaneelor paginii WAL va oferi un grad mediu de compresie pentru fișierele WAL, cel mai probabil chiar va îmbunătăți performanța. Compacția WAL nu va afecta pozitiv performanța, dar va reduce la minimum dimensiunea fișierelor WAL prin eliminarea înregistrărilor fizice.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster