В ходе работы с многочисленными проектами электронной коммерции мы часто сталкиваемся с ситуацией, когда сайт и бэк-офис представляют собой разные информационные системы, требующие постоянного обмена данными. При этом традиционные подходы к синхронизации данных часто оказываются недостаточно эффективными. Вероятно Вы уже сталкивались с таким и у Вас уже есть готовое решение. Но я очень часто встречаю системы, с подобными проблемами и может быть кому-то при написании своего обмена эта статья немного облегчит жизнь.
Будет круто, если в комментариях Вы поделитесь своими кейсами.
Основные проблемы существующих решений:
Задержка актуальности данных — в крупных системах с множеством офлайн‑магазинов и онлайн‑площадок информация о наличии товаров может устаревать уже через несколько часов
Избыточный трафик — при периодической выгрузке передаются все данные, включая не изменившиеся
Потеря данных — при использовании простого флага синхронизации возможны пропуски изменений
Низкая производительность — обработка больших объемов данных занимает значительное время
Сложность масштабирования — при росте количества товаров и точек продаж проблема только усугубляется
Например, в системах с 1 млн товаров и 60 офлайн-магазинами передача данных об остатках в интернет-магазин может занимать более суток. За это время информация успевает устареть, что приводит к:
Продажам отсутствующих товаров
Некорректным данным о наличии
Снижению доверия клиентов
Увеличению возвратов
Основная концепция
Предложенный подход к обмену данными базируется на использовании единой таблицы синхронизации (sync), которая позволяет управлять синхронизацией всех объектов системы. Таблица содержит три ключевых поля:
object_type — тип объекта (позволяет использовать одну таблицу для всех сущностей системы)
id — идентификатор объекта
sync_updated — строка, содержащая маркер последней успешной синхронизации (может быть временной меткой, хешем или версией)
Механизм работы
Процесс синхронизации осуществляется следующим образом:
-
Формирование выгрузки происходит путем объединения основной таблицы с таблицей sync. В выгрузку попадают записи, удовлетворяющие одному из условий:
sync_updated = null (объект никогда не синхронизировался)
sync_updated ≠ updated (объект был изменен после последней синхронизации)
Каждый пакет данных содержит информацию о времени изменения объекта, что позволяет точно отслеживать актуальность данных.
-
После успешной обработки на стороне сервера в ответ возвращается информация о:
Успешно обработанных объектах (id, updated)
Ошибках синхронизации для каждого элемента (id, updated, error_message)
При получении ответа система обновляет поле sync_updated в таблице синхронизации значениями поля updated, которые пришли в ответе сервера. Это гарантирует, что в случае изменения объекта во время обработки на сервере, запись снова попадет в очередь синхронизации при следующем цикле.
Преимущества подхода
Оптимизация трафика - передаются только измененные данные
Надежность синхронизации - система не пропускает изменения даже при одновременной обработке
Универсальность - не требует модификации существующих таблиц данных (при условии, что там уже есть время изменения в любом формате)
Минимализм - достаточно одной таблицы для синхронизации всех объектов системы
-
Гибкость - в качестве маркера версии может использоваться:
Временная метка
Версия объекта
Хеш значимых полей
Любое другое подходящее значение
Обработка ошибок
При возникновении ошибок синхронизации система получает детальный отчет по каждому объекту, что позволяет:
Корректно обработать исключительные ситуации
Провести повторный обмен только для проблемных записей
Сохранить целостность данных при частичном успехе операции
Заключение
Предложенный механизм синхронизации обеспечивает надежный и эффективный обмен данными между системами, минимизируя нагрузку на сеть и исключая потерю информации. Благодаря использованию единой таблицы синхронизации и версионного контроля, подход остается универсальным и легко интегрируемым в существующие системы, не требуя создания отдельных таблиц для каждой сущности.
Практический пример показывает, что внедрение такого подхода позволяет:
Сократить время синхронизации данных
Обеспечить актуальность информации в режиме реального времени
Снизить нагрузку на сеть
Повысить точность данных о наличии товаров
Улучшить клиентский опыт
Технические детали реализации
Структура таблицы 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.
savostin
Складываете изменения в Kafka и забираете когда и кому нужно...
Я не специалист в таких системах, но что это за SQL запросы такие гигантские? Имхо, обновление поля "остаток" 60 миллионов строк должно занимать, ну минут 5... Если конечно не открывать отдельный коннект и транзакцию на каждую цифру.
antirius Автор
Пример: бэкофис со своим сервером в офисе, онлайн-магазин в ДЦ. Обновление по API. API дальше может уже складывать себе в Кафку. В таком контексте тут уже не просто SQL запросы.
Еще сталкивался с таким.. даже не знаю как назвать).. в BI систему перезагружали вообще все данные за последние пару дней... А если документ и источнике был изменен более ранним числом, перезагружали данные за весь этот период (по отдельному запросу пользователей, когда данные переставали сходиться).
savostin
Имхо, batch insert/update нужно делать подготовленным файлом, например CSV, и делать его load средствами базы. Конечно, в каждом конкретном случае свое решение, но на каждое обновление дергать API - не рационально.
antirius Автор
Ну вот складская программа передает остатки на сайт.. тут никуда особо не денешься)
И как раз тут надо передавать данные пакетами, хотя бы раз в минуту (чтобы накопить данные, а не по одной записи через интернет гонять), предложенное решение в статье как раз делается на стороне wms.
А на сайте, уже надо думать как правильно записывать в базу этот пакет (и в Вашем комментарии как раз один из вариантов)... если текущая статья зайдет, попробую один из вариантов быстрой записи пакета расписать в следующей статье.
Для профи что текущая статья, что правильная запись пакетов - это само собой разумеющееся, но ведь полно тех, кто такую задачу решает в первый раз и тут есть вероятность, что такие простые статьи помогут)