NÄr man arbeider med store datamengder, kan det ofte oppstÄ et akutt problem med mangel pÄ lagringsplass pÄ diskene. En av lÞsningene pÄ dette problemet er komprimering, som gjÞr det mulig Ä Þke lagringskapasiteten med samme utstyr. I denne artikkelen vil vi se pÄ hvordan datakomprimering fungerer i Apache Ignite. Artikkelen vil bare beskrive de komprimeringsmetodene som er implementert i produktet. Andre metoder for datakomprimering (over nettverket, i minnet) vil, bÄde implementerte og ikke implementerte, falle utenfor rammen av denne artikkelen.
SÄ nÄr persistence-modus er aktivert, begynner Ignite Ä skrive til disk som et resultat av at data i cache-ene endres:
- Innholdet i cache-ene
- Journal for forhÄndsskrivning (Write Ahead Log, heretter bare WAL)
Det eksisterer allerede en mekanisme for komprimering av WAL, kalt WAL-komprimering. I den nylig utgitte Apache Ignite 2.8, ble det ogsÄ introdusert to mekanismer som gjÞr det mulig Ä komprimere data pÄ disk, nemlig disk sidekomprimering for Ä komprimere innholdet i cache-ene og WAL side snapshot-komprimering for Ä komprimere enkelte oppfÞringer i WAL. Mer informasjon om disse tre mekanismene fÞlger nedenfor.
Disk sidekomprimering
Hvordan det fungerer
La oss kort se pÄ hvordan Ignite lagrer data. For lagring brukes sidet minne. StÞrrelsen pÄ sidene angis ved oppstart av noden og kan ikke endres senere. StÞrrelsen pÄ en side mÄ ogsÄ vÊre en potens av to og delelig med blokkstÞrrelsen til filsystemet. Sider lastes inn i RAM fra disk etter behov, og mengden data pÄ disk kan overstige den tildelte RAM. Hvis det ikke er nok plass i RAM til Ä laste en side fra disk, vil gamle, allerede ubrukte sider bli utskiftet fra RAM.
PÄ disken lagres dataene pÄ fÞlgende mÄte: for hver partisjon av hver cache-gruppe opprettes en separat fil, i denne filen kommer sidene i stigende rekkefÞlge etter indeks. Den fullstendige side-ID-en inneholder ID-en til cache-gruppen, nummeret pÄ partisjonen og indeksen til siden i filen. Dermed kan vi med den fulle side-ID-en entydig bestemme filen og offset i filen for hver side. For mer detaljert informasjon om strukturen til sidet minne, kan du lese artikkelen pÄ Apache Ignite Wiki: .
Disk page compression-mekanismen fungerer, som navnet antyder, pÄ sidenivÄ. NÄr denne mekanismen er aktivert, hÄndteres data i RAM som de er, uten noen form for komprimering. Men i det Þyeblikket sidene lagres fra RAM til disk utfÞres det en komprimering.
Men Ä komprimere hver side individuelt er ikke lÞsningen pÄ problemet; vi mÄ pÄ en eller annen mÄte redusere stÞrrelsen pÄ de endelige datafilene. Hvis stÞrrelsen pÄ en side slutter Ä vÊre fast, kan vi ikke lenger skrive sidene i filen én etter én, da dette kan fÞre til en rekke problemer.
- Vi vil ikke kunne beregne offset for siden ved hjelp av sideindeksen, for Ä fastslÄ hvor den er plassert i filen.
- Det er uklart hvordan vi skal hÄndtere sider som ikke er pÄ slutten av filen og endrer stÞrrelse. Hvis stÞrrelsen pÄ en side reduseres, gÄr plassen den frigjÞr tapt. Hvis stÞrrelsen pÄ siden Þker, mÄ det sÞkes etter et nytt sted i filen for den.
- Hvis siden flyttes med et antall byte som ikke er et multiplum av blokkens stÞrrelse i filsystemet, vil lesing eller skriving kreve at ett ekstra filsystemblokk ogsÄ berÞres, noe som kan fÞre til ytelsesforringelse.
For ikke Ä hÄndtere disse problemene selv, bruker disk page compression i Apache Ignite et filsystemmekanisme kalt sparse filer. En sparse (sparsomt) fil er en fil hvor enkelte regioner fylt med nuller kan merkes som "hull". Med dette vil det ikke bli tildelt filsystemblokker for Ä lagre disse hullene, noe som resulterer i plassbesparelser pÄ disken.
Det er logisk at for Ä frigjÞre en filsystemblokk, mÄ stÞrrelsen pÄ hullet vÊre stÞrre enn eller lik filsystemblokkens stÞrrelse, noe som legger et ekstra begrensning pÄ stÞrrelsen pÄ siden i Apache Ignite: for at komprimeringen skal gi noen effekt, mÄ stÞrrelsen pÄ siden vÊre strengt stÞrre enn stÞrrelsen pÄ filsystemblokken. Hvis stÞrrelsen pÄ siden er lik stÞrrelsen pÄ blokken, vil vi aldri kunne frigjÞre en eneste blokk, da for Ä frigjÞre en enkelt blokk mÄ den komprimerte siden ta opp 0 byte. Hvis stÞrrelsen pÄ siden derimot er lik stÞrrelsen pÄ 2 eller 4 blokker, kan vi likevel frigjÞre minst én blokk hvis siden vÄr komprimeres med minst 50 % eller 75 % henholdsvis.
Dermed er den endelige beskrivelsen av hvordan mekanismen fungerer: NÄr en side skrives til disken, forsÞkes det Ä komprimere siden. Hvis stÞrrelsen pÄ den komprimerte siden gjÞr det mulig Ä frigjÞre en eller flere blokker av filsystemet, skrives siden i komprimert form, mens en «luke» opprettes pÄ stedet for de frigjorte blokkene (systemanrop blir utfÞrt med flagget «punch hole»). fallocate() Hvis stÞrrelsen pÄ den komprimerte siden ikke tillater blokkene Ä bli frigitt, lagres siden som den er, uforandret. Alle offsets for sidene beregnes pÄ samme mÄte som uten komprimering, ved Ä multiplisere sideindeksen med sidestÞrrelsen. Ingen relokasjon av sidene er nÞdvendig. Offsets for sidene, akkurat som uten komprimering, vil falle pÄ grensene til filsystemblokkene.

I den nÄvÊrende implementeringen kan Ignite jobbe med sparse-filer kun under Linux, og dermed kan disk page compression kun aktiveres nÄr Ignite brukes pÄ dette operativsystemet.
Komprimeringsalgoritmene som kan brukes for disk page compression: ZSTD, LZ4, Snappy. I tillegg finnes det en driftmodus (SKIP_GARBAGE), hvor bare det ubrukte plassen i siden kastes uten at komprimering pÄ de gjenvÊrende dataene brukes, noe som reduserer belastningen pÄ CPU sammenlignet med de tidligere nevnte algoritmene.
Innvirkning pÄ ytelse
Dessverre har jeg ikke utfÞrt faktiske ytelsestester pÄ virkelige oppsett, siden vi ikke planlegger Ä bruke denne mekanismen i produksjon, men vi kan teoretisk diskutere hvor vi vil tape og hvor vi vil vinne.
For dette mÄ vi huske hvordan lesing og skriving av sider utfÞres nÄr vi aksesserer dem:
- Ved utfÞrelse av en leseoperasjon sÞkes det fÞrst i RAM; hvis sÞket mislykkes, lastes siden inn i RAM fra disken av den samme trÄden som utfÞrer lesingen.
- Ved utfÞring av en skriveoperasjon markeres siden i RAM som skitten, men den fysiske lagringen av siden pÄ disken skjer ikke umiddelbart i trÄden som utfÞrer skrivingen. Alle skitne sider lagres pÄ disken senere i prosessen med checkpoint av separate trÄder.
Dermed er innvirkningen pÄ leseoperasjoner:
- Positiv (disk IO), takket vĂŠre reduksjonen i antall leste blokker av filsystemet.
- Negativ (CPU), fordi det er nÞdvendig med ekstra belastning for operativsystemet til Ä hÄndtere sparse filer. Det kan ogsÄ henvises til at det kan dukke opp ekstra IO-operasjoner for Ä lagre en mer kompleks struktur av sparse filer (jeg er dessverre ikke kjent med alle detaljene i hvordan sparse filer fungerer).
- Negativ (CPU), pÄ grunn av behovet for dekomprimering av sider.
- Ingen pÄvirkning pÄ skriveoperasjoner.
- PÄvirkning pÄ sjekkpunktprosessen (her er det likt som for leseoperasjoner):
- Positiv (disk IO), fordi antallet skrevne blokker i filsystemet reduseres.
- Negativ (CPU, muligens disk IO), pÄ grunn av arbeidet med sparse filer.
- Negativ (CPU), pÄ grunn av behovet for komprimering av sider.
Hvilken side veier tyngst? Dette avhenger veldig av miljÞet, men jeg tendere til Ä tro at disk page compression mest sannsynlig vil fÞre til ytelsesforringelse i de fleste systemer. Dessuten viser tester pÄ andre DBMS-er som bruker en lignende tilnÊrming med sparse filer fall i ytelse nÄr komprimering er aktivert.
Hvordan aktivere og konfigurere
Som nevnt tidligere, den minste versjonen av Apache Ignite som stĂžtter disk page compression: 2.8, og det stĂžttes kun av operativsystemet Linux. Aktivering og konfigurasjon gjĂžres som fĂžlger:
- Modulen ignite-compression mÄ vÊre i class-path. Som standard ligger den i distribusjonen av Apache Ignite i katalogen libs/optional og er ikke inkludert i class-path. Du kan ganske enkelt flytte katalogen opp ett nivÄ i libs, og da vil den bli automatisk inkludert nÄr du starter med ignite.sh.
- Persistens mÄ vÊre aktivert (Aktiveres gjennom
DataRegionConfiguration.setPersistenceEnabled(true)). - SidestÞrrelsen mÄ vÊre stÞrre enn blokkstÞrrelsen i filsystemet (kan settes ved hjelp av
DataStorageConfiguration.setPageSize()). - For hver cache hvor dataene mÄ komprimeres, mÄ du konfigurere komprimeringsmetoden og (valgfritt) komprimeringsnivÄet i konfigurasjonen (metoder
CacheConfiguration.setDiskPageCompression(), CacheConfiguration.setDiskPageCompressionLevel()).
WAL komprimering
Hvordan det fungerer
Hva er WAL og hvorfor er det viktig? Kort sagt: det er en journal som fanger alle hendelser som til slutt endrer sidelageret. Den er fÞrst og fremst nÞdvendig for Ä kunne gjenopprette etter et systemkrasj. Hver operasjon mÄ fÞrst registrere en hendelse i WAL fÞr den gir kontroll tilbake til brukeren, slik at man i tilfelle krasj kan spille tilbake journalen og gjenskape alle operasjoner som ga brukeren et vellykket svar, selv om disse operasjonene ikke ble lagret pÄ disk i sidelageret (det ble tidligere nevnt at den faktiske lagringen i sidelageret skjer i en prosess kalt «checkpoint», med noe etterslep fra separate trÄder).
OppfÞringene i WAL deles inn i logiske og fysiske. Logiske er selve nÞklene og verdiene. Fysiske reflekterer endringer i sidene i sidelageret. Mens logiske oppfÞringer kan vÊre nyttige i andre sammenhenger, er fysiske oppfÞringer bare nÞdvendige for Ä gjenopprette etter et krasj, og det er kun oppfÞringer fra det siste vellykkede checkpointet som er relevante. Her vil vi ikke gÄ i detaljer om hvorfor dette fungerer som det gjÞr, men de som er interesserte kan referere til den allerede nevnte artikkelen pÄ Apache Ignite Wiki: .
Det er ofte flere fysiske oppfÞringer for én logisk oppfÞring. For eksempel kan én put-operasjon i cachen pÄvirke flere sider i sideminnet (siden med selve dataene, sider med indekser, sider med free-list). I noen syntetiske tester fant jeg at fysiske oppfÞringer kunne utgjÞre opptil 90% av volumet pÄ WAL-filen. Samtidig er de nÞdvendige i svÊrt kort tid (som standard er intervallet mellom checkpointene 3 minutter). Det ville vÊrt logisk Ä fjerne disse dataene nÄr de mister sin aktualitet. Dette utfÞres nettopp av mekanismen for WAL-komprimering, som kvitter seg med fysiske oppfÞringer og komprimerer de gjenvÊrende logiske oppfÞringene med zip, hvilket resulterer i en betydelig reduksjon i filstÞrrelse (noen ganger opp til titalls ganger).
Fysisk WAL bestÄr av flere segmenter (som standard 10) med fast stÞrrelse (som standard 64 MB), som overfÞres pÄ en sirkulÊr mÄte. SÄ snart det nÄvÊrende segmentet er fylt opp, tildeles det neste segmentet, og det fylte segmentet kopieres til arkiv i en egen trÄd. WAL-komprimering fungerer allerede med arkivsegmentene. I tillegg fÞlger den med en egen trÄd opp gjennomfÞringen av sjekkpunktet og begynner komprimering av arkivsegmentene, hvor de fysiske registreringene ikke lenger er nÞdvendige.

Innvirkning pÄ ytelse
Siden WAL-komprimering kjÞrer i en egen trÄd, skal det ikke vÊre noen direkte innvirkning pÄ de utfÞrende operasjonene. Men den gir likevel en ekstra bakgrunnsbelastning pÄ CPU (komprimering) og disk (lesing av hvert WAL-segment fra arkivet og skriving av komprimerte segmenter), sÄ hvis systemet kjÞrer pÄ grensen av kapasitet, vil det ogsÄ fÞre til ytelsesdegradering.
Hvordan aktivere og konfigurere
WAL-komprimering kan aktiveres ved hjelp av egenskapen WalCompactionEnabled i DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). I tillegg kan graden av komprimering spesifiseres med metoden DataStorageConfiguration.setWalCompactionLevel() hvis standardverdien (BEST_SPEED) ikke er tilfredsstillende.
WAL-side snapshotkomprimering
Hvordan det fungerer
Vi har allerede avklart at WAL-poster deles inn i logiske og fysiske. For hver endring pĂ„ hver side i sidens minne dannes det en fysisk WAL-post. Fysiske poster deles igjen inn i to underkategorier: page snapshot record og delta record. Hver gang vi gjĂžr en endring pĂ„ en side og oversetter den fra en ren tilstand til en skitten tilstand, lagres en full kopi av denne siden i WAL (page snapshot record â en snapshot av siden). Selv om vi bare endrer ett byte, vil WAL lagre en post som er litt stĂžrre enn stĂžrrelsen pĂ„ siden. Dersom vi endrer noe pĂ„ en allerede skitten side, dannes det en delta record i WAL, som kun reflekterer endringene sammenlignet med den tidligere tilstanden til siden, men ikke hele siden. Siden tilbakestilling av sidens tilstand fra skitten til ren skjer i lĂžpet av et checkpoint, vil nesten alle fysiske poster rett etter starten pĂ„ checkpointet bestĂ„ av snapshots av sidene (ettersom alle sidene rett etter starten av checkpointet er rene). Etter hvert som vi nĂŠrmer oss neste checkpoint, begynner andelen delta records Ă„ Ăžke, fĂžr den igjen tilbakestilles ved starten av neste checkpoint. MĂ„linger fra noen syntetiske tester har vist at andelen snapshots av sidene i den totale mengden av fysiske poster kan nĂ„ 90%.
Ideen med WAL page snapshot kompresjon er Ä komprimere snapshots av sidene ved Ä bruke et allerede eksisterende verktÞy for kompresjon av sider (se disk page compression). Her lagres WAL-postene sekvensielt i en append-only-modus, og det er ikke nÞdvendig Ä knytte postene til grensene pÄ filsystemets blokker. Derfor, i motsetning til mekanismen for disk page compression, trenger vi absolutt ikke sparse-filer, noe som betyr at denne mekanismen vil fungere ikke bare pÄ Linux OS. I tillegg er det ikke lenger viktig hvor mye vi klarte Ä komprimere siden. Selv om vi frigjorde 1 byte, er det allerede et positivt resultat, og vi kan lagre de komprimerte dataene i WAL, i motsetning til disk page compression, hvor vi lagrer den komprimerte siden bare hvis vi har frigjort mer enn 1 blokk i filsystemet.
Sider er godt komprimerbare data, og deres andel av den totale WAL-stÞrrelsen er veldig hÞy. Dermed kan vi oppnÄ betydelig reduksjon i stÞrrelsen pÄ filen uten Ä endre formatet for WAL-filen. Komprimering av logiske oppfÞringer ville derimot krevd endring av formatet og tapt kompatibilitet, for eksempel for eksterne brukere som kan vÊre interessert i logiske oppfÞringer, uten Ä gi en betydelig reduksjon i filens volum.
Som med disk sidekomprimering kan algoritmer som ZSTD, LZ4, Snappy, samt SKIP_GARBAGE-modus brukes for WAL-sidebildekomprimering.
Innvirkning pÄ ytelse
Det er ikke vanskelig Ä legge merke til at direkte aktivering av WAL-sidebildekomprimering kun pÄvirker de trÄdene som skriver data til sideminne, det vil si de trÄdene som endrer dataene i cache. Lese fra WAL-fysiske oppfÞringer skjer bare én gang, i Þyeblikket nÄr noden starter etter ett fall (og bare i tilfelle av fall under sjekkpunktet).
Dette pÄvirker trÄdene som endrer data pÄ fÞlgende mÄte: vi fÄr en negativ effekt (CPU) pÄ grunn av nÞdvendigheten av Ä komprimere siden fÞr skriving til disk, og en positiv effekt (disk IO) pÄ grunn av reduksjonen av dataene som skrives. Dermed er det enkelt: hvis systemytelsen er begrenset av CPU, fÄr vi en liten nedgang; hvis det er diskinn-/utgang, fÄr vi en Þkning.
Indirekte reduserer stÞrrelsen pÄ WAL ogsÄ (positivt) pÄvirkning pÄ trÄdene som arkiverer WAL-segmenter og pÄ trÄdene som reduserer WAL.
Reelle ytelsestester i vÄrt miljÞ med syntetiske data viste en liten Þkning (gjennomstrÞmning Þkte med 10%-15%, latenstid reduserte med 10%-15%).
Hvordan aktivere og konfigurere
Minimum versjon av Apache Ignite: 2.8. Aktivering og konfigurasjon skjer som fĂžlger:
- Modulen ignite-compression mÄ vÊre i class-path. Som standard ligger den i distribusjonen av Apache Ignite i katalogen libs/optional og er ikke inkludert i class-path. Du kan ganske enkelt flytte katalogen opp ett nivÄ i libs, og da vil den bli automatisk inkludert nÄr du starter med ignite.sh.
- Persistens mÄ vÊre aktivert (Aktiveres gjennom
DataRegionConfiguration.setPersistenceEnabled(true)). - Komprimeringsmodusen mÄ angis ved hjelp av metoden
DataStorageConfiguration.setWalPageCompression(), som standard er komprimering deaktivert (modus DISABLED). - Valgfritt kan graden av komprimering angis med metoden
DataStorageConfiguration.setWalPageCompression(), se javadoc for metoden for tillatte verdier for hver av modusene.
Konklusjon
De mekanismene for datakomprimering i Apache Ignite kan brukes uavhengig av hverandre, men ogsÄ enhver kombinasjon er tillatt. ForstÄelsen av prinsippene for deres arbeid vil gjÞre det mulig Ä avgjÞre hvor godt de passer til dine oppgaver i ditt miljÞ og hva du mÄ ofre for Ä bruke dem. Disksidekomprimering er designet for Ä komprimere det primÊre lageret og kan gi en gjennomsnittlig komprimeringsgrad. WAL-sidefotosnap-komprimering vil gi en gjennomsnittlig komprimeringsgrad av WAL-filer, og vil mest sannsynlig til og med forbedre ytelsen. WAL-komprimering vil ikke ha en positiv innvirkning pÄ ytelsen, men vil maksimalt redusere stÞrrelsen pÄ WAL-filer ved Ä fjerne fysiske oppfÞringer.
Kilde: habr.com
