91cc07df03edfaed810727c206aaecda.jpg

Анализ JSON для больших объемов данных: баланс скорости, памяти и масштабируемости

Сравнительный анализ библиотек JSON для больших полезных нагрузок

Делиться

91cc07df03edfaed810727c206aaecda

Введение

Представьте, что маркетинговая кампания, которую вы организовали к «Чёрной пятнице», имела огромный успех, и клиенты хлынули на ваш сайт. Ваша конфигурация Mixpanel, которая обычно обрабатывает около 1000 событий с клиентами в час, в итоге обрабатывает миллионы событий с клиентами в течение часа. Таким образом, ваш конвейер данных теперь должен обрабатывать огромные объёмы JSON-данных и сохранять их в базе данных. Вы видите, что ваша стандартная библиотека для парсинга JSON не способна масштабироваться в соответствии с резким ростом объёма данных, и ваши аналитические отчёты, работающие практически в режиме реального времени, отстают. Именно здесь вы осознаёте важность эффективной библиотеки для парсинга JSON. Помимо обработки больших объёмов данных, библиотеки для парсинга JSON должны уметь сериализовать и десериализовать JSON-данные с высокой степенью вложенности.

В этой статье мы рассмотрим библиотеки Python для парсинга больших объёмов данных. Мы уделим особое внимание возможностям ujson, orjson и ijson. Затем мы проведём сравнительный анализ производительности сериализации и десериализации стандартной библиотеки JSON (stdlib/json), ujson и orjson. Поскольку в статье мы используем термины «сериализация» и «десериализация», освежим в памяти эти понятия. Сериализация подразумевает преобразование объектов Python в строку JSON, тогда как десериализация подразумевает пересоздание строки JSON из структур данных Python.

По мере прочтения статьи вы найдёте диаграмму процесса принятия решений, которая поможет вам выбрать парсер, исходя из вашего рабочего процесса и индивидуальных потребностей в парсинге. Кроме того, мы рассмотрим NDJSON и библиотеки для парсинга полезных данных NDJSON. Итак, приступим.

Stdlib JSON

Stdlib JSON поддерживает сериализацию всех основных типов данных Python, включая словари, списки и кортежи. При вызове функции json.loads() весь JSON-файл загружается в память одновременно. Это приемлемо для небольших объёмов данных, но для более крупных объёмов данных json.loads() может вызывать критические проблемы с производительностью, такие как ошибки нехватки памяти и остановку последующих рабочих процессов.

import json with open(«large_payload.json», «r») as f: json_data = json.loads(f) #загружает весь файл в память, все токены одновременно

айсон

Для данных объёмом порядка сотен мегабайт рекомендуется использовать ijson. ijson (сокращение от «iterative json») считывает файлы по одному токену за раз, не занимая при этом память. В приведённом ниже коде мы сравниваем json и ijson.

#Библиотека ijson считывает записи по одному токену за раз import ijson with open(«json_data.json», «r») as f: for record in ijson.items(f, «items.item»): #извлечь один словарь из массива process(record)

Как видите, ijson извлекает по одному элементу из JSON и загружает их в объект словаря Python. Затем он передаётся вызывающей функции, в данном случае функции process(record). Общая схема работы ijson показана на иллюстрации ниже.

918f7fbf646b85fb2c29842332238146

уйсон

793732ab53e7283c7c3d2bf0bd247803

Библиотека Ujson широко используется во многих приложениях, работающих с большими объёмами данных JSON, поскольку она была разработана как более быстрая альтернатива стандартной библиотеке JSON в Python. Скорость парсинга очень высокая, поскольку базовый код ujson написан на языке C с привязками Python, подключаемыми к интерфейсу Python. Области, требующие улучшения в стандартной библиотеке JSON, были оптимизированы в Ujson для скорости и производительности. Однако Ujson больше не используется в новых проектах, поскольку сами создатели библиотеки упомянули на PyPI, что она переведена в режим «только для обслуживания». Ниже представлена иллюстрация процессов ujson на высоком уровне.

import ujson taxonomy_data = '{«id»:1, «genus»:»Thylacinus», «species»:»cynocephalus», «extinct»: true}' data_dict = ujson.loads(taxonomy_data) #Десериализация с помощью open(«taxonomy_data.json», «w») как fh: #Сериализация ujson.dump(data_dict, fh) с помощью open(«taxonomy_data.json», «r») как fh: #Десериализация data = ujson.load(fh) print(data)

Переходим к следующей потенциальной библиотеке под названием «orjson».

орджсон

Поскольку Orjson написан на Rust, он оптимизирован не только для скорости, но и обладает безопасными для памяти механизмами для предотвращения переполнений буфера, с которыми сталкиваются разработчики при использовании JSON-библиотек на языке C, таких как ujson. Более того, Orjson поддерживает сериализацию нескольких дополнительных типов данных помимо стандартных типов данных Python, включая объекты dataclass и datetime. Ещё одно ключевое отличие orjson от других библиотек заключается в том, что функция dumps() в orjson возвращает объект bytes, тогда как другие библиотеки возвращают строку. Возврат данных в виде объекта bytes — одна из основных причин высокой производительности orjson.

import orjson book_payload = '{«id»:1,»name»:»Великий Гэтсби»,»author»:»Ф. Скотт Фицджеральд»,»Издательство»:»Сыновья Чарльза Скрибнера»}' data_dict = orjson.loads(book_payload) #Десериализует print(data_dict) с open(«book_data.json», «wb») как f: #Сериализует f.write(orjson.dumps(data_dict)) #Возвращает объект bytes с open(«book_data.json», «rb») как f:#Десериализует book_data = orjson.loads(f.read()) print(book_data)

Теперь, когда мы изучили некоторые библиотеки анализа JSON, давайте протестируем их возможности сериализации.

Тестирование возможностей сериализации JSON, ujson и orjson

Мы создаем пример объекта dataclass с целым числом, строкой и переменной datetime.

из dataclasses импорт dataclass из datetime импорт datetime @dataclass класс Пользователь: id: int имя: str создан: datetime u = Пользователь(id=1, name=»Thomas», created=datetime.now())

Затем мы передаём его каждой из библиотек, чтобы посмотреть, что произойдёт. Начнём с JSON-файла stdlib.

import json try: print(«json:», json.dumps(u)) except TypeError as e: print(«json error:», e)

Как и ожидалось, мы получаем следующую ошибку. (Стандартная библиотека JSON не поддерживает сериализацию объектов «dataclass» и объектов datetime.)

b5ce950f64311e69131128b28f3ec6e5

Далее мы проверим то же самое с помощью библиотеки ujson.

import ujson try: print(«json:», ujson.dumps(u)) except TypeError as e: print(«json error:», e)

c2bd62469c439c34c5d2700d6f28b967

Как видно выше, ujson не может сериализовать объект класса данных и тип данных datetime. Поэтому для сериализации мы используем библиотеку orjson.

import orjson try: print(«orjson:», orjson.dumps(u)) except TypeError as e: print(«orjson error:», e)

Мы видим, что orjson смог сериализовать как типы данных dataclass, так и datetime.

8fd6e939aa03f83d38a57ca3c3cde521

Работа с NDJSON (особое упоминание)

Мы рассмотрели библиотеки для парсинга JSON, но что насчёт NDJSON? NDJSON (Newline Delimited JSON), как вы, возможно, знаете, — это формат, в котором каждая строка представляет собой JSON-объект. Другими словами, разделителем служит не запятая, а символ перевода строки. Например, вот как выглядит NDJSON.

{«id»: «A13434», «name»: «Элла»} {«id»: «A13455», «name»: «Шармон»} {«id»: «B32434», «name»: «Ареида»}

NDJSON в основном используется для журналов и потоковых данных, поэтому полезные данные NDJSON отлично подходят для анализа с помощью библиотеки ijson. Для небольших и средних объёмов данных NDJSON рекомендуется использовать библиотеку stdlib JSON. Помимо ijson и stdlib JSON, существует специальная библиотека NDJSON. Ниже приведены фрагменты кода, демонстрирующие каждый из подходов.

NDJSON с использованием stdlib JSON и ijson

Поскольку NDJSON не разделён запятыми, он не подходит для массовой загрузки, поскольку stdlib json ожидает увидеть список словарей. Другими словами, парсер stdlib JSON ищет один допустимый элемент JSON, но вместо этого получает несколько элементов JSON в файле полезной нагрузки. Следовательно, файл необходимо анализировать итеративно, построчно, и отправлять вызывающей функции для дальнейшей обработки.

import json ndjson_payload = «»»{«id»: «A13434», «name»: «Элла»} {«id»: «A13455», «name»: «Шармон»} {«id»: «B32434», «name»: «Арейда»}»»» #Запись файла NDJSON с помощью open(«json_lib.ndjson», «w», encoding=»utf-8″) as fh: for line in ndjson_payload.splitlines(): #Разделение строки на объект JSON fh.write(line.strip() + «n») #Запись каждого объекта JSON в виде строки #Чтение файла NDJSON с помощью json.loads с помощью open(«json_lib.ndjson», «r», encoding=»utf-8″) as fh: for line in fh: if line.strip(): #Удаление новых строк item= json.loads(line) #Десериализуем print(item) #или передаем его вызывающей функции

В ijson парсинг выполняется так, как показано ниже. В стандартном JSON у нас есть только один корневой элемент, который представляет собой либо словарь, если это отдельный JSON, либо массив, если это список словарей. Но в NDJSON каждая строка является отдельным корневым элементом. Аргумент «» в ijson.items() указывает парсеру ijson на необходимость проверки каждого корневого элемента. Аргументы «» и multiple_values=True сообщают парсеру ijson о наличии в файле нескольких корневых элементов JSON и необходимости извлекать по одной строке (каждый JSON) за раз.

import ijson ndjson_payload = «»»{«id»: «A13434», «name»: «Элла»} {«id»: «A13455», «name»: «Чармонт»} {«id»: «B32434», «name»: «Арейда»}»»» #Запись полезной нагрузки в файл для обработки ijson with open(«ijson_lib.ndjson», «w», encoding=»utf-8″) as fh: fh.write(ndjson_payload) with open(«ijson_lib.ndjson», «r», encoding=»utf-8″) as fh: for item in ijson.items(fh, «», multiple_values=True): print(item)

Наконец, у нас есть специальная библиотека NDJSON. Она, по сути, преобразует формат NDJSON в стандартный JSON.

import ndjson ndjson_payload = «»»{«id»: «A13434», «name»: «Ella»} {«id»: «A13455», «name»: «Charmont»} {«id»: «B32434», «name»: «Areida»}»»» #запись полезной нагрузки в файл для обработки ijson с помощью open(«ndjson_lib.ndjson», «w», encoding=»utf-8″) как fh: fh.write(ndjson_payload) с помощью open(«ndjson_lib.ndjson», «r», encoding=»utf-8″) как fh: ndjson_data = ndjson.load(fh) #возвращает список словарей

Как вы видели, файлы формата NDJSON обычно можно анализировать с помощью библиотеки json и ijson из stdlib. Для очень больших объёмов данных ijson — лучший выбор, поскольку он эффективно использует память. Но если вы хотите генерировать данные NDJSON из других объектов Python, библиотека NDJSON — идеальный выбор. Это связано с тем, что функция ndjson.dumps() автоматически преобразует объекты Python в формат NDJSON, не перебирая эти структуры данных.

Теперь, когда мы изучили NDJSON, давайте вернемся к сравнительному анализу библиотек stdlib json, ujson и orjson.

Причина, по которой IJSON не рассматривается для бенчмаркинга

Потоковый парсер ijson сильно отличается от анализаторов массового анализа, которые мы рассматривали. Если бы мы сравнивали ijson с этими парсерами массового анализа, мы бы сравнивали яблоки с апельсинами. Даже если бы мы сравнивали ijson с другими парсерами, у нас сложилось бы ложное впечатление, что ijson самый медленный, хотя на самом деле он служит совершенно другой цели. ijson оптимизирован для эффективного использования памяти и, следовательно, имеет более низкую пропускную способность, чем парсеры массового анализа.

Создание синтетической полезной нагрузки JSON для целей сравнительного анализа

Мы генерируем большой синтетический JSON-файл, содержащий 1 миллион записей, используя библиотеку «mimesis». Эти данные будут использоваться для бенчмаркинга библиотек. Приведённый ниже код можно использовать для создания полезной нагрузки для этого бенчмаркинга, если вы захотите повторить его. Размер сгенерированного файла составит от 100 до 150 МБ, что, на мой взгляд, достаточно для проведения бенчмаркинга.

from mimesis import Person, Address import json person_name = Person(«en») complete_address = Address(«en») # потоковая передача в файл с помощью open(«large_payload.json», «w») as fh: fh.write(«[«) # массив JSON for i in range(1_000_000): payload = { «id»: person_name.identifier(), «name»: person_name.full_name(), «email»: person_name.email(), «address»: { «street»: complete_address.street_name(), «city»: complete_address.city(), «postal_code»: complete_address.postal_code() } } json.dump(payload, fh) if i < 999_999: # Чтобы избежать запятой в последней записи fh.write(",") fh.write("]") # конец массива JSON

Ниже представлен пример того, как будут выглядеть сгенерированные данные. Как видите, поля адреса вложены друг в друга, чтобы JSON не только был большим по размеру, но и соответствовал реальным иерархическим JSON-данным.

[ { «id»: «8177», «name»: «Уильям Хейс», «email»: «[email protected]», «address»: { «street»: «Изумрудная бухта», «city»: «Краун-Пойнт», «postal_code»: «58293» } }, { «id»: «5931», «name»: «Куинн Грир», «email»: «[email protected]», «address»: { «street»: «Олон», «city»: «Бриджпорт», «postal_code»: «92982» } } ]

Начнем с бенчмаркинга.

Предпосылки для сравнительного анализа

Мы используем функцию read() для сохранения JSON-файла в виде строки. Затем мы используем функцию loads() в каждой из библиотек (json, ujson и orjson) для десериализации JSON-строки в объект Python. Сначала мы создаём объект payload_str из исходного JSON-текста.

с открытым(«large_payload1.json», «r») как fh: payload_str = fh.read() #необработанный текст JSON

Затем мы создаём функцию бенчмаркинга с двумя аргументами. Первый аргумент — это тестируемая функция. В данном случае это функция loads(). Второй аргумент — это payload_str, сконструированная из файла выше.

def benchmark_load(func, payload_str): start = time.perf_counter() for _ in range(3): func(payload_str) end = time.perf_counter() return end — start

Мы используем указанную выше функцию для проверки скорости сериализации и десериализации.

Сравнительный анализ скорости десериализации

Мы загружаем три тестируемые библиотеки. Затем мы запускаем функцию benchmark_load() для функции loads() каждой из этих библиотек.

импорт json, ujson, orjson, время результаты = { «json.loads»: benchmark_load(json.loads, payload_str), «ujson.loads»: benchmark_load(ujson.loads, payload_str), «orjson.loads»: benchmark_load(orjson.loads, payload_str), } для lib, t в results.items(): print(f»{lib}: {t:.4f} секунд»)

Как мы видим, наименьшее время десериализации потребовалось orjson.

cc100e35c166c734ab8126784b644132

Сравнительный анализ скорости сериализации

Далее мы тестируем скорость сериализации этих библиотек.

импорт json импорт ujson импорт orjson импорт время результаты = { «json.dumps»: benchmark(«json», json.dumps, payload_str), «ujson.dumps»: benchmark(«ujson», ujson.dumps, payload_str), «orjson.dumps»: benchmark(«orjson», orjson.dumps, payload_str), } для lib, t в results.items(): print(f»{lib}: {t:.4f} секунд»)

Сравнивая время выполнения, мы видим, что orjson тратит наименьшее количество времени на сериализацию объектов Python в объект JSON.

fc160b7ee687d76f692688c1b51acb57

Выбор лучшей библиотеки JSON для вашего рабочего процесса

7d7b0985e2ae4f7977c7fa0f36a10e30

Хаки для буфера обмена и рабочих процессов JSON

Предположим, вы хотите просмотреть JSON-файл в текстовом редакторе, например, Notepad++, или поделиться фрагментом (из большого объёма данных) в Slack с коллегой по команде. Вы быстро столкнётесь со сбоями буфера обмена, текстового редактора или IDE. В таких ситуациях можно использовать Pyperclip или Tkinter. Pyperclip хорошо подходит для данных размером до 50 МБ, тогда как Tkinter — для данных среднего размера. Для больших объёмов данных можно записать JSON-файл для просмотра данных.

Заключение

JSON может показаться простым, но чем больше объём полезной нагрузки и чем выше уровень вложенности, тем быстрее эти нагрузки могут стать узким местом производительности. Цель этой статьи — показать, как каждая библиотека для парсинга Python решает эту проблему. При выборе библиотек для парсинга JSON скорость и пропускная способность не всегда являются главными критериями. Именно рабочий процесс определяет, требуется ли для парсинга полезной нагрузки пропускная способность, эффективность использования памяти или долгосрочная масштабируемость. Короче говоря, парсинг JSON не должен быть универсальным подходом.

Источник: towardsdatascience.com

✅ Найденные теги: Анализ, новости

ОСТАВЬТЕ СВОЙ КОММЕНТАРИЙ

Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Новое светоактивируемое покрытие способно убивать стойкие микробы
Добро пожаловать на темную сторону мечты о криптовалютах, где не требуется никаких разрешений.
Анонс программы «Чудо техники» на 15 марта 2026
ИИ-микрофон Echomic превращает речь в текст
Методология облучения 1-гексаноловых растворов: этапы исследования и анализ.
Agentic RAG против Classic RAG: от конвейера к контуру управления
Uber, Wayve и Nissan планируют запустить сервис роботакси в Токио в этом году.
ideipro logotyp
Врачи играют все более важную роль в оценке инструментов искусственного интеллекта для здравоохранения | MobiHealthNews
Image Not Found
Добро пожаловать на темную сторону мечты о криптовалютах, где не требуется никаких разрешений.

Добро пожаловать на темную сторону мечты о криптовалютах, где не требуется никаких разрешений.

Жан-Поль Торбьорнсен — лидер THORChain, блокчейна, который, как предполагалось, не должен иметь лидеров, и который сейчас переживает череду дорогостоящих скандалов. 18 февраля 2026 г. Каган Маклеод «Сейчас мы вне воздушного пространства. Мы можем делать все, что захотим»,…

Мар 13, 2026
Анонс программы «Чудо техники» на 15 марта 2026

Анонс программы «Чудо техники» на 15 марта 2026

В новом выпуске программы «Чудо техники» с Сергеем Малозёмовым»: Гены подскажут: кому грозит болезнь Альцгеймера и как её распознать на ранних стадиях? Правда ли питаться готовой едой выгоднее, чем домашней? Как работает кухня доставки?  В рубрике «Чудо-товары»…

Мар 13, 2026
ИИ-микрофон Echomic превращает речь в текст

ИИ-микрофон Echomic превращает речь в текст

Смарт-микрофон Echomic с искусственным интеллектом — это удобный инструмент для записи голоса, преобразования его в текст и управления мультимедийными задачами.  Новинка спроектирована как компактный микрофон, которая крепится на одежду, чтобы захватывать голос чисто и без лишнего шума.…

Мар 13, 2026
Методология облучения 1-гексаноловых растворов: этапы исследования и анализ.

Разработан подход к выявлению облученных пищевых продуктов

Этапы исследования © Molecules Учёные НИИ ядерной физики, физического и химического факультетов МГУ изучили влияние ионизирующего излучения на образование летучих органических соединений, содержащихся в продуктах питания. Исследование имеет важное значение для разработки уникальных биохимических маркеров радиационной обработки…

Мар 13, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых