Распродажа, товар остался в одном экземпляре, и на него одновременно нажимают «купить» три человека. Все трое видели «в наличии», все трое оформили заказ. На складе — одна единица. Дальше начинается ручная разборка: кому-то придётся звонить и извиняться, кто-то оставит гневный отзыв, а склад останется в минусе. Это не редкость и не «сбой» — это закономерность любой системы, где остаток читают и списывают без защиты от гонок.
Эта статья — про инженерную сторону управления остатками на 1С-Битрикс под нагрузкой: почему возникают гонки при списании, чем резерв отличается от списания, как защитить остаток блокировками и атомарными операциями, как это уживается с обменом с 1С, веб-кластером и highload-блоками. Материал технический и связан с нашей работой по автоматизации продаж и склада на 1С.
Коротко
- Перепродажа под нагрузкой — это гонка: два заказа списывают один остаток, потому что читают и пишут его отдельными операциями.
- Резерв удерживает товар под заказ, не искажая физический остаток; списание — окончательное уменьшение при отгрузке.
- Целостность обеспечивают атомарные операции и короткие блокировки строки товара, а не широкие блокировки таблиц.
- Источник истины по остаткам — 1С; сайт оперативно резервирует, обмен CommerceML сверяет и подтверждает.
Проблема: перепродажа под нагрузкой
При низком трафике проблема остатков почти незаметна: заказы приходят по одному, между ними есть паузы, и система успевает корректно уменьшить остаток. Всё меняется на пике — распродажа, рекламный всплеск, дефицитный товар. Когда десятки запросов на один товар приходят почти одновременно, наивная логика «прочитали остаток, проверили, записали новый» ломается: между чтением и записью успевает вклиниться другой запрос.
Результат — перепродажа: продано больше, чем есть. Для бизнеса это дороже, чем кажется: отмены, недовольные клиенты, ручные компенсации, испорченная репутация в самый прибыльный момент. Поэтому управление остатками под нагрузкой — это не оптимизация «на потом», а базовое требование к торговой системе.
Гонки при списании остатка
Чтобы защититься, нужно понимать механику гонки. Классический сценарий выглядит так:
- Запрос A читает остаток. Видит «1 в наличии».
- Запрос B читает остаток. Тоже видит «1» — списание A ещё не произошло.
- Запрос A проверяет и списывает. «1 ≥ 1» — списывает, остаток становится 0.
- Запрос B проверяет и списывает. Он проверял против старого «1» — списывает тоже, остаток уходит в −1.
Ошибка не в коде проверки, а в том, что чтение, проверка и запись разнесены во времени и не защищены. Между шагами возможно вмешательство другого запроса. Лечится это тем, что операция «проверить и уменьшить» должна быть неделимой — атомарной — или выполняться под блокировкой, которая не даёт двум запросам одновременно менять один остаток.
Резерв против списания
Второй ключевой концепт — разделение резерва и списания. Их путают, и от этого возникают искажённые остатки.
| Аспект | Резерв | Списание |
|---|---|---|
| Что делает | Удерживает товар под заказ | Окончательно уменьшает остаток |
| Когда | При оформлении / оплате | При отгрузке |
| Физический остаток | Не меняется, но недоступен другим | Уменьшается фактически |
| Обратимость | Снимается при отмене заказа | Обычно окончательно |
Резерв защищает от перепродажи, пока клиент оплачивает и заказ обрабатывается, но не искажает фактический остаток склада. Если резервы не снимать при отмене заказов, товар «зависнет» недоступным. Поэтому важна дисциплина: резерв ставится при оформлении, снимается при отмене или истечении срока, а списание происходит по факту отгрузки и сверяется с 1С.
Атомарность и блокировки строки
Технически целостность остатка обеспечивают на уровне СУБД двумя способами, которые часто комбинируют.
- Атомарное условное обновление. Одна операция уменьшает остаток только при условии, что его хватает. Из двух одновременных заказов на последнюю единицу успешно отработает только один; второй увидит, что уменьшить нечего.
- Блокировка строки товара. В транзакции строка конкретного товара блокируется на чтение-запись, пока идёт проверка и списание; второй запрос ждёт освобождения и работает уже с актуальным остатком.
Важно, что блокируется именно строка нужного товара, а не вся таблица остатков — иначе под нагрузкой сайт встанет в очередь. Правильно спроектированные транзакции и работа с данными в современном Битрикс строятся на ORM. Как использовать транзакции и корректно работать с данными, мы разбираем в статье про D7 и ORM в Битрикс.
Резервирование в заказе Битрикс
В 1С-Битрикс резервирование — штатная возможность модуля «Интернет-магазин». Товар резервируется под заказ по заданным правилам: при создании заказа, при оплате или по статусу. У резерва есть срок и логика снятия. Настраивается это в параметрах каталога и заказа, но под нагрузкой важны не столько галочки, сколько согласованность.
- Момент резерва. Решите, когда резервировать: сразу при оформлении (надёжнее против перепродажи) или после оплаты (меньше «зависших» резервов).
- Снятие резерва. Отмена, истечение срока оплаты, объединение заказов — резерв должен корректно освобождаться.
- Учёт резерва в наличии. Доступное количество = физический остаток минус активные резервы; именно это видит покупатель.
- Согласие с 1С. Резервы сайта и учётной системы не должны противоречить друг другу.
Тонкость в том, что резерв на сайте и резерв в 1С — это две системы, которые нужно синхронизировать, иначе доступное количество разойдётся.
Обмен остатками с 1С
Физические остатки ведутся в 1С и приходят на сайт обменом по протоколу CommerceML. Учётная система — источник истины по факту, но в момент продажи сайт должен управлять доступностью оперативно, не дожидаясь следующей выгрузки. Отсюда типовая архитектура:
- 1С отдаёт остатки. Регулярный обмен обновляет физические остатки в каталоге сайта.
- Сайт резервирует оперативно. В момент заказа сайт удерживает товар, не дожидаясь следующего обмена.
- Обмен сверяет. Следующая выгрузка согласует резервы и фактические остатки, подтверждает списания.
- Конфликты разрешаются по правилам. Если данные разошлись, приоритет и логика слияния заданы заранее.
Главная причина перепродаж на практике — редкий обмен и рассинхрон резервов. Если сайт узнаёт об изменении остатка раз в час, а продажи идут каждую минуту, окно ошибки велико. Частота и надёжность обмена — вопрос архитектуры интеграции, который мы решаем услугой автоматизации на 1С.
Веб-кластер и общая база
Когда трафик растёт, сайт масштабируют — в том числе штатным веб-кластером Битрикс: несколько веб-нод, репликация базы, распределённые сессии и кэш. Здесь важно не питать иллюзий: кластер увеличивает пропускную способность, но не решает проблему гонок за остаток сам по себе.
Наоборот, в кластере из нескольких нод корректная блокировка становится критичнее: одновременные запросы приходят с разных серверов к общей СУБД. Если целостность остатка держалась на предположении «всё в одном процессе», в кластере она рассыплется. Правило простое: пропускную способность масштабирует кластер, а целостность остатка обеспечивают транзакции и блокировки на уровне общей базы, которую видят все ноды. Как устроена инфраструктура под нагрузку, мы разбираем в статье про хостинг и инфраструктуру BitrixVM.
Highload-блоки и хранение данных
Highload-блоки в 1С-Битрикс — инструмент для больших справочников с быстрым доступом. Возникает соблазн хранить в них и остатки, но здесь нужна осторожность. Остаток — часто меняющаяся величина, тесно связанная с торговым каталогом и заказами, и штатные механизмы каталога и заказа уже умеют его резервировать и списывать.
- Остатки. Обычно ведутся штатными средствами торгового каталога и заказа, где уже есть логика резерва.
- Highload-блоки. Хороши для смежных больших данных: расширенных характеристик, справочников складов, истории движений.
- Выбор осознанно. Модель хранения проектируют под нагрузку и структуру данных, а не по инерции «всё в highload».
Ключевой критерий — где проще и надёжнее обеспечить атомарность изменения при вашей нагрузке. Иногда это штатный каталог, иногда — отдельная структура с продуманными блокировками. Это архитектурное решение, а не вопрос моды на инструмент.
Короткие блокировки и дедлоки
Блокировки спасают от гонок, но неаккуратные блокировки сами становятся проблемой: под нагрузкой возникают долгие ожидания и дедлоки, когда два процесса ждут друг друга. Принципы, которые держат систему живой:
- Минимальная область. Блокируйте строку конкретного товара, а не таблицу остатков.
- Короткая транзакция. Держите блокировку только на время атомарного изменения, а не на всю обработку заказа.
- Единый порядок доступа. Обращайтесь к товарам в согласованном порядке, чтобы избежать взаимных блокировок.
- Повтор при конфликте. При конфликте операцию повторяют, а не ждут бесконечно; клиенту при исчерпании — честный отказ.
Плохо спроектированные блокировки под пиком превращают быстрый сайт в очередь ожидающих запросов. Поэтому область и длительность блокировки важнее, чем сам факт её наличия.
Обработка нехватки на оформлении
Даже с идеальными блокировками бывает, что товара действительно не хватило — он кончился между добавлением в корзину и подтверждением заказа. Здесь важен UX-контракт: честный и мгновенный отказ по позиции лучше, чем оформленный заказ, который потом отменят.
- Проверка в момент подтверждения. Наличие перепроверяется при оформлении, а не только при добавлении в корзину.
- Точечное сообщение. «Товар закончился, доступно N» на конкретной позиции, а не общая ошибка на весь заказ.
- Мягкое действие. Предложите уменьшить количество или убрать позицию, сохранив остальной заказ.
- Никаких минусов и тихих отмен. Списание в отрицательный остаток и отмена после оплаты — худшее для доверия.
Частые ошибки
- Читают и пишут остаток раздельно. Классическая гонка и перепродажа под нагрузкой.
- Путают резерв и списание. Искажённые остатки и «зависшие» товары.
- Блокируют всю таблицу. Сайт встаёт в очередь на пике.
- Редкий обмен с 1С. Большое окно рассинхрона и перепродажи.
- Резервы не снимаются. Отменённые заказы держат товар недоступным.
- Надежда на кластер. Кластер масштабирует нагрузку, но не защищает остаток без блокировок.
- Нет проверки наличия на оформлении. Заказ уходит на товар, которого уже нет.
Чек-лист
- Списание атомарно. Проверка и уменьшение остатка — неделимая операция или блокировка строки.
- Резерв отделён от списания. Удержание под заказ и фактическое списание разведены по логике и времени.
- Резервы снимаются. Отмена, истечение срока и объединение заказов освобождают товар.
- Обмен с 1С частый и надёжный. Остатки и резервы согласуются, окно рассинхрона мало.
- Блокировки узкие и короткие. Строка товара, минимальная транзакция, повтор при конфликте.
- Кластер и база согласованы. Целостность держится на общей СУБД, а не на «одном процессе».
- Нехватка обрабатывается честно. Точечный отказ на оформлении вместо минусов и тихих отмен.
Вывод
Управление остатками под нагрузкой — это про целостность в момент, когда на один товар претендуют многие. Перепродажа рождается не из «плохого сервера», а из гонки между чтением и записью остатка. Лечится она атомарными операциями и короткими блокировками строки товара, разделением резерва и списания и частым, согласованным обменом с 1С как источником истины.
Веб-кластер и highload-блоки помогают масштабировать нагрузку, но не заменяют корректную работу с транзакциями. Спроектируйте списание как неделимую операцию, держите резервы дисциплинированно и обрабатывайте нехватку честно — и распродажа перестанет превращаться в разбор перепроданных заказов.