Локальний аналіз багатогігабайтних файлів JSON

Нещодавно я мав задоволення розбирати багатогігабайтні дампи JSON як частину проекту. Сам по собі формат JSON є досить зручним для використання, оскільки його легко читати людині та для нього доступно багато інструментів. JQ дозволяє виразити складні етапи обробки в одному командному рядку, а Jupyter із Python і Pandas дозволяє легко інтерактивно аналізувати, щоб швидко знайти те, що ви шукаєте.

Однак з файлами розміром кілька гігабайт аналіз стає набагато складнішим. Виконання однієї команди jq займе багато часу. Коли ви створюєте команди jq ітеративно, як це роблю я, ви швидко втомитеся від необхідності чекати приблизно хвилину, поки ваша команда завершиться успішно, лише щоб виявити, що вона насправді не повертає те, що ви шукали. Інтерактивний аналіз аналогічний. Читання 20 гігабайт JSON займе багато часу. Ви можете виявити, що дані не вміщуються в оперативну пам’ять (а саме, JSON – це формат, який читається людиною), або вам доведеться перезапустити ядро ​​Python, а це означає, що вам доведеться знову підтримувати завантаження .

Звичайно, існують хмарні пропозиції на основі Apache Beam, Flink тощо. Однак дані клієнтів не надходять до хмарних служб під моїм керівництвом, тому це виключено. Налаштувати локальне середовище, наприклад Flink, можливо, але потребує багато зусиль для одноразового сканування.

Під час спроби аналізу файлів такого розміру я знайшов два способи ефективної локальної обробки дуже великих файлів JSON, якими я хочу поділитися. Один — розпаралелювання командного рядка jq на основі GNU parallel, інший — на базі Jupyter із бібліотекою Dask.

На початку був командний рядок: JQ і Parallel#

Спочатку я намагаюся знайти прості рішення проблем, і більшість завдань, які я виконував для файлів JSON, були простими перетвореннями, які легко виразити мовою jq. Витягти вкладені значення або знайти певні об'єкти JSON дуже просто. Наприклад, уявіть, що у вас є 20 гігабайт таких структур (я вставив нові рядки для зручності читання, запис, який ми насправді читаємо, знаходиться в одному рядку):

{ "created_at": 1678184483, "modified_at": 1678184483, "artCode": "124546", "status": "ДОСТУПНИЙ", "description": "Светр для Windows XP", "brandName": "Майкрософт", "суб-мистецтво": [ { "created_at": 1678184483, "modified_at": 1678184483, "підкод": "123748", "зелений колір", "sub-sub-arts": [ { "created_at": 1678184483, "modified_at": 1678184483, "код": "12876", "size": "звисає", "currency": "євро", «поточна ціна»: 35 }, { "created_at": 1678184483, "modified_at": 1678184483, "код": "12876", "size": "підігнаний", "currency": "євро", «поточна ціна»: 30 } ] }, { "created_at": 1678184483, "modified_at": 1678184483, "підкод": "123749", "сірий колір", "sub-sub-arts": [ { "created_at": 1678184483, "modified_at": 1678184483, "код": "12879", "size": "звисає", "currency": "євро", «поточна ціна»: 40 }, { "created_at": 1678184483, "modified_at": 1678184483, "код": "12876", "size": "підігнаний", "currency": "євро", «поточна ціна»: 35 } ] } ] }

Запит jq на кшталт .subArts[]|select(.subSubArts[].size|contains("snug")) дасть вам усі підстатті, які мають підпідстаттю з розміром "snug" . Виконання подібної команди для 10-гігабайтного файлу JSON зайняло близько трьох хвилин, що не дуже добре, особливо якщо ви нетерплячі (як я).

На щастя, ми можемо пришвидшити це, якщо маємо інформацію про структуру вхідного файлу (очевидно, ми знаємо, що формат JSON). Ми використовуємо jq як фільтр для окремих об’єктів JSON, що означає, що ми повинні мати можливість ефективно розпаралелювати пошуковий вираз. Щоразу, коли мені потрібно запускати команди оболонки паралельно, я використовую GNU parallel, який може обробляти ко...

Локальний аналіз багатогігабайтних файлів JSON

Нещодавно я мав задоволення розбирати багатогігабайтні дампи JSON як частину проекту. Сам по собі формат JSON є досить зручним для використання, оскільки його легко читати людині та для нього доступно багато інструментів. JQ дозволяє виразити складні етапи обробки в одному командному рядку, а Jupyter із Python і Pandas дозволяє легко інтерактивно аналізувати, щоб швидко знайти те, що ви шукаєте.

Однак з файлами розміром кілька гігабайт аналіз стає набагато складнішим. Виконання однієї команди jq займе багато часу. Коли ви створюєте команди jq ітеративно, як це роблю я, ви швидко втомитеся від необхідності чекати приблизно хвилину, поки ваша команда завершиться успішно, лише щоб виявити, що вона насправді не повертає те, що ви шукали. Інтерактивний аналіз аналогічний. Читання 20 гігабайт JSON займе багато часу. Ви можете виявити, що дані не вміщуються в оперативну пам’ять (а саме, JSON – це формат, який читається людиною), або вам доведеться перезапустити ядро ​​Python, а це означає, що вам доведеться знову підтримувати завантаження .

Звичайно, існують хмарні пропозиції на основі Apache Beam, Flink тощо. Однак дані клієнтів не надходять до хмарних служб під моїм керівництвом, тому це виключено. Налаштувати локальне середовище, наприклад Flink, можливо, але потребує багато зусиль для одноразового сканування.

Під час спроби аналізу файлів такого розміру я знайшов два способи ефективної локальної обробки дуже великих файлів JSON, якими я хочу поділитися. Один — розпаралелювання командного рядка jq на основі GNU parallel, інший — на базі Jupyter із бібліотекою Dask.

На початку був командний рядок: JQ і Parallel#

Спочатку я намагаюся знайти прості рішення проблем, і більшість завдань, які я виконував для файлів JSON, були простими перетвореннями, які легко виразити мовою jq. Витягти вкладені значення або знайти певні об'єкти JSON дуже просто. Наприклад, уявіть, що у вас є 20 гігабайт таких структур (я вставив нові рядки для зручності читання, запис, який ми насправді читаємо, знаходиться в одному рядку):

{ "created_at": 1678184483, "modified_at": 1678184483, "artCode": "124546", "status": "ДОСТУПНИЙ", "description": "Светр для Windows XP", "brandName": "Майкрософт", "суб-мистецтво": [ { "created_at": 1678184483, "modified_at": 1678184483, "підкод": "123748", "зелений колір", "sub-sub-arts": [ { "created_at": 1678184483, "modified_at": 1678184483, "код": "12876", "size": "звисає", "currency": "євро", «поточна ціна»: 35 }, { "created_at": 1678184483, "modified_at": 1678184483, "код": "12876", "size": "підігнаний", "currency": "євро", «поточна ціна»: 30 } ] }, { "created_at": 1678184483, "modified_at": 1678184483, "підкод": "123749", "сірий колір", "sub-sub-arts": [ { "created_at": 1678184483, "modified_at": 1678184483, "код": "12879", "size": "звисає", "currency": "євро", «поточна ціна»: 40 }, { "created_at": 1678184483, "modified_at": 1678184483, "код": "12876", "size": "підігнаний", "currency": "євро", «поточна ціна»: 35 } ] } ] }

Запит jq на кшталт .subArts[]|select(.subSubArts[].size|contains("snug")) дасть вам усі підстатті, які мають підпідстаттю з розміром "snug" . Виконання подібної команди для 10-гігабайтного файлу JSON зайняло близько трьох хвилин, що не дуже добре, особливо якщо ви нетерплячі (як я).

На щастя, ми можемо пришвидшити це, якщо маємо інформацію про структуру вхідного файлу (очевидно, ми знаємо, що формат JSON). Ми використовуємо jq як фільтр для окремих об’єктів JSON, що означає, що ми повинні мати можливість ефективно розпаралелювати пошуковий вираз. Щоразу, коли мені потрібно запускати команди оболонки паралельно, я використовую GNU parallel, який може обробляти ко...

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow