Стиснення даних в 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 на цій операційній системі.

Алгоритми стиснення, які можуть бути використані для дискової композиції: ZSTD, LZ4, Snappy. Крім того є режим роботи (SKIP_GARBAGE), при якому тільки викидається місце, що не використовується в сторінці, без застосування компресії на даних, що дозволяє знизити навантаження на CPU в порівнянні з перерахованими раніше алгоритмами.

Вплив на продуктивність

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

Для цього нам необхідно згадати про те, як виконується читання та запис сторінок при зверненні до них:

  • При виконанні операції читання спочатку виконується її пошук у RAM, якщо пошук завершився невдало сторінка підвантажується в RAM з диска тим самим потоком, який виконує читання.
  • При виконанні операції запису сторінка в 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% зріс черезвихід, на 10%-15% зменшилася вага).

Як увімкнути та налаштувати

Мінімальна версія 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

Додати коментар або відгук