Datakomprimering i Apache Ignite. Sbers erfarenhet

Datakomprimering i Apache Ignite. Sbers erfarenhetNär man arbetar med stora datamängder kan problemet med brist på diskutrymme ibland uppstå. Ett sätt att lösa detta problem är komprimering, tack vare vilken du på samma utrustning har råd att öka lagringsvolymerna. I den här artikeln kommer vi att titta på hur datakomprimering fungerar i Apache Ignite. Den här artikeln kommer endast att beskriva diskkomprimeringsmetoderna som implementeras i produkten. Andra metoder för datakomprimering (över nätverket, i minnet), oavsett om de är implementerade eller inte, kommer att förbli utanför räckvidden.

Så, med beständighetsläget aktiverat, som ett resultat av förändringar i data i cachen, börjar Ignite skriva till disken:

  1. Innehåll i cacher
  2. Write Ahead Log (nedan bara WAL)

Det har funnits en mekanism för WAL-komprimering ganska länge nu, kallad WAL-komprimering. Den nyligen släppta Apache Ignite 2.8 introducerade ytterligare två mekanismer som gör att du kan komprimera data på disk: disksidaskomprimering för att komprimera innehållet i cacheminnet och WAL-sidans ögonblicksbildskomprimering för att komprimera vissa WAL-poster. Mer information om alla dessa tre mekanismer nedan.

Komprimering av disksida

Hur fungerar den här

Låt oss först ta en mycket kort titt på hur Ignite lagrar data. Sidminne används för lagring. Sidstorleken ställs in i början av noden och kan inte ändras i senare skeden; sidstorleken måste också vara en potens av två och en multipel av filsystemets blockstorlek. Sidor laddas in i RAM från disk efter behov; storleken på data på disken kan överstiga mängden tilldelat RAM. Om det inte finns tillräckligt med utrymme i RAM-minnet för att ladda en sida från disken kommer gamla, ej längre använda sidor att vräkas från RAM-minnet.

Data lagras på disk i följande form: en separat fil skapas för varje partition i varje cachegrupp, i denna fil visas sidorna en efter en i stigande indexordning. Helsidesidentifieraren innehåller cachegruppens identifierare, partitionsnummer och sidindex i filen. Med hjälp av helsidesidentifieraren kan vi således unikt bestämma filen och förskjutningen i filen för varje sida. Du kan läsa mer om sökningsminne i Apache Ignite Wiki-artikeln: Ignite Persistent Store - under huven.

Mekanismen för komprimering av skivsidor, som du kanske kan gissa från namnet, fungerar på sidnivå. När denna mekanism är aktiverad bearbetas data i RAM-minnet som det är, utan någon komprimering, men när sidor sparas från RAM-minne till disk komprimeras de.

Men att komprimera varje sida individuellt är inte en lösning på problemet, du måste på något sätt minska storleken på de resulterande datafilerna. Om sidstorleken inte längre är fixerad kan vi inte längre skriva sidor till filen en efter en, eftersom detta kan skapa ett antal problem:

  • Med hjälp av sidindexet kommer vi inte att kunna beräkna den offset med vilken den finns i filen.
  • Det är inte klart vad man ska göra med sidor som inte finns i slutet av filen och ändra storlek. Om sidstorleken minskar försvinner utrymmet det frigjorde. Om sidstorleken ökar måste du leta efter en ny plats i filen för det.
  • Om en sida flyttas med ett antal byte som inte är en multipel av filsystemets blockstorlek, kräver läsning eller skrivning av den att du trycker på ytterligare ett filsystemblock, vilket kan leda till prestandaförsämring.

För att undvika att lösa dessa problem på sin egen nivå använder komprimering av skivsidor i Apache Ignite en filsystemmekanism som kallas sparse filer. En gles fil är en där vissa nollfyllda områden kan markeras som "hål". I det här fallet kommer inga filsystemblock att tilldelas för att lagra dessa hål, vilket resulterar i besparingar på diskutrymme.

Det är logiskt att för att frigöra ett filsystemblock måste storleken på hålet vara större än eller lika med filsystemblocket, vilket medför en ytterligare begränsning för sidstorleken och Apache Ignite: för att komprimering ska ha någon effekt, sidstorleken måste vara strikt större än storleken på filsystemblocket. Om sidstorleken är lika med blockstorleken kommer vi aldrig att kunna frigöra ett enda block, eftersom för att frigöra ett enda block måste den komprimerade sidan uppta 0 byte. Om sidstorleken är lika med storleken på 2 eller 4 block kommer vi redan att kunna frigöra minst ett block om vår sida är komprimerad till minst 50% respektive 75%.

Alltså den slutliga beskrivningen av hur mekanismen fungerar: När man skriver en sida till disk görs ett försök att komprimera sidan. Om storleken på den komprimerade sidan tillåter att ett eller flera filsystemblock frigörs, skrivs sidan i komprimerad form och ett "hål" görs i stället för de frigjorda blocken (ett systemanrop exekveras fallocate() med hålflaggan). Om storleken på den komprimerade sidan inte tillåter att blocken frigörs, sparas sidan som den är, okomprimerad. Alla sidförskjutningar beräknas på samma sätt som utan komprimering, genom att multiplicera sidindexet med sidstorleken. Ingen förflyttning av sidor krävs på egen hand. Sidförskjutningar, precis som utan komprimering, faller på gränserna för filsystemblock.

Datakomprimering i Apache Ignite. Sbers erfarenhet

I den nuvarande implementeringen kan Ignite bara fungera med glesa filer under Linux OS; följaktligen kan komprimering av skivsidor endast aktiveras när Ignite används på det här operativsystemet.

Komprimeringsalgoritmer som kan användas för komprimering av skivsidor: ZSTD, LZ4, Snappy. Dessutom finns det ett driftläge (SKIP_GARBAGE), där endast oanvänt utrymme på sidan slängs ut utan att komprimera resterande data, vilket minskar belastningen på CPU:n jämfört med de tidigare listade algoritmerna.

Prestandapåverkan

Tyvärr gjorde jag inga faktiska prestationsmätningar på riktiga läktare, eftersom vi inte planerar att använda denna mekanism i produktionen, men vi kan teoretiskt spekulera var vi kommer att förlora och var vi kommer att vinna.

För att göra detta måste vi komma ihåg hur sidor läses och skrivs när de öppnas:

  • När en läsoperation utförs, söks den först i RAM; om sökningen misslyckas laddas sidan in i RAM från disken av samma tråd som utför läsningen.
  • När en skrivoperation utförs markeras sidan i RAM-minnet som smutsig, men sidan sparas inte fysiskt på disken omedelbart av tråden som utför skrivningen. Alla smutsiga sidor sparas på disk senare i checkpoint-processen i separata trådar.

Så effekten på läsoperationerna är:

  • Positiv (disk IO), på grund av en minskning av antalet läsfilsystemblock.
  • Negativ (CPU), på grund av den extra belastning som krävs av operativsystemet för att arbeta med glesa filer. Det är också möjligt att ytterligare IO-operationer implicit kommer att dyka upp här för att spara en mer komplex gles filstruktur (tyvärr är jag inte bekant med alla detaljer om hur glesa filer fungerar).
  • Negativ (CPU), på grund av behovet av att dekomprimera sidor.
  • Det har ingen inverkan på skrivoperationer.
  • Inverkan på checkpointprocessen (allt här liknar läsoperationer):
  • Positiv (disk IO), på grund av en minskning av antalet skrivna filsystemblock.
  • Negativ (CPU, möjligen disk IO), på grund av att arbeta med glesa filer.
  • Negativ (CPU), på grund av behovet av sidkomprimering.

Vilken sida av skalan kommer att tippa skalan? Allt detta beror väldigt mycket på miljön, men jag är benägen att tro att komprimering av skivsidor med största sannolikhet kommer att leda till prestandaförsämring på de flesta system. Dessutom visar tester på andra DBMS:er som använder ett liknande tillvägagångssätt med glesa filer en nedgång i prestanda när komprimering är aktiverad.

Hur man aktiverar och konfigurerar

Som nämnts ovan är den lägsta versionen av Apache Ignite som stöder komprimering av skivsidor 2.8 och endast operativsystemet Linux stöds. Aktivera och konfigurera enligt följande:

  • Det måste finnas en ignite-compression-modul i klassvägen. Som standard finns den i Apache Ignite-distributionen i libs/optional-katalogen och ingår inte i klasssökvägen. Du kan helt enkelt flytta katalogen upp en nivå till libs och sedan när du kör den genom ignite.sh aktiveras den automatiskt.
  • Persistens måste vara aktiverat (Aktiverad via DataRegionConfiguration.setPersistenceEnabled(true)).
  • Sidstorleken måste vara större än filsystemets blockstorlek (du kan ställa in den med DataStorageConfiguration.setPageSize() ).
  • För varje cache vars data behöver komprimeras måste du konfigurera komprimeringsmetoden och (valfritt) komprimeringsnivån (metoder CacheConfiguration.setDiskPageCompression() , CacheConfiguration.setDiskPageCompressionLevel()).

WAL-komprimering

Hur fungerar den här

Vad är WAL och varför behövs det? Mycket kortfattat: detta är en logg som innehåller alla händelser som i slutändan ändrar sidlagringen. Det behövs i första hand för att kunna återhämta sig vid ett fall. Alla operationer, innan de ger kontroll till användaren, måste först spela in en händelse i WAL, så att den i händelse av fel kan spelas upp i loggen och återställa alla operationer för vilka användaren fick ett framgångsrikt svar, även om dessa operationer hann inte reflekteras i sidlagringen på disk (redan ovan Det har beskrivits att själva skrivningen till sidlagret sker i en process som kallas "checkpointing" med viss fördröjning av separata trådar).

Poster i WAL är uppdelade i logiska och fysiska. Booleska är själva nycklarna och värderingarna. Fysisk – återspeglar ändringar av sidor i sidbutiken. Även om logiska poster kan vara användbara för vissa andra fall, behövs fysiska poster endast för återställning i händelse av en krasch och poster behövs endast sedan den senaste lyckade kontrollpunkten. Här ska vi inte gå in i detalj och förklara varför det fungerar på det här sättet, men intresserade kan hänvisa till den redan nämnda artikeln på Apache Ignite Wiki: Ignite Persistent Store - under huven.

Det finns ofta flera fysiska poster per logisk post. Det vill säga att till exempel en insättningsoperation i cachen påverkar flera sidor i sidminnet (en sida med själva data, sidor med index, sidor med fria listor). I vissa syntetiska tester fann jag att fysiska poster upptog upp till 90 % av WAL-filen. De behövs dock under en mycket kort tid (som standard är intervallet mellan kontrollpunkter 3 minuter). Det skulle vara logiskt att bli av med denna data efter att ha förlorat sin relevans. Detta är precis vad WAL-komprimeringsmekanismen gör: den gör sig av med fysiska poster och komprimerar de återstående logiska posterna med hjälp av zip, samtidigt som filstorleken minskas mycket avsevärt (ibland tiotals gånger).

Rent fysiskt består WAL av flera segment (10 som standard) med en fast storlek (64MB som standard), som skrivs över på ett cirkulärt sätt. Så snart det aktuella segmentet är ifyllt tilldelas nästa segment som aktuellt och det ifyllda segmentet kopieras till arkivet med en separat tråd. WAL-komprimering fungerar redan med arkivsegment. Dessutom, som en separat tråd, övervakar den exekveringen av kontrollpunkten och börjar komprimering i arkivsegment för vilka fysiska poster inte längre behövs.

Datakomprimering i Apache Ignite. Sbers erfarenhet

Prestandapåverkan

Eftersom WAL-komprimering går som en separat tråd, bör det inte vara någon direkt påverkan på de operationer som utförs. Men det lägger fortfarande ytterligare bakgrundsbelastning på CPU (komprimering) och disk (läser varje WAL-segment från arkivet och skriver de komprimerade segmenten), så om systemet körs på sin maximala kapacitet kommer det också att leda till prestandaförsämring.

Hur man aktiverar och konfigurerar

Du kan aktivera WAL-komprimering med hjälp av egenskapen WalCompactionEnabled в DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). Med hjälp av metoden DataStorageConfiguration.setWalCompactionLevel() kan du också ställa in komprimeringsnivån om du inte är nöjd med standardvärdet (BEST_SPEED).

Komprimering av ögonblicksbild av WAL-sidan

Hur fungerar den här

Vi har redan fått reda på att i WAL är poster uppdelade i logiska och fysiska. För varje ändring av varje sida genereras en fysisk WAL-post i sidminnet. Fysiska poster är i sin tur också uppdelade i två undertyper: sidsnapshot-post och delta-post. Varje gång vi ändrar något på en sida och överför det från ett rent tillstånd till ett smutsigt tillstånd, lagras en fullständig kopia av denna sida i WAL (page snapshot record). Även om vi bara ändrade en byte i WAL kommer posten att vara något större än sidstorleken. Om vi ​​ändrar något på en redan smutsig sida bildas en deltapost i WAL, som endast återspeglar ändringar jämfört med sidans tidigare tillstånd, men inte hela sidan. Eftersom återställning av sidornas tillstånd från smutsiga till rena utförs under kontrollpunktsprocessen, omedelbart efter starten av kontrollpunkten, kommer nästan alla fysiska poster endast att bestå av ögonblicksbilder av sidor (eftersom alla sidor omedelbart efter kontrollpunktens start är rena) , när vi närmar oss nästa kontrollpunkt börjar deltapostfraktionen växa och återställas igen i början av nästa kontrollpunkt. Mätningar i några syntetiska tester visade att andelen sidögonblicksbilder av den totala volymen fysiska poster når 2 %.

Idén med WAL-sid-snapshot-komprimering är att komprimera sid-ögonblicksbilder med hjälp av ett färdigt sidkomprimeringsverktyg (se disk-sida-komprimering). Samtidigt, i WAL, sparas poster sekventiellt i append-only-läge och det finns inget behov av att binda poster till gränserna för filsystemblock, så här, till skillnad från disksidans komprimeringsmekanism, behöver vi inte glesa filer på allt; följaktligen kommer denna mekanism inte bara att fungera på OS Linux. Dessutom spelar det inte längre någon roll för oss hur mycket vi kunde komprimera sidan. Även om vi frigjorde 1 byte, är detta redan ett positivt resultat och vi kan spara komprimerad data i WAL, till skillnad från disksideskomprimering, där vi sparar den komprimerade sidan endast om vi frigjorde mer än 1 filsystemblock.

Sidor är mycket komprimerbara data, deras andel av den totala WAL-volymen är mycket hög, så utan att ändra WAL-filformatet kan vi få en betydande minskning av dess storlek. Komprimering, inklusive logiska poster, skulle kräva en förändring av formatet och förlust av kompatibilitet, till exempel för externa konsumenter som kan vara intresserade av logiska poster, men skulle inte leda till en betydande minskning av filstorleken.

Liksom med komprimering av skivsidor kan komprimering av ögonblicksbilder av WAL-sidor använda ZSTD, LZ4, Snappy-komprimeringsalgoritmer, såväl som SKIP_GARBAGE-läget.

Prestandapåverkan

Det är inte svårt att lägga märke till att direkt aktivering av komprimering av WAL-sidans ögonblicksbild endast påverkar trådar som skriver data till sidminnet, det vill säga de trådar som ändrar data i cacheminnet. Läsning av fysiska poster från WAL sker endast en gång, i det ögonblick som noden höjs efter ett fall (och endast om den faller under en kontrollpunkt).

Detta påverkar trådar som ändrar data på följande sätt: vi får en negativ effekt (CPU) på grund av behovet av att komprimera sidan varje gång innan du skriver till disk, och en positiv effekt (disk IO) på grund av en minskning av mängden av skrivna data. Följaktligen är allt enkelt här: om systemprestandan begränsas av CPU:n får vi en liten försämring, om den begränsas av disk I/O får vi en ökning.

Indirekt påverkar en minskning av WAL-storleken också (positivt) strömmar som dumpar WAL-segment i arkivet och WAL-komprimeringsströmmar.

Verkliga prestandatester i vår miljö med hjälp av syntetiska data visade en liten ökning (genomströmningen ökade med 10%-15%, latensen minskade med 10%-15%).

Hur man aktiverar och konfigurerar

Minsta Apache Ignite-version: 2.8. Aktivera och konfigurera enligt följande:

  • Det måste finnas en ignite-compression-modul i klassvägen. Som standard finns den i Apache Ignite-distributionen i libs/optional-katalogen och ingår inte i klasssökvägen. Du kan helt enkelt flytta katalogen upp en nivå till libs och sedan när du kör den genom ignite.sh aktiveras den automatiskt.
  • Persistens måste vara aktiverat (Aktiverad via DataRegionConfiguration.setPersistenceEnabled(true)).
  • Komprimeringsläget måste ställas in med metoden DataStorageConfiguration.setWalPageCompression(), komprimering är inaktiverat som standard (DISABLED-läge).
  • Alternativt kan du ställa in komprimeringsnivån med metoden DataStorageConfiguration.setWalPageCompression(), se javadoc för metoden för giltiga värden för varje läge.

Slutsats

De övervägda datakomprimeringsmekanismerna i Apache Ignite kan användas oberoende av varandra, men vilken kombination av dem är också acceptabel. Genom att förstå hur de fungerar kan du avgöra hur lämpliga de är för dina uppgifter i din miljö och vad du måste offra när du använder dem. Komprimering av skivsidor är utformad för att komprimera huvudminnet och kan ge ett medium komprimeringsförhållande. WAL-sidans ögonblicksbildskomprimering kommer att ge en genomsnittlig grad av komprimering för WAL-filer, och kommer med största sannolikhet även att förbättra prestandan. WAL-komprimering kommer inte att ha en positiv effekt på prestanda, men kommer att minska storleken på WAL-filer så mycket som möjligt genom att ta bort fysiska poster.

Källa: will.com

Lägg en kommentar