Как устранить блокировки SQL при одновременной работе нескольких кассиров в 1С:УТ 10.3?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При работе в конфигурации 1С:Управление торговлей 10.3 (особенно в клиент-серверном варианте на MS SQL Server) программисты часто сталкиваются с проблемой взаимных блокировок при одновременной попытке нескольких кассиров пробить чек. Ситуация выглядит следующим образом: когда два кассира нажимают кнопку оплаты практически одновременно, один чек успешно печатается и проводится, а второй получает ошибку is blocked от SQL-сервера. Это приводит к рассинхронизации: чек на кассовом аппарате может быть пробит, а документ в базе 1С не создается или не проводится.

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

Анализ причины проблемы: почему возникают блокировки?

Прежде всего, проанализируем, почему типовой или самописный механизм в УТ 10.3 дает сбои. В старых конфигурациях 1С используется режим автоматических блокировок. В этом режиме MS SQL Server самостоятельно решает, какой объем данных заблокировать при выполнении транзакции. Часто вместо блокировки одной строки блокируется вся таблица (эскалация блокировок).

Основные факторы, провоцирующие проблему:

  1. Длительные транзакции: Если печать фискального чека на ККТ (физическом устройстве) происходит внутри транзакции 1С (между командами НачатьТранзакцию() и ЗафиксироватьТранзакцию()), то база данных остается заблокированной на все время работы железного принтера (от 2 до 5 секунд).
  2. Тяжелые запросы при нумерации: Конфигурация пытается определить следующий номер чека ККМ, выполняя запрос к таблице документов. Если в базе сотни тысяч чеков, этот запрос вызывает чтение всей таблицы, что накладывает разделяемую блокировку (S-lock), конфликтующую с записью.
  3. Контроль остатков: При проведении документа 1С блокирует записи в регистрах накопления (например, ТоварыНаСкладах). Если два кассира продают один и тот же товар, они неизбежно встанут в очередь.

Решение 1: Изменение логики «Сначала база, потом железо»

Самый эффективный способ, который применяется в современных конфигурациях (например, «1С:Розница 2.3»), заключается в изменении последовательности действий. Рассмотрим стандартную (ошибочную) логику и оптимизированную.

Ошибочная логика: Пробить чек на ККТ -> Если успешно, провести документ в 1С.

Правильная логика:

  1. Записать документ ЧекККМ в базу (статус «не проведен»).
  2. Попытаться провести документ.
  3. Если проведение успешно, отправить команду на печать чека в ККТ.
  4. Если печать успешна, записать фискальные признаки (номер чека ККТ, номер смены) в документ.

Разберем, почему это лучше. Если документ провелся в 1С, значит, товар списан и все проверки пройдены. Если после этого касса выдаст ошибку (например, кончилась бумага), у вас останется проведенный документ, который можно повторно отправить на печать без блокировки остатков.

Решение 2: Использование цикла повтора проведения

Если блокировка носит кратковременный характер (SQL просто не успел освободить таблицу), можно реализовать механизм «мягкого» ожидания. Вместо того чтобы сразу выкидывать ошибку кассиру, программа совершит несколько попыток проведения с небольшой паузой.

Рассмотрим пример кода для реализации такого механизма:


ПопыткаПроведения = 0;
МаксПопыток = 5;
УспешноПроведен = Ложь;

Пока ПопыткаПроведения < МаксПопыток Цикл
    Попытка
        ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
        УспешноПроведен = Истина;
        Прервать;
    Исключение
        ПопыткаПроведения = ПопыткаПроведения + 1;
        // Пауза 1-2 секунды перед следующей попыткой
        Предупреждение("База данных занята, ожидайте...", 1); 
    КонецПопытки;
КонецЦикла;

Если Не УспешноПроведен Тогда
    Сообщить("Не удалось провести чек из-за длительной блокировки. Попробуйте позже.");
КонецЕсли;

Этот метод позволяет сгладить пиковые нагрузки, когда несколько кассиров одновременно закрывают чеки.

Решение 3: Оптимизация получения номера чека

Проанализируем типовую функцию получения номера чека. В старых релизах УТ 10.3 часто встречается вызов функции, которая ищет максимальный номер чека в базе. Это «узкое место».

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

Решение 4: Разделение транзакций и работа вне транзакционного блока

Важнейшее правило: взаимодействие с внешним оборудованием не должно находиться внутри транзакции 1С. Рассмотрим, как правильно структурировать код в обработке печати чека:


// 1. Предварительная запись документа
ОбъектЧека.Записать(РежимЗаписиДокумента.Запись);

// 2. Попытка проведения (открывается короткая транзакция 1С)
Попытка
    ОбъектЧека.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    Сообщить("Ошибка проведения: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// 3. Печать на ККТ (ВНЕ транзакции базы данных)
РезультатПечати = НапечататьЧекНаККТ(ОбъектЧека);

Если РезультатПечати.Успех Тогда
    // 4. Записываем фискальные данные (еще одна короткая транзакция)
    ОбъектЧека.НомерЧекаККМ = РезультатПечати.НомерЧека;
    ОбъектЧека.Записать(РежимЗаписиДокумента.Запись);
Иначе
    Сообщить("Чек не пробит на ККТ, но проведен в 1С. Требуется ручное вмешательство.");
КонецЕсли;

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

Дополнительные рекомендации для системного администратора

Если программные методы не помогают полностью избавиться от Deadlocks или Timeout, следует обратить внимание на настройки SQL-сервера:

  1. Snapshot Isolation (Изоляция моментального снимка): Включение этого режима на уровне базы данных SQL позволяет читающим транзакциям не блокироваться пишущими. Это значительно ускоряет работу отчетов и подборов в РМК во время активных продаж.
  2. Разделение базы и лога: Убедитесь, что файлы базы данных (MDF) и лога транзакций (LDF) находятся на разных физических SSD-дисках. Это ускоряет фиксацию транзакций (COMMIT), уменьшая время удержания блокировок.
  3. Управляемые блокировки: Если у вас есть ресурсы на серьезную доработку, рассмотрите перевод документа ЧекККМ в режим управления блокировками. Это позволит блокировать данные по конкретному складу или товару программно, избегая блокировки всей таблицы SQL.

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

← На главную