Як використовувати Parquet і не посковзнутися



Про зберігання даних Parquet-файли не так багато інформації на Хабре, тому сподіваємося, розповідь про досвід Wrike по його впровадженню у зв'язці зі Spark вам стане в нагоді.
Зокрема, в цій статті ви дізнаєтесь:

— навіщо потрібен «паркет»;
— як він улаштований;
— коли варто його використовувати;
— в яких випадках він не дуже зручний.



Напевно, варто почати з питання, навіщо ми взагалі почали шукати новий спосіб зберігання даних замість попередньої агрегації і збереження результату в БД і якими критеріями керувалися при прийнятті рішення?

У відділі аналітики Wrike ми використовуємо Apache Spark, масштабований і набирає популярність інструмент для роботи з великими даними (у нас це різноманітні звіти та інші потоки вхідних даних і подій). Докладніше про те, як у нас працює Spark, ми розповідали раніше.

Спочатку нам хотілося розгорнути систему швидко і без особливих інфраструктурних хитрощів, тому ми вирішили обмежитися Standalone кластер-менеджером Спарка і текстовими файлами, які записували Json. На той момент у нас не було великого вхідного потоку даних, так що розгортати hadoop і т. п. не було сенсу.

Про те, яка картина у нас була на початковому етапі:
  • Ми прагнули обійтися мінімальним стеком технологій і зробити систему максимально простий, тому відразу відкинули Spark поверх hadoop, Cassandra, MongoDB, та інші, що потребують особливого менеджменту, способи зберігання. Наші диски цілком спритні і відмінно справлялися з потоком даних, крім того машини в кластері розташовані близько один до одного і пов'язані потужним мережевим інтерфейсом.
  • Вхідні дані у нас були погано структуровані, тому виділити універсальну схему було складно.
  • Схема швидко еволюціонувала з-за того, що ми постійно додавали нові події і покращували трэкинг активності користувачів, тому вибрали json як формат даних і складали їх у звичайні текстові файли.
  • Ми прагнули наблизитися до event-based аналітиці. А в цьому разі кожна подія має бути максимально збагачений інформацією, щоб звести кількість join операцій до мінімуму. На практиці збагачення даних породжувало все більше колонок у схемі (це важливий момент, ми до нього ще повернемося). Крім того, за великої кількості подій абсолютно різного роду, але пов'язаних між собою і характеризуються різними параметрами, схема також обзаводилась великою кількість колонок і інформація була досить розрядженою.
  • Ми працювали з незмінними даними: записали, а далі тільки читаємо.


Після кількох тижнів роботи ми зрозуміли, що з json даними працювати незручно і трудомістко: повільне читання, до того ж при численних тестових запитів кожен раз Spark змушений спочатку прочитати файл, визначити схему і тільки потім дістатися безпосередньо до виконання самого запиту. Звичайно, шлях Спарку можна скоротити, заздалегідь вказавши схему, але кожен раз проробляти цю додаткову роботу нам не хотілося.
Покопавшись в Спарці, ми виявили, що сам він активно використовує у себе всередині parquet-формат.

Що таке Parquet

Parquet — це бінарний, колоночно-орієнтований формат зберігання даних, спочатку створений для екосистеми hadoop. Розробники запевняють, що даний формат зберігання ідеальний для big data (незмінних).
Перше враження — ура, з Spark нарешті стало зручно працювати, він просто ожив, але, як не дивно, підкинув нам кілька несподіваних проблем. Справа в тому, що parquet веде себе як незмінна таблиця або БД. Значить для колонок визначений тип, і якщо раптом у вас комбінується складний тип даних (скажімо, вкладений json) з простим (звичайний рядковий значення), то вся система зруйнується. Наприклад, візьмемо дві події і напишемо їх у форматі Json:
{
 
"event_name": "event 1",
 
"value": "this is first value",
 
}
 

 
{
 
"event_name": "event 2",
 
"value": {"reason": "Ok"}
 
}
 


У parquet-файл записати їх не вийде, так як в першому випадку у вас рядок, а в другому складний тип. Гірше, якщо система записує вхідний потік даних у файл, скажімо, щогодини. У перший час можуть прийти події з рядковими value, а у другій — у вигляді складного типу. У підсумку, звичайно, вийде записати parquet файли, але при операції merge schema ви натрапите на помилку несумісності типів.

Щоб вирішити цю проблему, нам довелося піти на невеличкий компроміс. Ми визначили точно відому і гарантовану постачальником даних схему для частини інформації, а в іншому брали тільки верхнеуровневые ключі. При цьому самі дані записували як текст (найчастіше це був json), який ми зберігали у клітинці (надалі з допомогою простих map-reduce операцій це перетворювалося в зручний DataFrame) у разі прикладу вище ' «value»: {«reason»: «Ok»} 'перетворюється в' «value»: "{\«reason\»: \«Ok\»}" '. Також ми зіткнулися з деякими особливостями розбиття даних на частини Спарком.

Як виглядає структура Parquet файлів


Parquet є досить складним форматом порівняно з тим же текстовим файлом з json всередині.
Примітно, що свої корені цей формат навіть пустив в розробки Google, а саме в їхній проект під назвою Dremel — про це вже згадувалося на Хабре, але ми не будемо заглиблюватися в нетрі Dremel, бажаючі можуть прочитати про це тут: research.google.com/pubs/pub36632.html.

Якщо коротко, Parquet використовує архітектуру, засновану на рівнях визначення" (definition levels) і рівнях повторення" (repetition levels), що дозволяє досить ефективно кодувати дані, а інформація про схему виноситься в окремі метадані.
При цьому оптимально зберігаються і порожні значення.

Структура Parquet-файлу добре проілюстрована у документації:



Файли мають кілька рівнів розбиття на частини, завдяки чому можливо досить ефективне паралельне виконання операцій поверх них:

Row-group — це розбиття, що дозволяє паралельно працювати з даними на рівні Map-Reduce
Column chunk — розбиття на рівні колонок, що дозволяє розподіляти IO операції
Page — Розбиття колонок на сторінки, що дозволяє розподіляти роботу з кодування і стиску

Якщо зберегти дані в parquet файл на диск, використовуючи цю звичну нам файлову систему, ви виявите, що замість файлу створюється директорія, в якій міститься ціла колекція файлів. Частина з них — це метаінформація для, в ній — схема, а також різна службова інформація, включаючи частковий індекс, що дозволяє зчитувати тільки необхідні блоки даних при запиті. Інші частини, або партіціі, це і є наші Row group.

Для інтуїтивного розуміння будемо вважати Row groups набором файлів, об'єднаних загальною інформацією. До речі, це розбиття використовується HDFS для реалізації data locality, коли кожна нода в кластері може зчитувати ті дані, які безпосередньо розташовані у неї на диску. Більш того, row group виступає одиницею Map Reduce, і кожна map-reduce завдання в Spark працює зі своєю row-group. Тому worker зобов'язаний помістити групу рядків в пам'ять, і при налаштуванні розміру групи треба враховувати мінімальний обсяг пам'яті, виділений на завдання на найслабшій ноде, інакше можна наткнутися на OOM.
У нашому випадку ми зіткнулися з тим, що у певних умовах Spark, зчитуючи текстовий файл, формував тільки одну партицию, і з-за цього перетворення даних виконувалось тільки на одному ядрі, хоча ресурсів було доступно набагато більше. За допомогою операції repartition в rdd ми розбили вхідні дані, в результаті вийшло кілька row groups, і проблема пішла.

Column chunk (розбиття на рівні колонок) — оптимізує роботу з диском (дисками). Якщо представити дані як таблиці, то вони записуються не по рядках, а по колонках.

Представимо таблицю:

Тоді в текстовому файлі, скажімо, csv ми б зберігали дані на диску приблизно так:

У випадку з Parquet:

Завдяки цьому ми можемо зчитувати тільки необхідні нам колонки.

З усього різноманіття колонок на ділі аналітику в конкретний момент потрібні лише кілька, до того ж більшість колонок залишається порожніми. Parquet в рази прискорює процес роботи з даними, більше того — подібне структурування інформації спрощує стискання та кодування даних за рахунок їх однорідності і схожості.

Кожна колонка ділиться на сторінки (Pages), які, в свою чергу, містять метаінформацію і дані, закодовані за принципом архітектури з проекту Dremel. За рахунок цього досягається досить ефективне і швидке кодування. Крім того, на даному рівні здійснюється стиснення (якщо воно налаштоване). На даний момент доступні кодеки snappy, gzip, lzo.

Є підводні камені?


За рахунок «паркетної» організації даних складно налаштувати їх стрімінг — якщо передавати дані, то повністю всі групу. Також, якщо ви загубили метаінформацію або змінили контрольну суму для Сторінки даних, то вся сторінка буде втрачено (якщо для Column chank — то chank втрачено, аналогічно для row group). На кожному з рівнів розбиття будуються контрольні суми, так що можна відключити їх обчислення на рівні файлової системи для поліпшення продуктивності.

Висновки:



Переваги зберігання даних в Parquet:

  • Незважаючи на те, що вони створені для hdfs, дані можуть зберігатися і в інших файлових системах, таких як GlusterFs або поверх NFS
  • По суті це просто файли, а значить, з ними легко працювати, переміщати, бэкапить і повторити.
  • Колончатый вид дозволяє значно прискорити роботу аналітика, якщо йому не потрібні всі колонки відразу.
  • Нативна підтримка в Spark з коробки забезпечує можливість просто взяти і зберегти файл в улюблене сховище.
  • Ефективне зберігання з точки зору займаного місця.
  • Як показує практика, саме цей спосіб забезпечує найбільш швидку роботу на читання порівняно з використанням інших файлових форматів.


Недоліки:

  • Колончатый вид змушує замислюватися про схеми і типи даних.
  • Окрім Spark, Parquet не завжди має нативної підтримкою в інших продуктах.
  • Не підтримує зміну даних та еволюцію схеми. Звичайно, Spark вміє мерджить схему, якщо у вас вона змінюється з часом (для цього треба вказати спеціальну опцію при читанні), але, щоб щось змінити в уже існуючих файлі, не можна обійтися без перезапису, хіба що можна додати нову колонку.
  • Не підтримуються транзакції, так як це звичайні файли а не БД.


У Wrike ми вже досить давно використовуємо parquet-файли в якості зберігання збагачених подієвих даних, наші аналітики ганяють досить багато запитів до них кожен день, у нас виробилася особлива методика роботи з даною технологією, так що із задоволенням поділимося досвідом з тими, хто хоче спробувати parquet у справі, і відповімо на всі питання в коментарях.

p.s. Звичайно, надалі ми не раз переглядали свої погляди з приводу форми зберігання даних, наприклад, нам радили більш популярний Avro формат, але поки гострої необхідності щось змінювати у нас немає.

Для тих, хто досі не зрозумів різницю між строково-орієнтованими даними і колончато-орієнтованими, тобто чудове відео від Cloudera
а також досить цікава презентация про формати зберігання даних для аналітики.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.