
Можете ли вы исправить файл Protobuf? Не совсем - и вот почему
25 июня 2025 г.В мире высокопроизводительных распределенных систем рамки сериализации данных, такие какБуферы протокола Google (Protobuf)необходимы. Они предлагают компактные бинарные форматы и эффективный анализ, что делает их идеальными для всего, от межпроводной связи до постоянного хранения данных. Но когда дело доходит до обновления лишь небольшой части уже сериализованного капля данных, возникает общий вопрос: можем ли мы «исправить» его напрямую, избегая накладных расходов, изменяя и переписывая все это?
Краткий ответ, для большинства практических целей, являетсянетПолем В то время как Protobuf предоставляет умные механизмы, которые, кажется, предлагают прямое исправление, реальность более нюансирована. Давайте погрузимся в то, почему полный цикл «модификации чтения-модификации» остается в значительной степени неизбежным и где находится истинная эффективность.
Основная задача: Нефиксированный характер бинарных данных
Представьте себе книгу, в которой может измениться длина каждого слова, и нет фиксированных номеров страниц для отдельных слов. Если вы измените одно слово, все последующие слова на этой странице (и, возможно, вся книга) сдвинутся, что требует полного повторного промежутка. Это сродни проблеме исправления бинарной сериализованной капли.
Protobuf, как Apache Thrift, используетКомпактная бинарная кодировка с переменной длинойПолем Поля идентифицируются с помощью уникальных числовых тегов, а их значения кодируются эффективно, часто с целыми числами с переменной длиной или строками, наполненными длиной. Этот дизайн фантастический для минимизации размера данных и максимизации скорости анализа. Однако это означает, что точное смещение байта и длина любого данного поля не фиксированы. Изменение значения поля, особенно строки, может изменить свою длину байта, которая затем сдвигает позиции всех последующих полей в двоичном потоке. ПопыткаМодификация "на месте"Без пересчитывания и смещения все последующие байты приведут к повреждению данных.
Заблуждение 1: «Последнее поле побеждает» волшебный трюк
Одной интригующей особенностью буферов протокола является его«Последнее поле выигрывает» поведение слиянияДля не повторных полей. Это означает, что если у вас есть два сериализованные сообщения Protobuf для одного и того же типа, и вы объединяете их бинарные формы, когда комбинированный поток детериализируется, будет использоваться значение не повторного поля в последнем случае в потоке. Для повторных полей добавляются новые значения, а не перезаписаны.
Как это работает (и почему это вводит в заблуждение за исправление):
Допустим, у вас есть оригиналPerson
объект сериализован в каплей:
Original Blob: [name="Alice", age=30, phone_number=["111", "222"]]
Вы хотите обновить только имя «Алисии». Вы можете создать новое, небольшое сообщение Protobuf, содержащее только обновленное имя:
Patch Blob: [name="Alicia"]
Тогда вы могли бы объединить этоPatch Blob
вOriginal Blob
:
Combined Blob: [name="Alice", age=30, phone_number=["111", "222"]] + [name="Alicia"]
Когда протокуф -анализатор читает этоCombined Blob
, из -за «Последних побед», имя действительно решится на «Алисию», покаage
иphone_number
сохранит свои исходные значения.
Улов: хотя это кажется патч, этоПравило десериализации, не бинарный механизм исправления. Парсер по -прежнему должен читать и обрабатывать весь конкатенированный поток, чтобы определить окончательное состояние сообщения. Вы не избежали стоимости десериализации; Вы только что изменили то, как синтаксический анализатор разрешает конфликты во время десериализации.
Кроме того, этот подход имеет серьезные ограничения:
- Только для корневых объектов и не повторных полей:Это «хорошо работает только для корневого объекта» и «не работает для повторных» полей. Если вы попытались обновить конкретный номер телефона или поле в пределах вложенного сообщения, этот трюк с конкатенацией потерпит неудачу или приведет к непреднамеренным добавлениям.
- Увеличенный размер хранения/передачи:Сейчас вы храните или передаете больше данных (оригинал + патч), чем если бы вы просто повторно сериализовали весь объект.
Заблуждение 2: FieldMask сохраняет стоимость повторной сериализации
Официальные лучшие практики Google Protobuf рекомендуют использоватьFieldmaskДля поддержки частичных обновлений в API. Это отличная схема, но очень важно понять, где его эффективность действительно лежит.
Как работает Fieldmask:
АFieldMask
является отдельным сообщением Protobuf, которое явно перечисляет пути полей, которые клиент намеревается изменить (например,, например,name
Вaddress.street
) Когда клиент хочет обновить ресурс, он отправляет небольшой запрос, содержащий:
- А
FieldMask
сам - Только частичные данные для полей, указанных в маске.
Пример полезной нагрузки сети с использованиемFieldMask
:
Вместо отправки:
{ "name": "Alicia", "age": 30, "phone_number": ["111", "222"] } // (full object)
Клиент может отправить:
{ "update_mask": { "paths": ["name"] }, "person": { "name": "Alicia" } } // (much smaller payload)
ГдеFieldMask
Поистине сияет (и почему повторная сериализация все еще необходима):
FieldMask
Значительно повышает эффективность, но не из-за избегания цикла десериализации/повторной сериализации на постоянных данных сервера. Его преимущества в основном всетевая коммуникацияиприложения логики слоев:
- Оптимизация полосы пропускания:Отправив только
FieldMask
и частичные данные, размер полезной нагрузки запроса резко уменьшается. Это спасает пропускную способность сети, особенно важную для мобильных клиентов или API с большим объемом. - Уменьшенная обработка на стороне сервера:Сервер получает явные инструкции, по которым поля обновляются. Это упрощает логику приложения, не позволяя серверу вывозить изменения или обрабатывать большой объект, где большинство полей не изменяются.
Однако, как только сервер получает этот запрос на частичное обновление, чтобы применить его к хранимым сериализованным данным, он все еще выполняет следующие шаги:
- Получить существующие данные:Сервер получает полную, существующую сериализованную каплей из хранилища.
- Десериализовать:Весь капля детериализируется в полный объект Protobuf в памяти.
- Применить патч:Логика приложения использует
FieldMask
обновить только указанные поля на этом объекте в памяти. - Повторно провести:Весь модифицированный объект в памяти затем повторно сериализуется в новый бинарный каплей.
- Сопротивляться:Эта новая капля заменяет старый в хранении.
Неизбежная правда: читая модификация-записи
Для любого надежного и надежного модификации протокола буфера сериализовано капля данных,Цикл чтения-модификации-записиявляется стандартным и необходимым подходом. Это потому, что::
- Целостность данных:Это гарантирует, что весь объект остается последовательным и правильно закодированным после модификации.
- Эволюция схемы:Он изящно обрабатывает изменения схемы (добавление/удаление полей), позволяя парсеру правильно интерпретировать полную структуру данных.
- Двоичные ограничения формата:Характер кодировки Protobuf в длине длины делает прямые манипуляции на уровне байта нецелесообразными и подвержены коррупции.
Заключение
Буферы протокола невероятно мощны для эффективной сериализации данных и эволюции схемы. Функции, как"Последнее поле выигрывает"иFieldmaskценные инструменты, но их полезность для «исправления» существующих сериализованных каплей часто неправильно понимается.
Поведение «Последнее поля»-это правило десериализации, которое можно использовать для простых, не повторных обновлений поля посредством конкатенации, но оно по-прежнему требует полной десериализации и не является бинарным решением для общего назначения.
АFieldMask
является отличным шаблоном дизайна API, который оптимизирует пропускную способность сети и упрощает логику приложений для частичных обновлений, но сервер по-прежнему выполняет полный цикл считывания-модификации-записи на базовых данных.
В конечном счете, если вам нужно изменить сериализованный каплей Protobuf, подготовиться к полнойЧитающий модификатортанец Истинная эффективность заключается в том, чтобы оптимизировать передачу патча (например, сFieldMask
и обработка в памяти, а не волшебно изменяя байты на диске.
Дальнейшее чтение
- Протокол буферов документация
- Руководство по языку Protobuf (Proto3)
- Google Protobuf FieldMask
- Google Cloud API Руководство по проектированию - частичные обновления
Оригинал