Процессы сериализации и десериализации данных. Форматы сериализации

Введение в сериализацию

Что такое сериализация и десериализация?

Сериализация (маршалинг) — это процесс преобразования структуры данных или объекта в формат (текстовый или бинарный), пригодный для хранения или передачи (например, в файл, по сети или в базу данных) в рамках взаимодействия между различными системами и/или языками программирования. Форматы сериализации бывают в виде текста (например, JSON, XML, YAML) или в бинарном виде (например, Protobuf, Avro, Thrift, BSON).

Главная цель сериализации: Сделать сложную структуру данных переносимой и пригодной к восстановлению, независимо от среды исполнения (язык, платформа, система).

Десериализация (демаршалинг) — это процесс реконструкции структуры данных или объекта из его сериализованной формы. Он включает в себя интерпретацию сериализованных данных и создание эквивалентного объекта или структуры данных.

Цель десериализации: Получить рабочую структуру данных из сохранённого/переданного формата и использовать её в коде на любом языке.

Почему сериализация важна

  • Унификация данных: Форматы сериализации создают единый стандарт, который понимают разные языки и системы.
  • Межъязыковое взаимодействие: Службы на Python, Java, Go и других языках могут обмениваться данными, если они сериализованы в понятный им формат (например, JSON, Protobuf).
  • Устойчивость к хранению: Сериализация позволяет сохранять состояние объектов (например, в файлах, кэше или базах) и восстанавливать их позже.
  • Гибкость: можно сериализовать — примитивы (строки, числа), сложные структуры (списки, словари, объекты), и даже связи между объектами (в некоторых форматах, например Pickle или Thrift).

Сериализация vs Маршалинг

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

Точное значение каждого термина зависит от того, кого вы спрашиваете. Например, программисты Java склонны использовать слово «маршалинг» в контексте удаленного вызова методов (RMI). В Python «маршалинг» относится почти исключительно к формату, используемому для хранения скомпилированных инструкций байт-кода.

Статья на вики «Marshalling Comparison with serialization».

Как работает сериализация: из структуры — в байты/текст — обратно

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

Когда нужно использовать эти данные снова — они десериализуются, то есть преобразуются обратно в структуру, понятную языку программирования.

Обобщённая схема:

Форматы сериализации

В распоряжении инженеров данных имеется множество алгоритмов и форматов сериализации. Хотя обилие вариантов становится серьезным источником проблем при выполнении инженерии данных, они также открывают широкие возможности для повышения производительности. Мы сталкивались с ситуациями, когда производительность работы повышалась в 100 раз просто за счет перехода сериализации от CSV к Parquet. По мере продвижения данных по конвейеру инженеры также будут управлять повторной сериализацией — преобразованием данных из одного формата в другой. Иногда инженерам данных не остается ничего кроме принятия данных в древнем, неприглядном виде. Они должны разработать процессы десериализации этого формата и обработки исключений, а затем очистить и преобразовать данные для согласованной и быстрой последующей обработки и потребления.

Сериализация на основе строк

Как следует из названия, сериализация на основе строк организует данные по строкам. Формат CSV представляет собой архетипический формат на основе строк. Для полуструктурированных данных (объектов данных, поддерживающих вложенность и вариативность схемы) сериализация на основе строк подразумевает хранение каждого объекта как единицы.

CSV: Нестандартный стандарт

Это формат сериализации, заслуживший нелюбовь инженеров данных. Термин CSV (Comma-Separated Values — файлы данных с разделителями-запятыми) — это, по сути, общее обозначение разделенного текста, но существует гибкость в соглашениях об экранировании, символах кавычек, разделителях и т.д.

Инженерам данных следует избегать использования СSV-файлов в конвейерах, поскольку они подвержены большому количеству ошибок и имеют низкую производительность. Инженерам часто приходится использовать формат CSV для обмена данными с неподконтрольными им системами и бизнес-процессами. CSV стал распространенным форматом для архивирования данных. Если вы используете CSV для архивирования, включите полное техническое описание конфигурации сериализации для ваших файлов, чтобы будущие потребители могли получить эти данные.

XML

Расширяемый язык разметки (Extensible Markup Language, XML) был популярен во времена появления HTML и Интернета, но сейчас он рассматривается как устаревший. Он, как правило, медленно десериализуется и сериализуется для приложений из сферы инженерии данных. XML — еще один стандарт, с которым часто приходится взаимодействовать инженерам данных при обмене данными с унаследованными системами и программным обеспечением. JSON в значительной степени заменил XML для сериализации объектов в виде обычного текста.

JSON и JSONL

JavaScript Object Notation (JSON) стал новым стандартом для обмена данными через API, а также чрезвычайно популярным форматом для хранения данных. В контексте баз данных популярность JSON начала быстро расти с появлением MongoDB и других хранилищ документов. Такие базы данных, как Snowtlake, BigQuery и SQL Server, также имеют широкую встроенную поддержку, что облегчает обмен данными между приложениями, API и системами баз данных.

JSON Lines (JSONL) — это специализированная версия JSON для хранения объемных полуструктурированных данных в файлах. JSONL хранит последовательность JSОN-объектов, причем объекты разграничиваются разрывами строк. Чрезвычайно удобный формат для хранения данных сразу после их что JSONL — поглощения из API или приложений. Однако многие столбцовые форматы обеспечивают значительно более высокую производительность. Рассмотрите возможность перехода на другой формат для промежуточных этапов конвейера и предоставления данных.

Пример JSONL:

Avro

Avro — это формат данных на основе строк. Он предназначен для выполнения RPC и сериализации данных. Avro кодирует данные в двоичный формат, а метаданные схемы указываются в формате JSON.

Avro популярен в экосистеме Hadoop, а также поддерживается различными инструментами для работы с облачными данными.

Сериализация столбцов

Рассмотренные до сих пор форматы сериализации ориентированы на работу со строками. Данные кодируются в виде полных отношений (XML и JSON), последовательно записываемых в файлы.
При сериализации столбцов, организация данных, по сути, сводится к хранению каждого столбца в отдельном наборе файлов. Одним из очевидных преимуществ хранения на основе столбцов можно считать возможность считывания данных только из подмножества полей, а не выполнять считывание сразу всех строк. Такой сценарий часто встречается в аналитических приложениях и позволяет значительно сократить объем данных, требующих сканирования для выполнения запроса.

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

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

У хранения на основе столбцов и сжатия есть и определенные недостатки.

Отсутствие возможности легко получить доступ к отдельным записям данных влечет за собой необходимость восстанавливать записи путем считывания данных из нескольких файлов на основе столбцов.

Изменение записей также сопряжено с определенными трудностями. Чтобы изменить одно поле в одной записи, мы должны распаковать файл на основе столбцов, изменить его, снова запаковать и записать обратно в хранилище. Чтобы избежать полной перезаписи столбцов при каждом обновлении, столбцы разбиваются на множество файлов, обычно с использованием стратегий разбиения и кластеризации. Таким образом можно организовать данные в соответствии с шаблонами запросов и изменений для таблицы. Но даже в этом случае накладные расходы на обновление одной строки просто чудовищны.

Базы данных на основе столбцов плохо подходят для транзакционных рабочих нагрузок, поэтому в транзакционных базах данных обычно используется та или иная форма хранения, ориентированная на строки или записи.

Parquet

Parquet хранит данные в формате столбцов и предназначен для обеспечения отличной производительности чтения и записи в среде озера данных. Формат Parquet решает несколько часто возникающих у инженеров данных проблем.

В отличие от CSV, данные в кодировке Parquet содержат информацию о схеме и поддерживают вложенные данные.

Кроме того, Parquet — переносимый формат: если такие базы данных, как BigQuery и Snowflake, сериализуют данные в собственных форматах на основе столбцов и обеспечивают отличную производительность запросов к данным, хранящимся внутри, то при взаимодействии с внешними инструментами происходит огромный спад производительности. Данные должны быть десериализованы, пересортированы в удобный для обмена формат и экспортированы для использования таких инструментов для работы с озерами данных, как Spark и Presto.

Раrquеt-файлы в озере данных могут оказаться лучшим вариантом по сравнению с запатентованными облачными хранилищами данных в многоязыковой инструментальной среде.
Формат Parquet используется с различными алгоритмами сжатия, особенно популярны оптимизированные по скорости алгоритмы, такие как Snappy (рассматривается далее в этом приложении).

ORC

Optimized Row Columnar (ORC) — это столбцовый формат хранения данных, аналогичный Parquet. ORC был очень популярен в системе Apache Hive. Хотя он по прежнему широко используется, в целом мы видим его гораздо реже, чем Apache Parquet, и он пользуется несколько меньшей поддержкой в современных инструментах облачной экосистемы. Например, Snowtlake и BigQuery поддерживают импорт и экспорт файлов Parquet. Они также могут считывать данные из файлов ORC, но ни один из указанных инструментов не может экспортировать их в этот формат.

Apache Arrow или сериализация в памяти

Программное обеспечение может хранить данные в сложных объектах, разбросанных по памяти и связанных указателями, или в более упорядоченных, плотно упакованных структурах, таких как массивы в Фортране и Си.

Как правило, плотно упакованные структуры данных в памяти ограничивались простыми типами (например, фиксированной ширины (например, строками INT64) или структурами данных фиксированной ширины).

Более сложные структуры (например, JSОN-документы) не могут плотно храниться в памяти и требуют сериализации для хранения и передачи между системами.

Идея фреймворка Apache Arrow 1 заключается в том, чтобы переосмыслить сериализацию,  используя формат данных, подходящий как для обработки в памяти, так и для экспорта.

Это позволяет избежать накладных расходов на сериализацию и десериализацию — мы просто используем один и тот же формат для обработки в памяти, экспорта по сети и долговременного хранения. Arrow базируется на столбцовом хранении, где каждый столбец, по сути, получает свои собственные фрагменты памяти. Для вложенных данных мы используем технику измельчения, помогающую сопоставлять каждое место в схеме JSОN-документов с отдельным столбцом.

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

Одно из очевидных неудобств при таком подходе заключается в том, что разные языки программирования по-разному сериализуют данные. Для решения этой проблемы в рамках проекта Arrow были созданы программные библиотеки для различных языков программирования (включая С, Go, Java, JavaScript, МА TLAB, Python, R и Rust), позволяющие этим языкам взаимодействовать с данными Arrow в памяти.

В некоторых случаях эти библиотеки используют интерфейс между выбранным языком и низкоуровневым кодом на другом языке (например, С) для чтения и записи из Arrow. Это обеспечивает высокую совместимость между языками без дополнительных накладных расходов на сериализацию.

Например, программа на языке Scala может использовать библиотеку Java для записи данных Arrow, а затем передать их в виде сообщения программе на языке Python.

Arrow быстро находит применение в различных популярных фреймворках, таких как Apache Spark. Компания Arrow также выпустила новый продукт для хранения данных — Dremio. Он представляет собой платформу, состоящую из системы запросов и хранилища данных, построенную на основе сериализации Arrow для поддержки быстрых запросов.

Гибридная сериализация

Мы используем термин «гибридная сериализация» для обозначения технологий, сочетающих несколько методов сериализации или интегрирующих сериализацию с дополнительными уровнями абстракции, такими как управление схемами. В качестве примеров приведем Apache Hudi и Apache Iceberg.

Hudi

Hudi означает Hadoop Upserts Deletes Incrementals. Эта технология управления таблицами сочетает в себе несколько методов сериализации, что обеспечивает производительность столбцовых баз данных при аналитических запросах и одновременно поддерживает Hudi — атомарные, транзакционные обновления.

Типичное приложение — это таблица, обновляемая из потока CDC, поступающего из базы данных транзакционного приложения. Поток перехватывается в формате сериализации, ориентированном на строки, а основная часть таблицы сохраняется в столбцовом формате. Запрос работает как со столбцами, так и со строками, возвращая результаты для текущего состояния таблицы. Периодически выполняется процесс переупаковки, объединяющий строковые и столбцовые файлы в обновленные файлы на основе столбцов для повышения эффективности запросов.

lceberg

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

По Iceberg рекомендую статью «Введение в Apache Iceberg. Основы, архитектура, как работает?»

Другие форматы

Protocol Buffers (Protobuf)

Protocol Buffers (или Protobuf) — это бинарный формат сериализации данных, разработанный Google. Он позволяет эффективно и компактно описывать и передавать структурированные данные между программами, особенно в распределённых и микросервисных системах.

Работает это примерно следующим образом:

  • Пишется структура данных в .proto-файле.
  • С помощью protoc (компилятора) генерируются классы для нужного языка.
  • Эти классы позволяют сериализовать и десериализовать данные в бинарный формат.

Пример .proto файла:

FlatBuffers

FlatBuffers — Бинарный формат сериализации от Google, как альтернатива Protobuf.

  • Не требует десериализации — можно читать напрямую из байтовой строки (zero-copy).
  • Рекомендуется использовать в играх, мобильных и встраиваемых приложениях, где важна скорость и низкий overhead.
  • Быстрее всего, но сложнее в использовании и изменении схемы.

CBOR (Concise Binary Object Representation)

CBOR — Бинарная версия JSON, стандартизированная (RFC 8949).

  • Компактнее JSON, но сохраняет его структуру и семантику.
  • Применяется в IoT, веб-протоколы (CoAP), когда нужен JSON‑like формат, но без избыточности.
  • Хороший компромисс: читаемость + компактность + универсальность.

MessagePack

MessagePack — Бинарный формат сериализации, максимально совместимый с JSON по структуре.

  • В 2–5 раз меньше JSON, при этом так же прост для использования.
  • Используется в клиент-серверном общении, кэшировании, REST/gRPC, Redis (поддерживает MessagePack).
  • «JSON, но быстрее и легче» — удобен, если не нужна строгая схема.

pickle

pickle — это встроенный модуль сериализации в Python, предназначенный для сохранения и восстановления объектов Python в байтовом виде.

Используется в следующих сценариях:

  • Быстро сохранить объект Python на диск (например, модель машинного обучения).
  • Кэшировать данные между запусками.
  • Локальная отладка и временные файлы.

BSON (Binary JSON)

BSON — это бинарный формат, разработанный MongoDB. Он расширяет JSON, добавляя поддержку дополнительных типов (например, даты, бинарные данные) и более эффективную машинную обработку.

Рекомендуется применять при:

  • При работе с MongoDB.
  • Если нужен более мощный JSON (с типами, датами, бинарными данными).
  • Для API, где бинарный формат предпочтительнее текстового, но всё ещё нужен JSON‑подобный вид.

Бессхемный против основанного на схеме (Schemaless vs Schema-Based)

Независимо от того, являются ли они текстовыми или бинарными, многие форматы сериализации данных требуют документ схемы , который является формальным описанием ожидаемой структуры сериализованных данных. В то же время некоторые форматы не имеют схемы, в то время как другие могут работать как со схемой, так и без нее:

Подсистема хранения базы данных

Во всех базах данных есть базовый механизм хранения данных. Многие СУБД не представляют свои механизмы хранения данных в виде отдельной абстракции (например, BigQuery, Snowflake). Некоторые из них (в частности, MySQL) поддерживают полностью подключаемые механизмы хранения данных.

Другие (например, SQL Server) предлагают основные варианты конфигурации механизма хранения (столбцовое или строчное хранение), существенно влияющие на поведение базы данных.
Как правило, механизм хранения данных представляет собой отдельный от механизма запросов программный уровень.

Механизм хранения управляет всеми аспектами хранения данных на диске, включая сериализацию, физическое расположение данных и индексы.

В 2000-2010-х годах в подсистемах хранения данных появились значительные инновации. Если раньше подсистемы хранения данных были оптимизированы для прямого доступа к вращающимся дискам, то современные системы гораздо лучше оптимизированы для поддержки характеристик производительности твердотельных накопителей. Подсистемы хранения также обеспечивают улучшенную поддержку современных типов и структур данных, таких как строки переменной длины, массивы и вложенные данные.

Еще одним важным изменением в подсистемах хранения данных стал переход к системам хранения на основе столбцов для приложений аналитики и хранилищ данных. Такие СУБД, как SQL Server, PostgreSQL и MySQL, обеспечивают надежную поддержку хранилищ на основе столбцов.

Сжатие: gzip, bzip2, Snappy и т.д.

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

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

Возможно, с помощью этого наивного приема можно было бы реализовать коэффициент сжатия в соотношении 2 к 1 и более. Алгоритмы сжатия используют более сложные математические методы для выявления и устранения избыточности.

Зачастую они позволяют достичь коэффициента сжатия в соотношении 1О:1 для текстовых данных.

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

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

Традиционные механизмы сжатия, такие как gzip и bzip2, очень хорошо сжимают текстовые данные. Их часто применяют для работы с JSON, JSONL, XML, CSV и другими текстовыми форматами данных.

В последние годы инженеры создали новое поколение алгоритмов сжатия, в которых приоритет отдается скорости и эффективности процессора, а не степени сжатия. Основными примерами служат Snappy, Zstandard, LZFSE и LZ4. Эти алгоритмы часто используются для сжатия в озерах данных или столбчатых базах данных с целью оптимизации быстродействия запросов.

Сценарии использования сериализации

REST API

В веб-приложениях клиент (браузер, мобильное приложение) общается с сервером через HTTP-запросы.

  • Сервер отвечает сериализованными данными — чаще всего в JSON.
  • Клиент десериализует ответ и отображает его.

Пример:

  • Python (FastAPI, Django REST) → JSON
  • Node.js (Express) → JSON
  • Java (Spring) → JSON/XML

Микросервисы

В архитектуре микросервисов данные передаются между сервисами (например, аутентификация, платежи, каталог товаров).

Каждый сервис может быть написан на разном языке

Данные передаются через:

  • REST (JSON)
  • gRPC (Protobuf)
  • Kafka (Avro, JSON, Protobuf)

Пример:

Сервис заказов отправляет сериализованный заказ в сервис оплаты через Kafka в формате Avro или JSON.

Распределённые системы

Системы, разбитые на несколько узлов (кластеров, машин), требуют обмена данными между компонентами.

Пример: база данных (Cassandra, Redis), кэш, очереди (RabbitMQ, Kafka)

Сериализация нужна для:

  • передачи объектов между узлами,
  • репликации,
  • логирования событий.

Пример: Kafka брокер получает сообщение в Protobuf, десериализует его, и передаёт другому сервису.

Big Data и Data Pipelines

В системах обработки больших данных сериализация используется при:

  • Чтении/записи файлов в HDFS или S3 (форматы: Avro, Parquet)
  • Передаче данных через Apache Kafka, Spark, Flink и др.
  • Поддержании схемы данных (schema registry)

Пример: ETL-пайплайн получает CSV, преобразует его в Avro, затем пишет в Parquet на Hadoop. Apache Spark читает Parquet-файл — десериализация.

Литература для дополнительного ознакомления

0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x