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

Основные проблемы существующих решений:

  1. Задержка актуальности данных — в крупных системах с множеством офлайн‑магазинов и онлайн‑площадок информация о наличии товаров может устаревать уже через несколько часов

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

  3. Потеря данных — при использовании простого флага синхронизации возможны пропуски изменений

  4. Низкая производительность — обработка больших объемов данных занимает значительное время

  5. Сложность масштабирования — при росте количества товаров и точек продаж проблема только усугубляется

Например, в системах с 1 млн товаров и 60 офлайн-магазинами передача данных об остатках в интернет-магазин может занимать более суток. За это время информация успевает устареть, что приводит к:

  • Продажам отсутствующих товаров

  • Некорректным данным о наличии

  • Снижению доверия клиентов

  • Увеличению возвратов

Основная концепция

Предложенный подход к обмену данными базируется на использовании единой таблицы синхронизации (sync), которая позволяет управлять синхронизацией всех объектов системы. Таблица содержит три ключевых поля:

  • object_type — тип объекта (позволяет использовать одну таблицу для всех сущностей системы)

  • id — идентификатор объекта

  • sync_updated — строка, содержащая маркер последней успешной синхронизации (может быть временной меткой, хешем или версией)

Механизм работы

Процесс синхронизации осуществляется следующим образом:

  1. Формирование выгрузки происходит путем объединения основной таблицы с таблицей sync. В выгрузку попадают записи, удовлетворяющие одному из условий:

    • sync_updated = null (объект никогда не синхронизировался)

    • sync_updated ≠ updated (объект был изменен после последней синхронизации)

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

  3. После успешной обработки на стороне сервера в ответ возвращается информация о:

    • Успешно обработанных объектах (id, updated)

    • Ошибках синхронизации для каждого элемента (id, updated, error_message)

  4. При получении ответа система обновляет поле sync_updated в таблице синхронизации значениями поля updated, которые пришли в ответе сервера. Это гарантирует, что в случае изменения объекта во время обработки на сервере, запись снова попадет в очередь синхронизации при следующем цикле.

Преимущества подхода

  1. Оптимизация трафика - передаются только измененные данные

  2. Надежность синхронизации - система не пропускает изменения даже при одновременной обработке

  3. Универсальность - не требует модификации существующих таблиц данных (при условии, что там уже есть время изменения в любом формате)

  4. Минимализм - достаточно одной таблицы для синхронизации всех объектов системы

  5. Гибкость - в качестве маркера версии может использоваться:

    • Временная метка

    • Версия объекта

    • Хеш значимых полей

    • Любое другое подходящее значение

Обработка ошибок

При возникновении ошибок синхронизации система получает детальный отчет по каждому объекту, что позволяет:

  • Корректно обработать исключительные ситуации

  • Провести повторный обмен только для проблемных записей

  • Сохранить целостность данных при частичном успехе операции

Заключение

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

Практический пример показывает, что внедрение такого подхода позволяет:

  • Сократить время синхронизации данных

  • Обеспечить актуальность информации в режиме реального времени

  • Снизить нагрузку на сеть

  • Повысить точность данных о наличии товаров

  • Улучшить клиентский опыт

Технические детали реализации

Структура таблицы sync:

CREATE TABLE sync (
 id INT PRIMARY KEY,
 object_id INT, // идентфикатор объекта
 object_type VARCHAR(255), // тип объекта
 sync_updated VARCHAR(255), // хэш/дата изменений
 UNIQUE (object_id, object_type)
);

Запрос для формирования выгрузки:

SELECT t.* 
FROM main_table t
LEFT JOIN sync s ON t.id = s.object_id AND s.object_type = 'main_table',
WHERE s.sync_updated IS NULL OR s.sync_updated <> t.updated;

Пример ответа сервера:

{
    "success": {
        "id": 123,
        "updated": "2025-06-17T19:33:50"
    },
    "errors": {
        "id": 456,
        "updated": "2025-06-17T19:33:50",
        "error_message": "Ошибка обработки данных"
    }
}

Пример обработки ответа

// с использованием INSERT... ON DUPLICATE KEY UPDATE

foreach ($response['success'] as $item) {

$sql = "INSERT INTO sync (object_id, object_type, sync_updated)

VALUES (:object_id, :object_type, :updated)

ON DUPLICATE KEY UPDATE sync_updated = :updated";

$stmt = $db->prepare($sql);

$stmt->execute([

':object_id' => $item['object_id'],

':object_type' => 'main_table', // тип объекта

':updated' => $item['updated']

]);

}

// Обработка ошибок

foreach ($response['errors'] as $error) {

logError($error['error_message']);

scheduleRetry($error['id']);

}

Дополнительно

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

Комментарии (4)


  1. savostin
    21.06.2025 12:09

    Складываете изменения в Kafka и забираете когда и кому нужно...

    в системах с 1 млн товаров и 60 офлайн-магазинами передача данных об остатках в интернет-магазин может занимать более суток

    Я не специалист в таких системах, но что это за SQL запросы такие гигантские? Имхо, обновление поля "остаток" 60 миллионов строк должно занимать, ну минут 5... Если конечно не открывать отдельный коннект и транзакцию на каждую цифру.


    1. antirius Автор
      21.06.2025 12:09

      Пример: бэкофис со своим сервером в офисе, онлайн-магазин в ДЦ. Обновление по API. API дальше может уже складывать себе в Кафку. В таком контексте тут уже не просто SQL запросы.

      Еще сталкивался с таким.. даже не знаю как назвать).. в BI систему перезагружали вообще все данные за последние пару дней... А если документ и источнике был изменен более ранним числом, перезагружали данные за весь этот период (по отдельному запросу пользователей, когда данные переставали сходиться).


      1. savostin
        21.06.2025 12:09

        Имхо, batch insert/update нужно делать подготовленным файлом, например CSV, и делать его load средствами базы. Конечно, в каждом конкретном случае свое решение, но на каждое обновление дергать API - не рационально.


        1. antirius Автор
          21.06.2025 12:09

          Ну вот складская программа передает остатки на сайт.. тут никуда особо не денешься)
          И как раз тут надо передавать данные пакетами, хотя бы раз в минуту (чтобы накопить данные, а не по одной записи через интернет гонять), предложенное решение в статье как раз делается на стороне wms.
          А на сайте, уже надо думать как правильно записывать в базу этот пакет (и в Вашем комментарии как раз один из вариантов)... если текущая статья зайдет, попробую один из вариантов быстрой записи пакета расписать в следующей статье.
          Для профи что текущая статья, что правильная запись пакетов - это само собой разумеющееся, но ведь полно тех, кто такую задачу решает в первый раз и тут есть вероятность, что такие простые статьи помогут)