Сціск дадзеных у Apache Ignite. Вопыт Сбера

Сціск дадзеных у Apache Ignite. Вопыт СбераПры працы з вялікімі аб'ёмамі дадзеных часам можа востра ўстаць праблема недахопу месца на дысках. Адным са спосабаў рашэння дадзенай праблемы з'яўляецца сціск, дзякуючы якому, на тым жа абсталяванні, можна сабе дазволіць павялічыць аб'ёмы захоўвання. У дадзеным артыкуле мы разгледзім, як працуе сціск дадзеных у Apache Ignite. У артыкуле будуць апісаны толькі рэалізаваныя ўсярэдзіне прадукта спосабы сціску на дыску. Іншыя спосабы сціску дадзеных (па сетцы, у памяці) як рэалізаваныя, так і няма застануцца за рамкамі.

Такім чынам, пры ўключаным persistence рэжыме, у выніку змены дадзеных у кэшах, Ignite пачынае запісваць на дыск:

  1. Змесціва кэшаў
  2. Часопіс папераджальнага запісу (Write Ahead Log, далей проста WAL)

Для сціску WAL ужо даўнавата існуе механізм, які завецца WAL compaction. У нядаўна які выйшаў Apache Ignite 2.8 з'явілася яшчэ два механізму якія дазваляюць сціскаць дадзеныя на дыску, гэта disk page compression для сціску змесціва кэшаў і WAL page snapshot compression для сціску некаторых запісаў WAL. Больш падрабязна аб усіх гэтых трох механізмах ніжэй.

Disk page compression

Як гэта працуе

Для пачатку вельмі сцісла спынімся на тым як Ignite захоўвае дадзеныя. Для захоўвання выкарыстоўваецца старонкавая памяць. Памер старонкі задаецца на старце вузла і не можа быць зменены на пазнейшых этапах, таксама памер старонкі павінен быць ступенню двойкі і крацены памеру блока файлавай сістэмы. Старонкі падгружаюцца ў RAM з дыска па меры неабходнасці, памер дадзеных на дыску можа перавышаць аб'ём выдзеленай RAM. У выпадку недахопу месца ў RAM для падгрузкі старонкі з дыска старыя, ужо не выкарыстоўваюцца старонкі будуць выцесненыя з RAM.

На дыску дадзеныя захоўваюцца ў наступным выглядзе: на кожную партыцыю кожнай кэш-групы ствараецца асобны файл, у гэтым файле, у парадку ўзрастання азначніка, адна за іншы ідуць старонкі. Поўны ідэнтыфікатар старонкі ўтрымоўвае ідэнтыфікатар кэш-групы, нумар партыцыі і азначнік старонкі ў файле. Такім чынам па поўным ідэнтыфікатары старонкі мы можам адназначна вызначыць файл і афсет у файле для кожнай старонкі. Больш падрабязна аб прыладзе старонкавай памяці можна прачытаць у артыкуле на Apache Ignite Wiki: Ignite Persistent Store - under the hood.

Механізм disk page compression, як можна здагадацца з назову, працуе на старонкавым узроўні. Пры ўключэнні дадзенага механізму праца з дадзенымі ў RAM выконваецца як ёсць, без які-небудзь кампрэсіі, але ў момант захавання старонак з RAM на дыск выконваецца іх сціск.

Але сціснуць кожную старонку ў асобнасці гэтае яшчэ не рашэнне праблемы, трэба неяк паменшыць памер выніковых файлаў з дадзенымі. Калі памер старонкі перастае быць фіксаваным, мы ўжо не можам пісаць старонкі ў файл адну за адной, бо гэта можа спарадзіць цэлы шэраг праблем:

  • Мы не зможам з дапамогай азначніка старонкі вылічыць афсет па якім яна размяшчаецца ў файле.
  • Не зразумела, што рабіць са старонкамі, якія знаходзяцца не ў канцы файла і мяняюць свой памер. Калі памер старонкі памяншаецца, месца якое яна вызваліла знікае. Калі памер старонкі павялічваецца, для яе трэба шукаць ужо новае месца ў файле.
  • Калі старонка зрушыцца на не кратнае памеру блока файлавай сістэмы лік байт, то для яе чытання ці запісы, запатрабуецца закрануць на адзін блок файлавай сістэмы больш, што можа прывесці да дэградацыі прадукцыйнасці.

Каб не вырашаць гэтыя праблемы на сваім узроўні самастойна, disk page compression у Apache Ignite выкарыстоўвае механізм файлавай сістэмы пад назвай sparse файлы. Sparse (разрэджаны) файл - гэта такі файл, у якім некаторыя, запоўненыя нулямі рэгіёны могуць быць пазначаныя як «дзіркі». Пры гэтым блокаў файлавай сістэмы для захоўвання гэтых дзюр выдзелена не будзе, у выніку чаго дасягаецца эканомія месца на дыску.

Лагічна, што каб вызваліць блок файлавай сістэмы, памер дзюры павінен быць больш або роўны блоку файлавай сістэмы, што накладвае дадатковае абмежаванне на памер старонкі а Apache Ignite: каб сціск даваў хоць нейкі эфект неабходна каб памер старонкі быў строга больш памеру блока файлавай сістэмы . Калі памер старонкі будзе роўны памеру блока, то мы ніколі не зможам вызваліць ніводнага блока, бо каб вызваліць адзіны блок трэба каб сціснутая старонка займала 0 байт. Калі ж памер старонкі будзе роўны памеру 2-х ці 4-х блокаў, мы ўжо зможам вызваліць як мінімум адзін блок, калі наша старонка сціснецца як мінімум да 50% альбо да 75% адпаведна.

Такім чынам, выніковае апісанне працы механізму: Пры запісе старонкі на дыск, робіцца спроба сціснуць старонку. Калі памер сціснутай старонкі дазваляе вызваліць адзін ці больш блок файлавай сістэмы, то старонка запісваецца ў сціснутым выглядзе, на месцы вызваленых блокаў прабіваецца "дзірка" (выконваецца сістэмны выклік. fallocate() са сцягам «punch hole»). Калі памер сціснутай старонкі не дазваляе вызваліць блокі, старонка захоўваецца як ёсць, у несціснутым выглядзе. Усе афсеты старонак лічацца таксама як і без кампрэсіі, множаннем індэкса старонкі на памер старонкі. Ніякай рэлакацыі старонак саматугам не патрабуецца. Афсеты старонак як і без кампрэсіі пападаюць на межы блокаў файлавай сістэмы.

Сціск дадзеных у Apache Ignite. Вопыт Сбера

У бягучай рэалізацыі Ignite умее працаваць са sparse файламі толькі пад АС Linux, адпаведна disk page compression можа быць уключаны толькі пры выкарыстанні Ignite на гэтай аперацыйнай сістэме.

Алгарытмы сціску, якія могуць быць скарыстаны для disk page compression: ZSTD, LZ4, Snappy. Акрамя таго ёсць рэжым працы (SKIP_GARBAGE), пры якім толькі выкідваецца невыкарыстоўванае ў старонцы месца без ужывання кампрэсіі на пакінутых дадзеных, што дазваляе зменшыць нагрузку на CPU у параўнанні з пералічанымі раней алгарытмамі.

Уплыў на прадукцыйнасць

Нажаль фактычнага замеру прадукцыйнасці на рэальных стэндах я не праводзіў, бо ў нас не плануецца выкарыстоўваць гэты механізм у прадакшне, але можна паразважаць тэарэтычна, дзе мы страцім, а дзе выйграем.

Для гэтага нам неабходна ўспомніць аб тым як выконваецца чытанне і запіс старонак пры звароце да іх:

  • Пры выкананні аперацыі чытання спачатку выконваецца яе пошук у RAM, калі пошук завяршыўся няўдала старонка падгружаецца ў RAM c дыска тым жа струменем, які выконвае чытанне.
  • Пры выкананні аперацыі запісу, старонка ў RAM пазначаецца як брудная, пры гэтым фізічнае захаванне старонкі на дыск адразу ў струмені якія выконваюць запіс не адбываецца. Усе брудныя старонкі захоўваюцца на дыск пазней падчас чэкпоінта асобнымі струменямі.

Такім чынам, уплыў на аперацыі чытання:

  • Дадатнае (disk IO), за рахунак памяншэння колькасці прачытаных блокаў файлавай сістэмы.
  • Адмоўнае (CPU), за кошт дадатковай нагрузкі неабходнай аперацыйнай сістэме для працы са sparse файламі. Таксама магчыма тут няяўна з'явяцца дадатковыя IO аперацыі для захавання больш складанай структуры sparse файла (з усімі дэталямі працы sparse файлаў я, нажаль, не знаёмы).
  • Адмоўнае (CPU), за кошт неабходнасці дэкампрэсіі старонак.
  • Уплывы на аперацыі запісы няма.
  • Уплыў на працэс чэкпоінта (тут усё аналагічна аперацыям чытання):
  • Дадатнае (disk IO), за рахунак памяншэння колькасці запісаных блокаў файлавай сістэмы.
  • Адмоўнае (CPU, магчыма disk IO), за рахунак працы са sparse файламі.
  • Адмоўнае (CPU), за кошт неабходнасці сціску старонак.

Якая чара шаляў пераважыць? Гэта ўсё вельмі залежыць ад атачэння, але я схіляюся да таго, што disk page compression прывядзе хутчэй да дэградацыі прадукцыйнасці на большасці сістэм. Тым больш што тэсты на іншых СКБД выкарыстоўвалых падобны падыход са sparse файламі паказваюць падзенне прадукцыйнасці пры ўключаным сціску.

Як уключыць і наладзіць

Як ужо было сказанае вышэй, мінімальная версія Apache Ignite, якая падтрымлівае disk page compression: 2.8 і падтрымліваецца толькі аперацыйная сістэма Linux. Уключэнне і настройка выконваецца наступным чынам:

  • У class-path павінен быць модуль ignite-compression. Па змаўчанні ён знаходзіцца ў дыстрыбутыве Apache Ignite у дырэкторыі libs/optional і не ўключаецца ў class-path. Можна проста перанесці дырэкторыю на адзін узровень уверх у libs і тады пры запуску праз ignite.sh ён аўтаматычна будзе ўключаны.
  • Persistence павінен быць уключаны (Уключаецца праз DataRegionConfiguration.setPersistenceEnabled(true)).
  • Памер старонкі павінен быць большы за памер блока файлавай сістэмы (задаць можна з дапамогай DataStorageConfiguration.setPageSize() ).
  • Для кожнага кэша, дадзеныя якога патрабуецца сціскаць неабходна ў канфігурацыі наладзіць метад сціску і (апцыянальна) узровень сціску (метады CacheConfiguration.setDiskPageCompression() , CacheConfiguration.setDiskPageCompressionLevel()).

WAL compaction

Як гэта працуе

Што такое WAL і навошта ён патрэбен? Вельмі коратка: гэта часопіс у які трапляюць усе падзеі якія змяняюць у выніку старонкавае сховішча. Патрэбны ён у першую чаргу для магчымасці аднаўлення ў выпадку падзення. Любая аперацыя перш чым аддаць кіраванне карыстачу павінна спачатку запісаць падзею ў WAL, каб у выпадку падзення мець магчымасць прайграць па часопісе і аднавіць усе аперацыі, па якіх карыстач атрымаў паспяховы адказ, нават калі гэтыя аперацыі не паспелі адаб'ецца ў старонкавым сховішчы на ​​дыску (вышэй ужо было апісана, што фактычны запіс у старонкавае сховішча выконваецца ў працэсе, які называецца «чэкпоінт» з некаторым спазненнем асобнымі патокамі).

Запісы ў WAL дзеляцца на лагічныя і фізічныя. Лагічныя - гэта самі ключы і значэння. Фізічныя - адлюстроўваюць змены старонак у старонкавым сховішчы. Калі лагічныя запісы могуць быць карысныя яшчэ для якіх-небудзь выпадкаў, фізічныя запісы патрэбны толькі для аднаўлення ў выпадку падзення і патрэбны запіс толькі з моманту апошняга паспяховага чэкпоінта. Тут мы не будзем удавацца ў падрабязнасці і тлумачыць чаму гэта працуе менавіта так, але каму цікава, могуць звярнуцца да ўжо згаданага артыкула на Apache Ignite Wiki: Ignite Persistent Store - under the hood.

На адзін лагічны запіс часта прыпадае некалькі фізічных запісаў. Гэта значыць, напрыклад, адна аперацыя put у кэш закранае некалькі старонак у старонкавай памяці (старонку з самімі дадзенымі, старонкі з азначнікамі, старонкі з free-list'амі). На некаторых сінтэтычных тэстах у мяне атрымлівалася, што фізічныя запісы займалі да 90% аб'ёму WAL файла. Пры гэтым патрэбны яны вельмі непрацяглы час (па змаўчанні інтэрвал паміж чэкпаінтамі - 3 хвіліны). Лагічна было б ад гэтых дадзеных пасля страты іх актуальнасці пазбаўляцца. Менавіта гэта і выконвае механізм WAL compaction, пазбаўляецца ад фізічных запісаў і сціскае з дапамогай zip пакінутыя лагічныя запісы, пры гэтым памер файла памяншаецца вельмі значна (часам у дзясяткі разоў).

Фізічна WAL складаецца з некалькіх сегментаў (па змаўчанні 10) фіксаванага памеру (па змаўчанні 64Мб), якія перазапісваюцца па крузе. Як толькі бягучы сегмент запаўняецца, бягучым прызначаецца наступны за ім сегмент, а запоўнены сегмент капіюецца ў архіў асобным струменем. WAL compaction ужо працуе з архіўнымі сегментамі. Таксама асобным струменем ён адсочвае выкананне чэкпоінта і пачынае сціск па архіўных сегментах, фізічныя запісы для якіх ужо не патрэбныя.

Сціск дадзеных у Apache Ignite. Вопыт Сбера

Уплыў на прадукцыйнасць

Паколькі WAL compaction працуе асобным струменем, то прамога ўплыву на выкананыя аперацыі быць не павінна. Але ён усё ж такі дае дадатковую фонавую нагрузку на CPU (сціск) і дыск (чытанне кожнага WAL сегмента з архіва і запіс сціснутых сегментаў), таму калі сістэма працуе на мяжы магчымасцяў, ён таксама прывядзе да дэградацыі прадукцыйнасці.

Як уключыць і наладзіць

Уключыць WAL compaction можна з дапамогай уласцівасці WalCompactionEnabled в DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). Таксама, з дапамогай метаду DataStorageConfiguration.setWalCompactionLevel() можна задаць ступень сціску, калі не задавальняе значэнне па змаўчанні (BEST_SPEED).

WAL page snapshot compression

Як гэта працуе

Раней ужо высветлілі, што ў WAL запісы падзяляюцца на лагічныя і фізічныя. На кожную змену кожнай старонкі ў старонкавай памяці фармуецца фізічны запіс WAL. Фізічныя запісы ў сваю чаргу таксама дзеляцца на 2 падвіда: page snapshot record і delta record. Кожны раз, калі мы мяняем нешта на старонцы і пераводзім яе з чыстага стану ў бруднае, у WAL захоўваецца поўная копія гэтай старонкі (page snapshot record — снэпшот старонкі). Нават калі мы памянялі толькі адзін байт у WAL захаваецца запіс памерам крыху больш за памер старонкі. Калі ж мы мяняем нешта на ўжо бруднай старонцы, то ў WAL фарміруецца delta record, у якой адлюстраваны толькі змены ў параўнанні з папярэднім станам старонкі, але не ўся старонка цалкам. Паколькі скід стану старонак з бруднага на чысты выконваецца падчас чэкпоінта, адразу пасля пачатку чэкпоінта практычна ўсе фізічныя запісы будуць складацца толькі са снэпшотаў старонак (бо ўсе старонкі адразу пасля пачатку чэкпоінта чыстыя), затым па меры набліжэння да наступнага чэкпоінту дзель delta record пачынае расці і зноў скідаецца на пачатку наступнага чэкпоінта. Замеры на некаторых сінтэтычных тэстах паказвалі, што доля снэпшотаў старонак у агульным аб'ёме фізічных запісаў дасягае 90 працэнтаў.

Ідэя WAL page snapshot compression складаецца ў тым, каб сціскаць снэпшоты старонак выкарыстаючы ўжо гатовая прылада для сціску старонак (гл. disk page compression). Пры гэтым у WAL запісы захоўваюцца паслядоўна ў append-only рэжыме і няма неабходнасці прывязкі запісаў да меж блокаў файлавай сістэмы, таму тут, у адрозненні ад механізму disk page compression, нам зусім не патрэбныя sparse файлы, адпаведна працаваць гэты механізм будзе не толькі на АС Linux. Акрамя таго, нам ужо не важна як моцна мы змаглі сціснуць старонку. Нават калі мы вызвалілі 1 байт гэта ўжо дадатны вынік і мы можам захоўваць у WAL сціснутыя дадзеныя, у адрозненне ад disk page compression, дзе мы захоўваем сціснутую старонку толькі калі вызвалілі больш за 1 блок файлавай сістэмы.

Старонкі - добра сцісканыя дадзеныя, іх доля ў агульным аб'ёме WAL вельмі высокая, такім чынам не змяняючы фармат WAL файла мы можам атрымаць значнае скарачэнне яго памеру. Сціск у тым ліку лагічных запісаў запатрабавала б змена фармату і страту сумяшчальнасці, напрыклад, для вонкавых спажыўцоў, якім могуць быць цікавыя лагічныя запісы, пры гэтым не прынесла б значнага памяншэння аб'ёму файла.

Як і для disk page compression для WAL page snapshot compression могуць быць скарыстаны алгарытмы сціску ZSTD, LZ4, Snappy, а таксама рэжым SKIP_GARBAGE.

Уплыў на прадукцыйнасць

Як не цяжка заўважыць, наўпрост уключэнне WAL page snapshot compression уплывае толькі на струмені якія запісваюць дадзеныя ў старонкавую памяць, гэта значыць на тыя струмені якія змяняюць дадзеныя ў кэшах. Чытанне з WAL фізічных запісаў адбываецца толькі аднаразова, у момант узняцця вузла пасля падзення (і толькі ў выпадку падзення ў працэсе чэкпоінта).

На патокі змяняюць дадзеныя гэта ўплывае наступным чынам: мы атрымліваем адмоўны эфект (CPU) за кошт неабходнасці кожны раз сціскаць старонку перад запісам на дыск і станоўчы эфект (disk IO) за кошт памяншэння колькасці дадзеных. Адпаведна, тут усё проста, калі прадукцыйнасць сістэмы ўпіраецца ў CPU, атрымліваем невялікую дэградацыю, калі ў дыскавы ўвод/выснова – атрымліваем прырост.

Ускосна памяншэнне памеру WAL таксама ўплывае (дадатна) на струмені якія скідаюць у архіў сегменты WAL і на струмені WAL compaction.

Рэальныя тэсты прадукцыйнасці на нашым асяроддзі на сінтэтычных дадзеных паказалі невялікі прырост (на 10% -15% вырас throughput, на 10% -15% паменшылася latency).

Як уключыць і наладзіць

Мінімальная версія Apache Ignite: 2.8. Уключэнне і настройка выконваецца наступным чынам:

  • У class-path павінен быць модуль ignite-compression. Па змаўчанні ён знаходзіцца ў дыстрыбутыве Apache Ignite у дырэкторыі libs/optional і не ўключаецца ў class-path. Можна проста перанесці дырэкторыю на адзін узровень уверх у libs і тады пры запуску праз ignite.sh ён аўтаматычна будзе ўключаны.
  • Persistence павінен быць уключаны (Уключаецца праз DataRegionConfiguration.setPersistenceEnabled(true)).
  • Павінен быць зададзены рэжым сціску з дапамогай метаду DataStorageConfiguration.setWalPageCompression(), па змаўчанні сціск адключана (рэжым DISABLED).
  • Апцыянальна можна задаць ступень сціску з дапамогай метаду DataStorageConfiguration.setWalPageCompression(), дапушчальныя значэння для кожнага з рэжымаў глядзі ў javadoc да метаду.

Заключэнне

Разгледжаныя механізмы сціску дадзеных у Apache Ignite могуць выкарыстоўвацца незалежна сябар ад сябра, але таксама дапушчальныя і любыя іх камбінацыі. Разуменне прынцыпаў іх працы дазволіць вызначыць наколькі яны падыходзяць пад вашыя задачы на ​​вашым асяроддзі і чым давядзецца ахвяраваць пры іх выкарыстанні. Disk page compression прызначаны для сціску асноўнага сховішча і можа даць сярэднюю ступень сціску. WAL page snapshot compression дасць сярэднюю ступень сціску ўжо WAL файлаў, пры гэтым хутчэй за ўсё нават павысіць прадукцыйнасць. WAL compaction на прадукцыйнасць дадатна не паўплывае, але максімальна скароціць памер WAL файлаў за рахунак выдалення фізічных запісаў.

Крыніца: habr.com

Дадаць каментар