Pri delu z velikimi količinami podatkov se lahko pogosto pojavi problem pomanjkanja prostora na diskih. Eden od načinov reševanja te težave je stiskanje, kar omogoča, da na enakem delu opreme povečamo prostornino shranjevanja. V tem članku bomo pregledali, kako delo stiskanja podatkov deluje v Apache Ignite. V članku bodo opisani samo načini stiskanja, ki so implementirani znotraj proizvoda. Drugi načini stiskanja podatkov (po omrežju, v pomnilniku), tako realizirani kot niso, bodo ostali izven okvira.
Torej, ko je način trajne shranjevanja vklopljen, začne Ignite v rezultatih sprememb podatkov v predpomnilnikih zapisovati na disk:
- Vsebina predpomnilnikov
- Dnevnik predhodnega zapisa (Write Ahead Log, dalje samo WAL)
Za stiskanje WAL že dolgo obstaja mehanizem, imenovan WAL compaction. V nedavno izdanem Apache Ignite 2.8 sta se pojavila še dva mehanizma, ki omogočata stiskanje podatkov na disku: disk page compression za stiskanje vsebine predpomnilnikov in WAL page snapshot compression za stiskanje nekaterih zapisov WAL. Več o vseh teh treh mehanizmih spodaj.
Stiskanje strani na disku
Kako to funkcionira
Najprej se zelo na kratko ustavimo pri tem, kako Ignite shranjuje podatke. Za shranjevanje se uporablja stranična pomnilnik. Velikost strani se določi ob zagonu vozlišča in je ni mogoče spremeniti v kasnejših fazah, prav tako mora biti velikost strani potencna dvojke in večkratnik velikosti bloka datotečnega sistema. Strani se nalagajo v RAM iz diska po potrebi, velikost podatkov na disku pa lahko presega dodeljen RAM. V primeru pomanjkanja prostora v RAM za nalaganje strani z diska bodo stare, že neužitne strani izrinjene iz RAM.
Na disku so podatki shranjeni v naslednji obliki: za vsako particijo vsake skupine predpomnilnika se ustvari ločena datoteka, v tej datoteki pa se strani izmenično postavljajo v naraščajočem vrstnem redu indeksa. Celoten identifikator strani vsebuje identifikator skupine predpomnilnika, številko particije in indeks strani v datoteki. Tako lahko na podlagi celotnega identifikatorja strani nedvoumno določimo datoteko in ofset v datoteki za vsako stran. Več o strukturi straničnega pomnilnika lahko preberete v članku na Apache Ignite Wiki: .
Mehanizem stiskanja stran diskov, kot že ime pove, deluje na ravni strani. Ko je ta mehanizem vklopljen, se delo s podatki v RAM-u izvaja brez kakršne koli kompresije, vendar se ob shranjevanju strani iz RAM-a na disk izvaja njihovo stiskanje.
Vendar pa stiskanje vsake strani posebej še ni rešitev problema, potrebno je nekako zmanjšati velikost končnih datotek s podatki. Če velikost strani neha biti fiksna, ne moremo več shranjevati strani v datoteko eno za drugo, ker to lahko povzroči vrsto težav:
- Ne bomo mogli izračunati ofseta, na katerem se stran nahaja v datoteki, s pomočjo indeksa strani.
- Ni jasno, kaj storiti s stranmi, ki se ne nahajajo na koncu datoteke in spreminjajo svojo velikost. Če se velikost strani zmanjša, se prostor, ki ga je osvobodila, izgubi. Če se velikost strani poveča, je potrebno zanj iskati novo mesto v datoteki.
- Če se stran premakne za število bajtov, ki ni večje od velikosti bloka datotečnega sistema, bo za njeno branje ali zapis potrebno zajeti še en blok datotečnega sistema, kar lahko povzroči degradacijo zmogljivosti.
Da ne bi reševali teh težav na svojem nivoju, stiskanje strani disk v Apache Ignite uporablja mehanizem datotečnega sistema, imenovan redek datoteka. Redek datoteka je datoteka, v kateri so lahko nekateri, z ničlami napolnjeni regiji označeni kot 'luknje'. Tako blokov datotečnega sistema za shranjevanje teh lukenj ne bo dodeljenih, kar rezultira v prihranku prostora na disku.
Logično je, da mora biti za osvoboditev bloka datotečnega sistema velikost luknje večja ali enaka bloku datotečnega sistema, kar nalaga dodatno omejitev na velikost strani v Apache Ignite: da bi stiskanje imelo kakršen koli učinek, mora biti velikost strani strogo večja od velikosti bloka datotečnega sistema. Če bo velikost strani enaka velikosti bloka, ne bomo mogli nikoli osvoboditi nobenega bloka, saj mora za osvoboditev enega bloka stisnjena stran zasedati 0 bajtov. Če bo velikost strani enaka velikosti dveh ali štirih blokov, lahko osvobodimo vsaj en blok, če se naša stran stisne na vsaj 50 % ali 75 % ustrezno.
Tako, končna razlaga delovanja mehanizma: Pri shranjevanju strani na disk se poskusi zmožnosti stiskanja strani. Če velikost stisnjene strani omogoča sprostitev enega ali več blokov datotečnega sistema, se stran shrani v stisnjeni obliki, na mestu sproščenih blokov pa se ustvari "luknja" (izvede se sistemski klic fallocate() z označbo "punch hole"). Če velikost stisnjene strani ne omogoča sprostitve blokov, se stran shrani taka, kot je, v nestisnjeni obliki. Vse ofsetne strani se štejejo kot pred stiskanjem, s pomnoževanjem indeksa strani z velikostjo strani. Nobena relokacija strani ni potrebna. Ofseti strani, kot pred stiskanjem, se nahajajo na mejah blokov datotečnega sistema.

V trenutni implementaciji Ignite zna delati z redkimi datotekami samo na operacijskem sistemu Linux, zato je stiskanje diskovnih strani mogoče omogočiti le pri uporabi Ignite na tem operacijskem sistemu.
Algoritmi stiskanja, ki jih je mogoče uporabiti za stiskanje diskovnih strani: ZSTD, LZ4, Snappy. Poleg tega obstaja način delovanja (SKIP_GARBAGE), pri katerem se le odstrani neizkoriščen prostor na strani brez uporabe stiskanja na preostalih podatkih, kar omogoča zmanjšanje obremenitve CPU v primerjavi z zgoraj navedenimi algoritmi.
Vpliv na zmogljivost
Žal dejanske meritve zmogljivosti na realnih okoljih nisem izvajal, ker ne nameravamo uporabljati tega mehanizma v produkciji, vendar lahko teoretično razpravljamo, kje bomo izgubili, in kje pridobili.
Za to se moramo spomniti, kako poteka branje in zapis strani ob dostopu do njih:
- Pri izvajanju operacije branja se najprej izvede iskanje v RAM-u, če iskanje ni bilo uspešno, se stran naloži v RAM iz diska s tistim tokom, ki izvaja branje.
- Pri izvajanju operacije zapisa se stran v RAM-u označi kot umazana, pri čemer fizično shranjevanje strani na disk ne nastopi takoj v tokom, ki izvaja zapis. Vse umazane strani se shranijo na disk kasneje v procesu točkovanja z drugimi tokovi.
Tako vpliv na operacije branja:
- Pozitiven (disk IO), zaradi zmanjšanja števila prebranih blokov datotečnega sistema.
- Negativno (CPU), zaradi dodatne obremenitve, potrebne operacijskemu sistemu za delo z sparse datotekami. Možno je tudi, da se tukaj implicitno pojavijo dodatne IO operacije za shranjevanje bolj kompleksne strukture sparse datoteke (z vsemi podrobnostmi delovanja sparse datotek, na žalost, nisem seznanjen).
- Negativno (CPU), zaradi potrebe po dekompresiji strani.
- Na operacije zapisa ni vpliva.
- Vpliv na postopek kontrolne točke (tukaj je vse podobno operacijam branja):
- Pozitivno (disk IO), zaradi zmanjšanja števila zapisanih blokov datotečnega sistema.
- Negativno (CPU, mogoče disk IO), zaradi dela s sparse datotekami.
- Negativno (CPU), zaradi potrebe po stiskanju strani.
Katera skodelica tehtnice bo prevladala? To je zelo odvisno od okolja, a nagibam se k temu, da bo stiskanje diskovnih strani verjetno povzročilo degradacijo zmogljivosti v večini sistemov. Še posebej ker testi na drugih DBMS, ki uporabljajo podoben pristop s sparse datotekami, kažejo padec zmogljivosti pri vklopljenem stiskanju.
Kako vključiti in nastaviti
Kot je bilo že omenjeno zgoraj, minimalna različica Apache Ignite, ki podpira stiskanje diskovnih strani: 2.8 in se podpira le operacijski sistem Linux. Vključitev in nastavitev poteka na naslednji način:
- V class-pathu mora biti modul ignite-compression. Po privzetku se nahaja v distribuciji Apache Ignite v direktoriju libs/optional in ni vključen v class-path. Lahko preprosto premaknete direktorij eno raven višje v libs in takrat bo ob zagonu preko ignite.sh samodejno vključen.
- Vztrajnost mora biti vključena (vključi se preko
DataRegionConfiguration.setPersistenceEnabled(true)). - Velikost strani mora biti večja od velikosti bloka datotečnega sistema (to lahko nastavite z
DataStorageConfiguration.setPageSize()). - Za vsak predpomnilnik, katerega podatke je treba stiskati, je treba v konfiguraciji nastaviti metodo stiskanja in (opcijsko) raven stiskanja (metode
CacheConfiguration.setDiskPageCompression(), CacheConfiguration.setDiskPageCompressionLevel()).
WAL stiskanje
Kako to funkcionira
Kaj je WAL in zakaj je potreben? Na kratko: to je dnevnik, v katerega se zabeležijo vsi dogodki, ki na koncu spremenijo shranjevanje strani. Potreben je predvsem za možnost obnovitve v primeru padca. Vsaka operacija mora najprej zabeležiti dogodek v WAL, preden predaja nadzor uporabniku, da bi v primeru padca lahko predvajali dnevnik in obnovili vse operacije, za katere je uporabnik prejel uspešen odziv, tudi če te operacije niso uspele odražati v shranjevanju strani na disku (zgoraj je bilo opisano, da se dejanska zapisovanje v shranjevanje strani izvaja v procesu, imenovanem
Zapiski v WAL so razdeljeni na logične in fizične. Logične so sami ključi in vrednosti. Fizični odražajo spremembe strani v shranjevanju strani. Če so logični zapisi morda uporabni še v drugih primerih, so fizični zapisi potrebni le za obnovitev v primeru padca in potrebni so le zapisi od zadnjega uspešnega kontrolnega točke. Tukaj se ne bomo poglobili v podrobnosti in razložili, zakaj točno tako deluje, vendar lahko tisti, ki jih zanima, preberejo že omenjeno članek na Apache Ignite Wiki: .
Na en logični zapis pogosto pripada več fizičnih zapisov. To pomeni, da ena operacija put v predpomnilnik vpliva na več strani v pomnilniku strani (stran s podatki, strani z indeksi, strani z seznamom prostih mest). Na nekaterih sintetičnih testih sem ugotovil, da so fizični zapisi predstavljali do 90 % prostornine datoteke WAL. Hkrati so potrebni zelo kratek čas (privzeto je interval med kontrolnimi točkami 3 minute). Smiselno bi bilo, da se teh podatkov po izgubi njihove aktualnosti znebimo. Točno to izvaja mehanizem stiskanja WAL, ki se znebi fizičnih zapisov in stisne preostale logične zapise s pomočjo zip, pri tem pa se velikost datoteke znatno zmanjša (včasih tudi večkrat).
Fizični WAL sestavlja več segmentov (privzeto 10) fiksne velikosti (privzeto 64 MB), ki se prepisujejo v krogu. Takoj, ko se trenutni segment napolni, se mu dodeli naslednji segment, napolnjen segment pa se kopira v arhiv prek ločenega toka. WAL kompresija že deluje z arhivskimi segmenti. Prav tako prek ločenega toka spremlja izvajanje kontrolne točke in začne s stiskanjem arhivskih segmentov, fizični zapisi za katere več niso potrebni.

Vpliv na zmogljivost
Ker WAL kompresija deluje prek ločenega toka, ne bi smela imeti neposrednega vpliva na izvajane operacije. Vendar pa vseeno povzroča dodatno obremenitev CPU (kompresija) in diska (branje vsakega WAL segmenta iz arhiva in zapis stisnjenih segmentov), zato, če sistem deluje na robu svojih zmožnosti, lahko to povzroči tudi zmanjšanje zmogljivosti.
Kako vključiti in nastaviti
WAL kompresijo lahko omogočite z lastnostjo WalCompactionEnabled u DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). Prav tako lahko z metodo DataStorageConfiguration.setWalCompactionLevel() določite raven kompresije, če vas privzeta vrednost (BEST_SPEED) ne zadostuje.
Kompresija fotografij WAL strani
Kako to funkcionira
Prej smo ugotovili, da se zapisi WAL delijo na logične in fizične. Vsaka sprememba vsake strani v pomnilniku strani tvori fizični zapis WAL. Fizični zapisi se nato delijo na dva podvrsta: zapis posnetka strani in zapis delta. Vsakič, ko spremenimo kaj na strani in jo prenesemo iz čistega stanja v umazano, se v WAL shrani popolna kopija te strani (zapis posnetka strani — snapshot strani). Tudi če smo spremenili le en bajt, bo v WAL shranjen zapis velikosti malo večje od velikosti strani. Če pa spremenimo kaj na že umazani strani, se v WAL ustvari zapis delta, v katerem so odražene le spremembe v primerjavi s prejšnjim stanjem strani, ne pa cela stran. Ker se preklop stanja strani z umazanega na čisto izvede med točko preverjanja, bodo takoj po začetku točke preverjanja praktično vsi fizični zapisi sestavljeni le iz posnetkov strani (ker so vse strani takoj po začetku točke preverjanja čiste), nato pa se z bližanjem naslednji točki preverjanja delež zapisov delta začne povečevati in se spet ponastavi ob začetku naslednje točke preverjanja. Meritve nekaterih sintetičnih testov so pokazale, da delež posnetkov strani v skupni količini fizičnih zapisov doseže 90%.
Ideja stiskanja posnetkov strani WAL se nanaša na stiskanje posnetkov strani z uporabo že obstoječega orodja za stiskanje strani (glej stiskanje diskovnih strani). Pri tem se zapisi v WAL shranjujejo zaporedno v načinu le za dodajanje in ni potrebno povezovati zapisov z mejami blokov datotečnega sistema, zato tukaj, za razliko od mehanizma stiskanja diskovnih strani, sploh ne potrebujemo razpršenih datotek, kar pomeni, da bo ta mehanizem deloval tudi v operacijskem sistemu Linux. Poleg tega nam ni več pomembno, kako močno smo uspeli stisniti stran. Tudi če smo osvobodili 1 bajt, je to že pozitiven rezultat in lahko shranjujemo stisnjene podatke v WAL, za razliko od stiskanja diskovnih strani, kjer shranjujemo stisnjeno stran le, če smo osvobodili več kot 1 blok datotečnega sistema.
Strani so dobro stiskani podatki, njihov delež v skupni prostornini WAL je zelo visok, tako da lahko, ne da bi spreminjali format WAL datoteke, dosežemo znatno zmanjšanje njene velikosti. Stiskanje tudi logičnih zapisov bi zahtevalo spremembo formata in izgubo združljivosti, na primer za zunanje uporabnike, ki jih lahko zanimajo logični zapisi, hkrati pa ne bi prineslo znatnega zmanjšanja obsega datoteke.
Tako kot pri stiskanju datotečnih strani za stiskanje zapisov strani WAL lahko uporabimo algoritme za stiskanje ZSTD, LZ4, Snappy ter način SKIP_GARBAGE.
Vpliv na zmogljivost
Kot lahko opazite, neposredno vključitev stiskanja zapisov strani WAL vpliva samo na tokove, ki zapisujejo podatke v stranično pomnilnik, torej na tiste tokove, ki spreminjajo podatke v predpomnilniku. Branje fizičnih zapisov iz WAL poteka samo enkrat, ob sprostitvi vozlišča po napaki (in samo v primeru napake med prelaganjem).
Na tokove, ki spreminjajo podatke, to vpliva na naslednji način: dobimo negativen učinek (CPU) zaradi potrebe po stiskanju strani pred zapisom na disk in pozitiven učinek (disk IO) zaradi zmanjšanja količine zapisanih podatkov. Tako je tukaj preprosto, če se zmogljivost sistema zaradi CPU omejuje, dobimo majhno degradacijo, če pa zaradi diska vnos/izhod — dobimo povečanje.
Posredno zmanjšanje velikosti WAL prav tako pozitivno vpliva na tokove, ki arhivirajo segmente WAL, in na tokove kompresije WAL.
Dejanski testi zmogljivosti v našem okolju na sintetičnih podatkih so pokazali majhno povečanje (prehodnost se je povečala za 10%-15%, zakasnitev pa je zmanjšana za 10%-15%).
Kako vključiti in nastaviti
Minimalna različica Apache Ignite: 2.8. Vklop in nastavitev izvedemo na sledeči način:
- V class-pathu mora biti modul ignite-compression. Po privzetku se nahaja v distribuciji Apache Ignite v direktoriju libs/optional in ni vključen v class-path. Lahko preprosto premaknete direktorij eno raven višje v libs in takrat bo ob zagonu preko ignite.sh samodejno vključen.
- Vztrajnost mora biti vključena (vključi se preko
DataRegionConfiguration.setPersistenceEnabled(true)). - Morate določiti način stiskanja z metodo
DataStorageConfiguration.setWalPageCompression(), privzeto je stiskanje izklopljeno (način DISABLED). - Opcijsko lahko določite stopnjo stiskanja z metodo
DataStorageConfiguration.setWalPageCompression(), dovoljene vrednosti za vsak od načinov glejte v javadoc za metodo.
Zaključek
Obravnavani mehanizmi stiskanja podatkov v Apache Ignite se lahko uporabljajo neodvisno drug od drugega, vendar so dovoljene tudi vse njihove kombinacije. Razumevanje načel njihovega delovanja bo omogočilo določitev, kako dobro ustrezajo vašim nalogam v vaši okolici in čemu boste morali ob tem opustiti. Stiskanje diskovnih strani je namenjeno stiskanju glavnega shranjevanja in lahko doseže povprečno stopnjo stiskanja. Stiskanje posnetkov strani WAL bo dalo povprečno stopnjo stiskanja že datotek WAL, pri tem pa skoraj zagotovo celo povečalo zmogljivost. Kompaktiranje WAL-ov ne bo pozitivno vplivalo na zmogljivost, vendar bo maksimalno zmanjšalo velikost datotek WAL z odstranitvijo fizičnih zapisov.
Izvor: habr.com
