В процессе работы высоконагруженных систем на базе 1С:Предприятие 8.3 и MS SQL Server часто возникает ситуация, когда документы (например, «Отчет о розничных продажах») перестают проводиться из-за ошибок транзакций. Одной из самых коварных проблем является взаимоблокировка (deadlock). В этом материале мы подробно разберем, почему возникают такие конфликты в регистре накопления БонусныеБаллы, как механизм разделения итогов влияет на стабильность системы и что нужно изменить в коде и настройках сервера, чтобы навсегда забыть об этой ошибке.
Рассмотрим ситуацию: в конфигурации включен режим разделения итогов для регистра накопления. Этот механизм задуман для того, чтобы несколько пользователей могли одновременно записывать движения по одним и тем же измерениям, не ожидая друг друга на уровне управляемых блокировок 1С. Платформа просто записывает данные с разными значениями специального разделителя в физическую таблицу итогов.
Однако здесь кроется ловушка. Если в коде при записи набора записей не установлено свойство БлокироватьДляИзменения = Истина, 1С не выстраивает транзакции в очередь. В результате две и более транзакции одновременно «проваливаются» на уровень СУБД (MS SQL Server). Проанализируем, что происходит внутри SQL Server:
_AccumRegTotals).Выясним, как предотвратить этот хаос на уровне кода 1С. Самый эффективный способ — перенести ожидание с уровня СУБД на уровень управляемых блокировок 1С. Для этого нам необходимо явно указать платформе, что мы собираемся изменять данные по конкретным измерениям.
Разберем пример кода, который необходимо внедрить в процедуру проведения документа или в модуль набора записей регистра БонусныеБаллы. Мы будем использовать объект БлокировкаДанных, чтобы заблокировать только те баллы, которые относятся к конкретному дисконтному номеру или партнеру:
// Создаем объект блокировки
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.БонусныеБаллы");
// Устанавливаем режим и пространство блокировки
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокДисконтныхКарт; // ТЗ или массив с данными
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("ДисконтнаяКарта", "ДисконтнаяКарта");
// Накладываем блокировку перед записью движений
Блокировка.Заблокировать();
Также крайне важно проверить свойство коллекции движений. В модуле проведения документа, перед тем как выполнять запись, убедимся, что установлено БлокироватьДляИзменения. Рассмотрим, как это выглядит на практике:
Движения.БонусныеБаллы.БлокироватьДляИзменения = Истина;
Движения.БонусныеБаллы.Записать();
Важный нюанс: Установка этого флага заставляет платформу наложить исключительную управляемую блокировку. Это выстраивает документы в очередь еще до того, как они обратятся к SQL Server, что исключает возникновение дедлоков на уровне страниц базы данных.
Иногда программных мер недостаточно, если база данных находится в «запущенном» состоянии. Проанализируем технические настройки MS SQL Server, которые напрямую влияют на вероятность возникновения взаимоблокировок.
Во-первых, обратим внимание на эскалацию блокировок (Lock Escalation). Если документ «Отчет о розничных продажах» содержит сотни строк, SQL Server может решить, что блокировать каждую строку по отдельности слишком дорого, и заблокирует всю таблицу целиком. Чтобы снизить вероятность этого, необходимо регулярно выполнять обслуживание индексов.
Во-вторых, проверим настройки параллелизма. Для работы с 1С рекомендуется устанавливать параметр Max Degree of Parallelism (MAXDOP) в значение 1. Если SQL Server пытается распараллелить запрос на обновление итогов, он может создать взаимоблокировку сам с собой (intra-query deadlock).
Рассмотрим ситуацию, когда регистр БонусныеБаллы перегружен записями. В «Рознице» этот регистр часто содержит тысячи «нулевых» остатков, которые SQL Server все равно вынужден учитывать при поиске и блокировке. Проанализируем способы решения:
_AccumRegTotals.Подводя итог нашему разбору, отметим: взаимоблокировка — это всегда следствие того, что система управления базами данных не смогла разрешить порядок доступа к ресурсам. Для решения проблемы в 1С:Розница мы рекомендуем:
БлокироватьДляИзменения = Истина при включенном разделении итогов.БлокировкаДанных по ключевым измерениям (Дисконтная карта, Владелец).MAXDOP.