Как снять блокировку пользователей 1С
Хорошо знать, как снять блокировку пользователей 1С в случае, если возникнут проблемы с доступом к системе. В этой статье мы подробно расскажем, как снять блокировку пользователей 1С, как снять блокировку с конфигурации 1С, как снять активного пользователя в 1С, как войти в заблокированную базу 1С и как отключить пользователей от файловой базы 1С.
Навигация по разделам доступна по ссылке ниже:
Как разрешить дать доступ к 1С Бухгалтерия пользователям
Как разблокировать объект в 1С
Как войти в заблокированную базу 1С
Как снять активного пользователя в 1С
Полезные советы и выводы
Сказать, что вы чувствуете
Для снятия блокировки пользователей в программе 1С нужно внести код разрешения доступа в параметр «/UC», который был указан при установке блокировки. После выполнения всех запланированных работ и до истечения срока блокировки, нужно выбрать команду «Блокировка работы пользователей» в разделе «Администрирование — Поддержка и обслуживание» и нажать на кнопку «Снять блокировку». Это позволит всем пользователям в системе вновь получить доступ к работе с программой. Важно помнить, что снятие блокировки без разрешения может привести к нарушению безопасности данных и общей работе системы. Поэтому необходимо следовать указанным инструкциям и соблюдать требования безопасности при работе в программе 1С.
Как снять блокировку пользователей 1С
Для снятия блокировки пользователей 1С нужно в параметре «/UC» написать код разрешения доступа, который был указан при установке блокировки. После проведения запланированных работ до окончания срока блокировки нужно выбрать команду Блокировка работы пользователей в разделе Администрирование — Поддержка и обслуживание и нажать кнопку Снять блокировку.
Как снять блокировку с конфигурации 1С
Чтобы снять блокировку с конфигурации 1С, нужно выбрать пункт меню «Конфигурация > Поддержка > Настройка поддержки…». В появившемся окне нужно нажать на кнопку «Снять с поддержки» и на вопрос ответить «Да».
Как снять активного пользователя в 1С
Для снятия активного пользователя в 1С можно запустить консоль администрирования сервера 1С. В ветке Информационные базы нужно найти базу, в которой будем завершать работу пользователя. Открыв ее, нужно зайти в ветку Сеансы. Щелкнув правой кнопкой мыши по имени пользователя, нужно выбрать пункт Удалить.
Как войти в заблокированную базу 1С
Есть два варианта снятия блокировки, один из которых позволяет войти в базу, на которую установлена блокировка. Решение через код разрешения запуска 1С: при запуске программы 1С нужно использовать параметр \UC и указать код разрешения запуска 1С.
Как отключить пользователей от файловой базы 1С
Блокировка работы пользователей в файловой базе осуществляется следующим образом: нужно перейти в разделе «Администрирование» («НСИ и администрирование» — в зависимости от конфигурации может быть такое название) по ссылке «Обслуживание», далее по ссылке «Блокировка работы пользователей».
Полезные советы
- Не забывайте о коде разрешения доступа, который был указан при установке блокировки. Он пригодится в случае необходимости снять блокировку.
- В случае проблем с доступом к системе следует связаться с технической поддержкой 1С для получения квалифицированной помощи.
- Некоторые операции с 1С могут повлиять на работоспособность всей системы, поэтому перед выполнением любых действий следует обязательно сделать резервную копию данных.
Выводы
Снятие блокировки пользователей 1С — важная процедура, которую необходимо знать всем, кто работает с системой. Надеемся, что данный гайд поможет вам быстро и без проблем выполнить данную операцию. Чтобы избежать проблем, связанных с блокировкой, рекомендуется правильно установить все необходимые настройки и следить за соответствием разрешений доступа.
Разблокировка базы 1С после незавершенного резервного копирования
При автоматическом обновлении типовой конфигурации 1С:Предприятие происходит создание архивной копий базы данных, и в этот момент вход в информационную базу блокируется на время выполнения процесса архивирования.
В случае возникновения нештатной ситуации (например, отключение электричества или компьютера, перезагрузка Windows и т.п.) вход в программу остается заблокированным, при попытке запуска выходит сообщение:
Для разблокировки базы в данном случае существует несколько вариантов:
1) Прописать команду в Дополнительных параметрах запуска 1С:
ENTERPRISE /F»» /CРазрешитьРаботуПользователей /UCРезервноеКопирование
Необходимо открыть окно 1С:Предприятие, в списке баз выбрать базу, которая заблокирована, и в меню справа выбрать пункт «Изменить», в открывшемся окне нажимаем кнопку «Далее».
Открывается окно «Редактирование информационной базы», здесь нам необходим пункт Дополнительные параметры запуска. В пустой строке прописываем команду:
ENTERPRISE /F»» /CРазрешитьРаботуПользователей /UCРезервноеКопирование
В данной команде необходимо прописать путь к заблокированной базе, в угловых скобках вместо «»:
ENTERPRISE /F»< D:\users\Logunova\Documents\BP_3.0.58.41>» /CРазрешитьРаботуПользователей /UCРезервноеКопирование, и нажимаем «Готово».
В этот момент начинает запускаться база в режиме «Предприятия», необходимо выбрать пользователя и ввести пароль, процесс запуска происходит, но потом выкидывает — это нормально. После необходимо заново открыть окно 1С:Предприятия, выбрать базу, которая заблокирована, и в меню справа выбрать пункт «Изменить», в открывшемся окне нажимаем «Далее» и в «Дополнительных параметрах запуска» удаляем внесенную команду ENTERPRISE /F»< D:\users\Logunova\Documents\BP_3.0.58.41>» /CРазрешитьРаботуПользователей /UCРезервноеКопирование, затем заново открываем базу в обычном режиме.
2) Изменение даты (времени) на компьютере на момент до блокировки. Метод позволяет зайти в базу и снять блокировку, после чего дата (время) меняются обратно.
Для того чтобы изменить дату и время на компьютере, необходимо одним кликом левой кнопкой мыши открыть настройки даты и времени, которые располагаются внизу справа на «Рабочем столе», затем нажимаем на «Изменение настроек даты и времени…», в открывшемся окне выбираем кнопку «Изменить дату и время» — выставляем время и дату до блокировки: Например, дата и время блокировки 26.09.2019 14:50:45, чтобы снять блокировку, меняем дату на 25.09.2018, а время 13:53:17, после изменений заходим в базу и восстанавливаем изначальные параметры даты и времени.
3) Удаление файла блокировки 1Cv8.cdn из каталога с информационной базой.
Для удаления файла блокировки 1Сv8.cdn необходимо запустить 1С:Предприятие, в списке информационных баз выделяем нужную, нажимаем кнопку «Изменить».
Из поля «Каталог информационной базы» копируем путь до базы данных и нажимаем «Отмена».
Вставляем скопированный путь до каталога с информационной базой в проводнике Windows.
В открывшемся каталоге находим и удаляем файл 1Cv8.cdn, после в обычном режиме запускаем 1С.
4) Модификация файла 1Cv8.cdn в каталоге базы.
Открываем 1Cv8.cdn с помощью текстового редактора (блокнота).
Первый параметр «1» является признаком блокировки. Если его поменять на «0» и сохранить файл, то база должна запуститься в обычном режиме.
Поле «Кода для разблокировки» указывается в кавычках после «/UC». Если код разрешения ранее не был задан, то его можно указать в данном поле и сохранить файл (например «0000»). Далее этот код указываем в строке параметра запуска программы и запускаем как в варианте 1 разблокировки данной статьи.
Анализ и устранение взаимоблокировок
Взаимоблокировка — это неразрешимый конфликт блокировок, возникающий при работе двух или более сессий «1С:Предприятия».
Для понимания взаимоблокировок необходимо знать, что такое блокировка и ожидание на блокировке. Подробная информация по этой теме содержится в статье базы знаний «Блокировки данных в 1С:Предприятии 8». Рекомендуется предварительно ознакомиться с этим материалом для лучшего понимания текста настоящей статьи.
В случае взаимоблокировки двух сессий конфликт заключается в том, что сессия 1 ожидает снятия блокировки, установленной сессией 2, в то время как сессия 2, в свою очередь, ожидает снятия блокировки, установленной сессией 1. Ни одна из сессий не может ни продолжить свою работу (из-за ожидания), ни снять свою блокировку для того, чтобы дать конкурирующей сессии продолжить работу.
Такой конфликт не может быть разрешен без участия внешних (по отношению к обеим сессиям) механизмов. Подобные механизмы реализованы во всех СУБД, используемых «1С:Предприятием», а также в самом сервере «1С:Предприятия» (для обнаружения неразрешимых конфликтов управляемых блокировок). Будем называть эти механизмы «менеджерами взаимоблокировок».
Менеджер взаимоблокировок работает следующим образом:
- С определенной периодичностью анализирует установленные в данный момент блокировки на предмет наличия неразрешимых конфликтов.
- При обнаружении неразрешимого конфликта (взаимоблокировки) менеджер откатывает одну из транзакций, участвующих во взаимоблокировке. При этом он обычно пытается выбрать ту транзакцию, откат которой будет менее болезненным для системы в целом. Будем называть эту сессию «жертвой взаимоблокировки».
- Сессия, выбранная «жертвой взаимоблокировки», получает соответствующее сообщение об ошибке.
- При откате транзакции «жертвы» автоматически снимаются все установленные ею блокировки, что позволяет остальным сессиям продолжить свою работу.
Следует учитывать, что во взаимоблокировках могут участвовать несколько (более двух) сессий «1С:Предприятия». Причем одна и та же причина может приводить к возникновению разных (по конфигурации и по сложности) взаимоблокировок с различным количеством участников и блокировок между ними. Поэтому при анализе большого количества взаимоблокировок в системе всегда имеет смысл начинать с самых простых взаимоблокировок – с минимальным количеством участников и с самыми простыми схемами. Высока вероятность того, что, устранив одну простую взаимоблокировку вы одновременно устраните несколько других – в том числе более сложные.
Другой важной особенностью взаимоблокировок является тот факт, что взаимоблокировка почти всегда «распределена» по нескольким различным строкам кода конфигурации. При этом на начальном этапе анализа пользователю известна только одна строка кода. Это может быть, например, последняя строка, выполненная «жертвой» перед тем, как ее транзакция была отменена менеджером взаимоблокировок (эта строка доступна в информации об ошибке) или строка кода конфигурации, которую выдал ЦУП в дереве аналитической информации.
При этом причина взаимоблокировки (ошибка, приводящая к ее возникновению) может находиться совсем в другом месте исходного кода. Это создает определенные трудности при анализе и устранении взаимоблокировок.
Известны следующие типичные причины возникновения взаимоблокировок в системах на платформе «1С:Предприятие 8»:
- Повышение уровня блокировки ресурса в рамках одной транзакции.
- Захват ресурсов в разном порядке.
- Неоптимальная работа запроса.
- Ошибка блокировок при работе внутренних механизмов MS SQL Server.
В настоящей статье будут рассмотрены особенности возникновения этих видов взаимоблокировок, объяснены причины, по которым они возникают, и даны рекомендации по анализу и устранению этих причин.
Следует заметить, что взаимоблокировки не всегда поддаются анализу в том случае, если нам известна только одна строка кода – та, в которой было получено сообщение об ошибке взаимоблокировки. Как правило, для анализа взаимоблокировки необходимо использовать подробную информацию, предоставляемую «Центром управления производительностью». Однако для понимания информации ЦУП необходимо знать основные схемы взаимоблокировок и причины, по которым они возникают. Поэтому рекомендуется сначала освоить материал следующих четырех разделов, а затем, если понадобится, переходить к анализу информации, предоставляемой ЦУП.
Текущая версия ЦУП показывает схемы взаимоблокировок только в том случае, если исследуемая информационная база работает с использованием MS SQL Server 2005.
Повышение уровня блокировки ресурса в рамках одной транзакции
Общие сведения
Повышение уровня блокировки в рамках одной транзакции является наиболее распространенной причиной возникновения взаимоблокировок.
Схема возникновения взаимоблокировки такова. Две конкурирующие транзакции (Т1 и Т2) читают один и тот же ресурс – Р1. При этом устанавливаются разделяемые блокировки на этот ресурс. Разделяемые блокировки от конкурирующих транзакций могут существовать одновременно, поэтому к моменту времени t1 обе транзакции успешно установили свои блокировки и продолжают работу.
Затем транзакция Т1 изменяет ресурс Р1 и пытается его записать. При этом устанавливается эксклюзивная блокировка на этот ресурс. Однако эта блокировка не может быть установлена одновременно с разделяемой блокировкой от транзакции Т2, поэтому она устанавливается в состояние ожидания. Транзакция Т1 прекращает работу и ждет, пока будет снята разделяемая блокировка, установленная транзакцией Т2.
Транзакция Т2, в свою очередь, хочет записать ресурс Р1, для чего также предпринимает попытку заблокировать его в эксклюзивном режиме. Однако это невозможно, так как существует уже установленная транзакцией Т1 разделяемая блокировка на этот ресурс. Транзакция Т2 также прекращает работу и ждет, пока будет снята разделяемая блокировка, установленная транзакцией Т1.
Обе транзакции находятся в режиме ожидания и не могут ни продолжить работу, ни освободить заблокированные ими ресурсы.
Причина возникновения взаимоблокировки данного вида – повышение уровня изоляции заблокированного ресурса в рамках одной транзакции. Транзакции сначала блокировали ресурс при помощи разделяемой блокировки, а затем повышали уровень блокировки до эксклюзивного.
Исходя из этого можно сформулировать следующее правило: блокировка в транзакции должна изначально осуществляться с максимальным необходимым уровнем изоляции.
Применительно к данному случаю перед чтением ресурса необходимо было заблокировать его в эксклюзивном режиме. Тогда картина изменилась бы следующим образом:
В данном случае обе транзакции, перед тем как читать ресурс Р1, устанавливают на него эксклюзивную блокировку. Одна из транзакций (Т1) успевает сделать это первой. К моменту времени t1 ресурс Р1 оказывается заблокированным в эксклюзивном режиме и транзакция Т2, которая не может установить свою блокировку, становится в ожидание на блокировке.
Транзакция Т1 продолжает работу — считывает и записывает ресурс Р1. К моменту времени t2 транзакция Т1 завершается, все установленные ею блокировки автоматически снимаются. После этого начинает работать транзакция Т2 и так же успешно выполняет все свои действия.
Особенности взаимоблокировок данного вида
Взаимоблокировки данного вида проявляются, как правило, при конкурентном выполнении однотипных действий (например, несколько пользователей вводят документы одного вида — РеализацияТоваровУслуг). Для анализа таких взаимоблокировок обычно достаточно знать, в какой строке кода было получено сообщение об ошибке взаимоблокировки. Исходя из этого можно провести анализ кода конфигурации и решить проблему, не обращаясь к подробной информации о взаимоблокировке, предоставляемой ЦУП.
Если такой анализ не дал результатов, необходимо исследовать взаимоблокировку при помощи «Центра управления производительностью» , который выдаст полную информацию по схеме взаимоблокировки.
Взаимоблокировки данного вида достаточно просты для анализа. При разработке (или доработке) системы следует всегда использовать сформулированное выше правило: блокировка в транзакции должна изначально осуществляться с максимально необходимым уровнем изоляции. Это позволит полностью исключить возникновение таких взаимоблокировок в системе.
Типичной ошибкой в коде конфигурации, которая может приводить к возникновению взаимоблокировок данного вида, является неблокирующее чтение и последующая запись в рамках одной транзакции:
- Автоматический режим:
- Чтение без опции «ДЛЯ ИЗМЕНЕНИЯ» и последующая запись.
- Чтение в объектной технике и последующая запись.
- Чтение без установки блокировки (либо с установкой разделяемой блокировки) и последующая запись.
Для возникновения взаимоблокировки данного вида необходимо одновременное выполнение следующих условий:
- Операции чтения и записи происходят в рамках одной транзакции. Если чтение и запись происходят в разных транзакциях, то это не может быть причиной возникновения взаимоблокировок. Следует учитывать, что транзакция может быть неявной, то есть автоматически создаваться «1С:Предприятием» (например, при проведении документа).
- Записывается тот же самый ресурс, который перед этим был прочитан. Если читается один ресурс, а записывается другой, то это не может быть причиной возникновения взаимоблокировки.
Автоматический режим. Чтение без опции «ДЛЯ ИЗМЕНЕНИЯ» и последующая запись в рамках одной транзакции
Рассмотрим следующий пример.
// Обработчик проведения документа // Процедура ОбработкаПроведения(Отказ, РежимПроведения) Если Не ПроверитьОстатки() Тогда Отказ = Истина; Возврат; КонецЕсли; СписатьТовары(); КонецПроцедуры // ОбработкаПроведения()
Обратите внимание на то, что проведение документа выполняется в неявной транзакции, которая автоматически открывается «1С:Предприятием». Таким образом, весь текст обработчика проведения будет выполнен в рамках одной транзакции.
// Проверить наличие товаров на складах // // Возвращаемое значение: // Булево – Истина, товаров достаточно // Функция ПроверитьОстатки() Запрос = Новый Запрос; Запрос.УстановитьПараметр("Документ", Ссылка); Запрос.Текст = " |ВЫБРАТЬ | ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура, | СУММА(НеобходимыеТовары.Количество) КАК Необходимо, | СУММА(ТоварыНаСкладахОстатки.КоличествоОстаток) КАК Остаток |ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки( | , | (Склад, Номенклатура) В | (ВЫБРАТЬ | Документ.Реализация.Товары.Склад, | Документ.Реализация.Товары.Номенклатура | ИЗ | Документ.Реализация.Товары | ГДЕ | Документ.Реализация.Товары.Ссылка = &Документ)) КАК ТоварыНаСкладахОстатки | | ЛЕВОЕ СОЕДИНЕНИЕ Документ.Реализация.Товары КАК НеобходимыеТовары | ПО НеобходимыеТовары.Ссылка = &Документ | И НеобходимыеТовары.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура | |СГРУППИРОВАТЬ ПО | ТоварыНаСкладахОстатки.Номенклатура"; Результат = Запрос.Выполнить().Выбрать(); Пока Результат.Следующий() Цикл Если Результат.Остаток = null Или Результат.Остаток - Результат.Необходимо 0 Тогда Сообщить("Недостаточно (" + Результат.Остаток + " из " + Результат.Необходимо + ") товара (" + Результат.Номенклатура + ")!"); Возврат Ложь; КонецЕсли; КонецЦикла; Возврат Истина; КонецФункции // ПроверитьОстатки()
При выполнении запроса, описанного в данной функции, происходит чтение остатков регистра накопления «ТоварыНаСкладах». При этом автоматически устанавливается разделяемая блокировка остатков списываемых товаров. Эта блокировка не позволит конкурирующей транзакции изменить остатки по данным товарам, но позволит ей считать остатки. То есть чтение является неблокирующим.
// Списать товары со складов // Процедура СписатьТовары() Для Каждого ТекСтрокаТовары Из Товары Цикл Движение = Движения.ТоварыНаСкладах.Добавить(); Движение.ВидДвижения = ВидДвиженияНакопления.Расход; Движение.Период = Дата; Движение.Склад = ТекСтрокаТовары.Склад; Движение.Характеристика = ТекСтрокаТовары.Характеристика; Движение.Номенклатура = ТекСтрокаТовары.Номенклатура; Движение.Количество = ТекСтрокаТовары.Количество; КонецЦикла; Движения.ТоварыНаСкладах.Записать(); КонецПроцедуры // СписатьТовары()
В этой функции выполняется запись регистра накопления «ТоварыНаСкладах». В том случае, если остатки по товару реально изменились, при записи будет автоматически установлена эксклюзивная блокировка на остатки по данному товару. То есть произойдет повышение уровня изоляции ресурса в рамках одной транзакции.
Если несколько пользователей системы будут одновременно списывать одинаковые товары с одних и тех же складов, это может привести к возникновению взаимоблокировки.
Для того чтобы избежать возникновения взаимоблокировки, необходимо сделать чтение остатков блокирующим. Для этого используется опция «ДЛЯ ИЗМЕНЕНИЯ».
Необходимо изменить функцию «ПроверитьОстатки» следующим образом:
// Проверить наличие товаров на складах // // Возвращаемое значение: // Булево – Истина, товаров достаточно // Функция ПроверитьОстатки() Запрос = Новый Запрос; Запрос.УстановитьПараметр("Документ", Ссылка); Запрос.Текст = "ВЫБРАТЬ | ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура, | СУММА(НеобходимыеТовары.Количество) КАК Необходимо, | СУММА(ТоварыНаСкладахОстатки.КоличествоОстаток) КАК Остаток |ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки( | , | (Склад, Номенклатура) В | (ВЫБРАТЬ | Документ.Реализация.Товары.Склад, | Документ.Реализация.Товары.Номенклатура | ИЗ | Документ.Реализация.Товары | ГДЕ | Документ.Реализация.Товары.Ссылка = &Документ)) КАК ТоварыНаСкладахОстатки | ЛЕВОЕ СОЕДИНЕНИЕ Документ.Реализация.Товары КАК НеобходимыеТовары | ПО (НеобходимыеТовары.Ссылка = &Документ) | И (НеобходимыеТовары.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура) | |СГРУППИРОВАТЬ ПО | ТоварыНаСкладахОстатки.Номенклатура | |ДЛЯ ИЗМЕНЕНИЯ | РегистрНакопления.ТоварыНаСкладах.Остатки"; Результат = Запрос.Выполнить().Выбрать(); Пока Результат.Следующий() Цикл Если Результат.Остаток = null Или Результат.Остаток - Результат.Необходимо 0 Тогда Сообщить("Недостаточно (" + Результат.Остаток + " из " + Результат.Необходимо + ") товара (" + Результат.Номенклатура + ")!"); Возврат Ложь; КонецЕсли; КонецЦикла; Возврат Истина; КонецФункции // ПроверитьОстатки()
Автоматический режим. Чтение в объектной технике и последующая запись
Предположим, что в коде конфигурации определена следующая процедура
Процедура ИзменитьНаименованиеКонтрагента(Кнопка) НачатьТранзакцию(); КонтрагентОбъект = КонтрагентСсылка.ПолучитьОбъект(); КонтрагентОбъект.Наименование = КонтрагентОбъект.Наименование + » (изменения)»; КонтрагентОбъект.Записать(); ЗафиксироватьТранзакцию(); КонецПроцедуры
В транзакции считывается элемент справочника «Контрагенты». Чтение реализовано в объектной технике. При выполнении этой строки кода «1С:Предприятие» выполнит запрос к базе данных, который автоматически установит разделяемую блокировку для данного контрагента.
Затем в этой же транзакции реквизиты контрагента изменяются, и объект записывается в информационную базу. Перед записью будет автоматически установлена эксклюзивная блокировка для данного контрагента, то есть произойдет повышение уровня изоляции ресурса. В том случае если эта операция будет выполняться несколькими пользователями одновременно, возможно возникновение взаимоблокировок.
Для решения этой проблемы необходимо установить блокировку обновления для данного контрагента перед чтением. Единственный способ сделать это в автоматическом режиме – выполнить запрос с опцией «ДЛЯ ИЗМЕНЕНИЯ» аналогично тому, как это было сделано в предыдущем примере.
В этом случае процедура будет выглядеть следующим образом:
Процедура ИзменитьНаименованиеКонтрагента(Кнопка) НачатьТранзакцию(); Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | Контрагент.Ссылка |ИЗ | Справочник.Контрагент КАК Контрагент |ГДЕ | Контрагент.Ссылка = &Ссылка | |ДЛЯ ИЗМЕНЕНИЯ | Справочник.Контрагент"; Запрос.УстановитьПараметр("Ссылка", КонтрагентСсылка); Результат = Запрос.Выполнить(); // Установлена блокировка обновления КонтрагентОбъект = КонтрагентСсылка.ПолучитьОбъект(); КонтрагентОбъект.Наименование = КонтрагентОбъект.Наименование + " (изменения)"; КонтрагентОбъект.Записать(); ЗафиксироватьТранзакцию(); КонецПроцедуры
Управляемый режим. Чтение без установки блокировки (либо с установкой разделяемой блокировки) и последующая запись
Если в примере 1 перевести конфигурацию в управляемый режим блокировки данных в транзакции, то опция «ДЛЯ ИЗМЕНЕНИЯ» будет игнорирована «1С:Предприятием», и необходимой эксклюзивной блокировки перед чтением остатков не произойдет. Кроме того, разделяемая блокировка, которая автоматически установится на уровне СУБД, будет снята после завершения выполнения запроса (то есть до конца транзакции). Такая ситуация может привести к нарушению бизнес-логики системы.
Разделяемые блокировки чтения были сняты непосредственно после выполнения запросов на чтение, то есть к моменту времени t1. Последующая запись не блокируется, и взаимоблокировки не происходит. Вместо этого обе транзакции по очереди записывают измененные остатки. Обратите внимание на то, что контроль остатков при этом работает некорректно. Например, на складе было 5 единиц товара. Обе транзакции могут беспрепятственно списать по 5 единиц товара, то есть остатки на складе после выполнения обеих транзакций будут равны -5.
Обратите внимание на то, что взаимоблокировки в данном случае не произошло. Вместо этого была нарушена бизнес-логика системы (списаны товары, отсутствующие на складе), что по серьезности последствий намного хуже, чем взаимоблокировка.
Источником этой проблемы является низкий уровень изоляции транзакции на уровне СУБД. Разделяемые блокировки, которые были автоматически установлены в момент чтения остатков, снялись непосредственно после выполнения запроса и остались незащищенными (то есть незаблокированными) до конца транзакции. Отсюда следует необходимость установить такую блокировку, которая бы защищала итоги до окончания транзакции. Для решения таких задач предназначены явные управляемые блокировки, устанавливаемые из кода конфигурации.
Подробная информация о том, как работать с управляемыми блокировками, содержится в данной статье базы знаний.
Для данного примера измененный текст функции должен быть следующим:
Функция ПроверитьОстатки() Блокировка = Новый БлокировкаДанных; ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах"); ЭлементБлокировки.ИсточникДанных = ЭтотОбъект.Товары; ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Склад", "Склад"); ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура"); Блокировка.Заблокировать(); Запрос = Новый Запрос; //… И так далее по тексту функции без изменений
Обратите внимание на то, что для блокировки выбран режим «Исключительный». Это сделано в соответствии со сформулированным выше правилом: необходимо изначально устанавливать блокировку с наивысшим требуемым уровнем изоляции ресурсов.
Если в данном случае установить режим блокировки «Разделяемый», то при одновременной работе нескольких пользователей возможно возникновение взаимоблокировки на уровне «1С:Предприятия».
Захват ресурсов в разном порядке
Общие сведения
Захват ресурсов в разном порядке является второй наиболее распространенной причиной возникновения взаимоблокировок.
В отличие от взаимоблокировки, возникающей при повышении уровня изоляции ресурса в рамках одной транзакции, данная блокировка возникает не на одном ресурсе, а минимум на двух (или более).
Схема возникновения взаимоблокировки для двух ресурсов такова. В начале две конкурирующие транзакции (Т1 и Т2) захватывают два разных ресурса – Р1 и Р2. Устанавливаемые при этом блокировки не мешают друг другу (так как заблокированы разные ресурсы), поэтому к моменту времени t1 обе транзакции успешно блокируют ресурсы и продолжают работу.
Затем транзакция Т1 пытается заблокировать ресурс Р2. Однако это невозможно, поскольку он уже заблокирован транзакцией Т1. В свою очередь, транзакция Т2 пытается заблокировать ресурс Р2, но это также невозможно, поскольку он уже захвачен транзакцией Т1.
Возникает взаимоблокировка: обе транзакции находятся в режиме ожидания и не могут ни продолжить работу, ни освободить заблокированные ими ресурсы.
В данном примере все блокировки установлены в эксклюзивном режиме, однако это не является необходимым условием. Если бы первая пара блокировок была разделяемой, а вторая – эксклюзивной (или наоборот), то взаимоблокировка также возникла бы. Из-за этого взаимоблокировку из-за разного порядка захвата ресурсов иногда можно спутать со взаимоблокировкой из-за повышения уровня изоляции ресурса.
Также не имеет значения, какие именно действия транзакций привели к захвату ресурсов. Это могут быть операции чтения или записи ресурсов, а также установка управляемых блокировок в явном виде из кода конфигурации.
Причина возникновения взаимоблокировки данного вида – разный порядок захвата ресурсов в транзакции. Исходя из этого можно сформулировать следующее правило: ресурсы в транзакции должны блокироваться в одинаковом порядке. Если это по каким-то причинам невозможно (например, противоречит требованиям алгоритмов обработки данных), то следует использовать искусственную блокировку, которая обеспечит нужный порядок. Пример такой блокировки дан ниже в этой статье.
При блокировании ресурсов в одинаковом порядке картина изменится следующим образом:
В этом случае транзакция Т1 успевает первой заблокировать ресурс Р1. Транзакция Т2 пытается установить аналогичную блокировку, но не может этого сделать и становится в ожидание на блокировке.
Транзакция Т1 продолжает работу, выполняет все необходимые действия (при этом, в частности, захватывает ресурс Р2) и завершается. После завершения транзакции все установленные ей блокировки автоматически снимаются и транзакция Т2 продолжает работу. Ей также никто не мешает, и она успешно выполняет все необходимые действия, включая установку блокировок на ресурсы.
Особенности взаимоблокировок данного вида
Взаимоблокировки первого вида (возникающие по причине повышения уровня изоляции ресурса в транзакции) обычно проявляются при одновременной работе однотипных транзакций. Взаимоблокировки второго вида (возникающие из-за разного порядка захвата ресурсов в транзакции), как правило, проявляются при выполнении пользователями разных действий. Например, первый пользователь может вводить документ «РеализацияТоваров», а второй – «ПоступлениеТоваров».
Эта особенность делает взаимоблокировки данного вида неудобными для анализа на уровне кода. Если нам известна только строка кода, на которой жертва взаимоблокировки получила сообщение об ошибке, то мы не имеем следующей необходимой информации:
- Какое действие выполняла конкурирующая транзакция?
- Какие еще ресурсы, кроме захваченного в данной строке кода, участвовали во взаимоблокировке?
Как правило, для анализа взаимоблокировок данного вида необходимо использовать информацию, предоставляемую «Центром управления производительностью».
Изначально разрабатывать приложение таким образом, чтобы в нем всегда использовался одинаковый порядок захвата ресурсов, – достаточно сложная организационная и техническая задача. Как правило, такие взаимоблокировки приходится анализировать и устранять по факту их возникновения во время многопользовательского нагрузочного тестирования либо при работе реальных пользователей системы.
Типичные причины возникновения взаимоблокировок данного вида таковы:
- Явный захват ресурсов без учета порядка;
- Захват ресурсов в разном порядке в соответствии с требованиями алгоритмов обработки данных;
- Непредсказуемый порядок захвата ресурсов в СУБД при выполнении сложных запросов.
Захват ресурсов без учета порядка
При разработке кода конфигурации необходимо обеспечить правильную работу приложения в рамках одной транзакции. Это понятная и разрешимая задача. Однако взаимоблокировка данного вида возникает при конкурентной работе разных транзакций. Для того чтобы исключить потенциальную возможность таких взаимоблокировок, необходимо было бы анализировать код всех имеющихся в системе транзакций на предмет одинакового порядка захвата ресурсов. Для сложных конфигураций такая задача может оказаться невыполнимой.
Однако можно сформулировать несколько несложных правил, выполнение которых позволит значительно снизить риск возникновения взаимоблокировок данного вида:
- Избегать захвата ресурсов в явном виде. Некоторые действия (например, запись движений документа) платформа выполняет автоматически. При этом захват ресурсов гарантированно производится в одинаковом порядке. Для исключения возможности ошибки не следует прописывать в явном виде в коде конфигурации те действия, которые платформа может выполнить автоматически.
- Везде, где это возможно, придерживаться одного и того же определенного порядка захвата ресурсов.
Предположим, что в конфигурации определены два вида документов: «РеализацияТоваров» и «ПоступлениеТоваров». Документы записывают движения в два регистра накопления: «ТоварыНаСкладах» и «ТоварыОрганизаций». При этом движения заполняются и записываются в явном виде в обработчиках проведения документов.
При написании этого кода разработчики не учли порядок захвата ресурсов, в результате чего движения записываются в разном порядке. Например, в документе «РеализацияТоваровУслуг»:
// Заполнение таблицы движений по регистру ТоварыНаСкладах … Движения.ТоварыНаСкладах.Записать(); //… // Заполнение таблицы движений по регистру ТоварыОрганизаций … Движения.ТоварыОрганизаций.Записать();
При этом в документе «ПоступлениеТоваров» те же движения записываются в обратном порядке:
// Заполнение таблицы движений по регистру ТоварыНаСкладах … Движения.ТоварыОрганизаций.Записать(); //… // Заполнение таблицы движений по регистру ТоварыОрганизаций … Движения.ТоварыНаСкладах.Записать();
В этой ситуации возможно возникновение взаимоблокировок при конкурентном вводе документов этих двух видов. Фактически блокировки будут происходить не на таблицах движения этих регистров, а на таблицах остатков.
Для того чтобы избежать этих взаимоблокировок, можно отказаться от записи движений в явном виде. В этом случае движения будут записываться платформой, которая будет делать это гарантированно в одинаковом порядке.
Другое решение – обеспечить одинаковый порядок записи движений организационными мерами. Например, подготовить проектный документ, который описывает рекомендуемый порядок захвата ресурсов и обеспечить выполнение этих рекомендаций всеми программистами системы.
Обратите внимание на то, что ресурсы захватываются (блокируются) не только при записи движений. Полный перечень операций, при которых происходит захват ресурсов:
- Модификация любых данных в системе: методы Записать(), Удалить() и т. п. любых объектов метаданных.
- Чтение в объектной технике и выполнение запросов в транзакции. Такие операции не могут приводить к взаимоблокировкам друг с другом (чтение ресурса в транзакции не блокирует другое чтение), но могут приводить к взаимоблокировкам с операциями модификации данных (запись ресурса блокируется чтением в транзакции).
- Выполнение запросов в транзакции с опцией «ДЛЯ ИЗМЕНЕНИЯ».
- Установка явных блокировок из кода конфигурации в управляемых транзакциях.
Захват ресурсов в разном порядке в соответствии с требованиями алгоритмов обработки данных
Алгоритмы обработки информации могут требовать строго определенного порядка захвата ресурсов, и для разных алгоритмов этот порядок может быть различным. В этом случае задача может оказаться неразрешимой либо потребовать серьезного пересмотра структур данных и алгоритмов.
Возможна ситуация, при которой такой порядок записи движений, как в предыдущем примере, применен осознанно в связи с тем, что таков алгоритм обработки информации. Например, документ «РеализацияТоваров» должен сначала записать регистр «ТоварыНаСкладах», затем при помощи запросов к этому и другим регистрам произвести некоторые вычисления и только потом – на основании результатов этих вычислений – сформировать и записать движения регистра «ТоварыОрганизаций». В свою очередь, документ «ПоступлениеТоваров» может, наоборот, вычислять данные для регистра «ТоварыНаСкладах» на основании данных регистра «ТоварыОрганизаций».
Для того чтобы обеспечить одинаковый порядок захвата ресурсов в этом случае, может потребоваться глубокий пересмотр алгоритмов обработки информации. Поэтому в такой ситуации можно применить другой способ – установить искусственную блокировку на ресурс, которая обеспечит одинаковый порядок захвата при неизменном алгоритме.
Например, при работе в автоматическом режиме мы можем выполнить запрос с опцией «ДЛЯ ИЗМЕНЕНИЯ» к виртуальной таблице остатков регистра «ТоварыОрганизаций» в обработчике проведения документа «РеализацияТоваров». В качестве условия в этот запрос следует передать набор значений измерений, который будет изменяться в этой транзакции (то есть выборку из табличной части документа). В этом случае последовательность установки блокировок будет такая же, как в документе «ПоступлениеТоваров»: сначала будет блокироваться регистр «ТоварыОрганизаций», а затем «ТоварыНаСкладах». Взаимоблокировка при этом будет исключена.
При работе в управляемом режиме вместо запроса с опцией «ДЛЯ ИЗМЕНЕНИЯ» следует прописать в коде обработчика проведения явную блокировку пространства «РегистрНакопления.ТоварыОрганизаций» по соответствующему набору значений измерений.
Непредсказуемый порядок захвата ресурсов в СУБД при выполнении сложных запросов
При выполнении сложных запросов, включающих соединение нескольких таблиц, не всегда можно точно предсказать, в каком порядке эти таблицы будут обрабатываться СУБД. Этот порядок будет зависеть от выбора плана запроса, а план, в свою очередь, от множества непредсказуемых факторов – распределения данных по индексам, размеров таблиц, нагрузки на систему и т. д.
Иначе говоря, возможны такие ситуации, когда СУБД будет устанавливать блокировки при выполнении одного и того же запроса к нескольким ресурсам в разном порядке. При этом также возможно возникновение взаимоблокировок. Для устранения таких взаимоблокировок необходимо также установить искусственные блокировки на ресурсы в предопределенном порядке.
Неоптимальная работа запроса
При неоптимальной работе запроса блокируются избыточные ресурсы, что может значительно увеличить вероятность возникновения взаимоблокировок. Если взаимоблокировка возникла при выполнении прикладного запроса, следует провести анализ и оптимизацию его текста в соответствии с рекомендациями, данными в статье «Типичные причины неоптимальной работы запросов и методы оптимизации».
Ошибка блокировок при работе внутренних механизмов MS SQL Server
Известны случаи возникновения взаимоблокировок при работе внутренних механизмов MS SQL Server. О возникновении таких взаимоблокировок говорят следующие сообщения об ошибках:
- «Intra-query parallelism caused your server command (process ID #XX) to deadlock. Rerun the query without intra-query parallelism by using the query hint option (maxdop 1)».
- «Transaction (Process ID #XX) was deadlocked on thread communication buffer resources with another process and has been chosen as the deadlock victim».
Обычно для возникновения взаимоблокировки необходимо участие четырех и более запросов (по два в двух конкурирующих транзакциях). Однако при выполнении некоторых запросов MS SQL Server может самостоятельно повышать уровень изоляции заблокированных ресурсов, что иногда также приводит к возникновению взаимоблокировки. В этом случае возможна взаимоблокировка, в которой участвуют всего два запроса от двух конкурирующих транзакций. Для возникновения такой взаимоблокировки необходимо одновременное выполнение следующих условий:
- Транзакция работает в автоматическом режиме блокировки данных.
- Выполняется прикладной запрос с опцией «ДЛЯ ИЗМЕНЕНИЯ».
- При выполнении запроса выполняется сканирование таблицы или индекса (TABLE SCAN или INDEX SCAN в плане запроса).
Соответственно, для устранения такой взаимоблокировки необходимо исключить любое из трех условий:
- Перевести данную транзакцию (или все приложение) в управляемый режим блокировки данных.
- Не использовать опцию «ДЛЯ ИЗМЕНЕНИЯ», если в этом нет необходимости.
- Оптимизировать запрос таким образом, чтобы вместо сканирования выполнялся поиск по индексу.
Анализ взаимоблокировок при помощи ЦУП
Как уже было отмечено выше, взаимоблокировки не всегда поддаются анализу на основании одной строки кода – той, в которой было получено сообщение об ошибке взаимоблокировки. Если исследуемая база работает с использованием MS SQL Server 2005, то есть возможность получить детальную информацию по взаимоблокировке с помощью Центра управления производительностью.
При анализе взаимоблокировок с помощью ЦУП нужно учитывать следующий фактор. Одна ошибка в прикладном коде конфигурации (например, отсутствие опции «ДЛЯ ИЗМЕНЕНИЯ» в запросе, контролирующем остатки) может привести к возникновению множества разнообразных взаимоблокировок. Они могут существенно отличаться друг от друга по сложности, в том числе включать различное количество конкурирующих транзакций. Всегда имеет смысл начинать анализ с более простой взаимоблокировки – с понятной вам схемой и с минимальным количеством участников. Обнаружив и устранив ее причину, вы можете одновременно избавиться от множества других (в том числе более сложных) взаимоблокировок.
ЦУП пытается автоматически ранжировать взаимоблокировки по сложности, показывая на верхних позициях в списках более простые случаи. Однако оценить сложность взаимоблокировки возможно не всегда. Не стоит тратить много времени, пытаясь разобраться в сложной для понимания взаимоблокировке. Следует выбрать из списка более простой и понятный вам случай.
Для получения информации по взаимоблокировке необходимо выполнить следующую последовательность действий:
1. Настроить права доступа и подключиться к исследуемой информационной базе в режиме мониторинга (см. руководство по использованию ЦУП).
2. Добавить показатель «Анализ взаимоблокировок» в список показателей ЦУП.
3. Включить запись этого показателя.
Внимание!
При включении записи аналитических показателей может наблюдаться падение производительности в исследуемой информационной базе.4. Воспроизвести взаимоблокировку в исследуемой информационной базе либо дождаться, пока взаимоблокировка возникнет во время работы пользователей. О возникновении взаимоблокировки можно судить по значениям показателя «Количество взаимоблокировок».
Внимание!
После включения записи аналитического показателя ЦУП начинает сбор информации примерно через 1 минуту. Необходимо выдержать паузу между включением записи показателя и воспроизведением взаимоблокировки.5. Выключить запись показателя «Анализ взаимоблокировок». После выключения записи ЦУП начнет разбор и анализ собранной информации. Этот процесс может занять длительное время.
6. Остановить работу сценария «мониторинг» и подключиться к исследуемой базе в режиме просмотра. При необходимости выбрать интервал времени, на котором наблюдалась интересующая вас взаимоблокировка.
7. Нажать кнопку «Анализ».
8. В форме анализа проблем производительности выбрать закладку «Код конфигурации», раскрыть первую вершину дерева и двойным щелчком открыть форму анализа взаимоблокировок:
9. Будет открыта форма списка взаимоблокировок, в которых участвует выбранный источник проблем – строка кода конфигурации. Группы взаимоблокировок объединяют одинаковые (по схеме) взаимоблокировки, произошедшие в разное время. Поскольку в системе может наблюдаться множество различных взаимоблокировок, следует проанализировать каждую группу. Из группы следует выбирать наиболее простую взаимоблокировку.
Для того чтобы открыть взаимоблокировку в отдельном окне, следует дважды щелкнуть по ней мышкой.
10. Провести анализ схемы взаимоблокировки и причин ее возникновения.
Схема отображает все процессы, попавшие во взаимоблокировку, и полную хронологическую последовательность блокировок, установленных каждым процессом. При этом указываются не все блокировки, а только те, которые имеют (или могут иметь) отношение к данной взаимоблокировке. Для каждой блокировки указан контекст ее установки, то есть строка кода конфигурации, при выполнении которой она была установлена.
Далее приведены примеры анализа взаимоблокировок двух основных видов:
- Взаимоблокировка из-за повышения уровня блокировки ресурса в рамках одной транзакции.
- Взаимоблокировка из-за захвата ресурсов в разном порядке.
Примеры получены на измененной конфигурации УПП при помощи теста «Продажи» и доступны для самостоятельного изучения в демонстрационной базе ЦУП.
Повышение уровня блокировки ресурса в рамках одной транзакции
Рассмотрим пример взаимоблокировки, возникшей в результате повышения уровня блокировки ресурса в рамках одной транзакции:
Во взаимоблокировке участвуют два процесса (транзакции):
Для каждого процесса показана хронология событий, которая привела к возникновению взаимоблокировки.
Вначале оба процесса выполняют запросы, в результате которых успешно устанавливаются две блокировки:
Затем оба процесса пытаются выполнить два других запроса, но при установке блокировок возникает взаимное ожидание, то есть взаимоблокировка:
В этой взаимоблокировке можно видеть следующие характерные признаки взаимоблокировок данного вида (возникающих по причине повышения уровня блокировки ресурса):
1. Во взаимоблокировке участвует один ресурс – регистр накопления «ТоварыВРезервеНаСкладах»:
2. Процессы являются однотипными, то есть выполняются одинаковые прикладные действия и, соответственно, работает один и тот же код конфигурации:
Откроем конфигурацию исследуемой базы и проанализируем запрос, который исполняется в строке 506 модуля набора записей регистра накопления «ТоварыКПередачеСоСкладов». При этом нас будет интересовать только обращение к заблокированному ресурсу – регистру «ТоварыВРезервеНаСкладах».
Ниже приведен сокращенный текст процедуры КонтрольСвободныхОстатков_Реализация(), из которой вызывается интересующий нас запрос.
Процедура КонтрольСвободныхОстатков_Реализация() … Запрос = Новый Запрос; … ТекстЗапроса = " |ВЫБРАТЬ // Запрос, контролирующий остатки на складах | Док.Номенклатура.Представление КАК НоменклатураПредставление, | … |ЛЕВОЕ СОЕДИНЕНИЕ | РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(, " + УсловияТаблицыОстатков.БезКачества + ") КАК Резервы |ПО … | … |ДЛЯ ИЗМЕНЕНИЯ | РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки"; … Выборка = Запрос.Выполнить().Выбрать(); // Строка 506
При выполнении данного запроса идет чтение остатков регистра ТоварыВРезервеНаСкладах. При этом будет установлена разделяемая блокировка, так как опция «ДЛЯ ИЗМЕНЕНИЯ» применена только к регистру «ТоварыКПередачеСоСкладов».
Затем проанализируем код, который исполняется во второй интересующей нас строке: модуль набора записей регистра накопления «ТоварыВРезервеНаСкладах», строка 395. Нас по-прежнему будут интересовать только обращения к регистру «ТоварыВРезервеНаСкладах».
Процедура КонтрольОстатков_Реализация() … Запрос = Новый Запрос; … ТекстЗапроса = " |ВЫБРАТЬ // Запрос, контролирующий остатки на складах | Док.Номенклатура.Представление | … |ЛЕВОЕ СОЕДИНЕНИЕ | РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(," + УсловияТаблицыОстатков.Резерв + ") КАК Резервы |ПО … |… |ДЛЯ ИЗМЕНЕНИЯ | РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки"; … Выборка = Запрос.Выполнить().Выбрать(); // Строка 395
При выполнении этого запроса также производится чтение остатков регистра «ТоварыВРезервеНаСкладах», но они читаются с опцией «ДЛЯ ИЗМЕНЕНИЯ», то есть при этом будет установлена эксклюзивная блокировка.
Таким образом, регистр «ТоварыВРезервеНаСкладах» блокируется в рамках одной транзакции сначала разделяемой блокировкой, а затем эксклюзивной. Это и является причиной возникновения данной взаимоблокировки.
Для исправления этой ошибки необходимо изначально заблокировать остатки регистра «ТоварыВРезервеНаСкладах» с максимальным необходимым уровнем изоляции, то есть с опцией «ДЛЯ ИЗМЕНЕНИЯ». При этом код процедуры изменится следующим образом:
Процедура КонтрольСвободныхОстатков_Реализация() … Запрос = Новый Запрос; … ТекстЗапроса = " |ВЫБРАТЬ // Запрос, контролирующий остатки на складах | Док.Номенклатура.Представление КАК НоменклатураПредставление, | … |ЛЕВОЕ СОЕДИНЕНИЕ | РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(, " + УсловияТаблицыОстатков.БезКачества + ") КАК Резервы |ПО … | … |ДЛЯ ИЗМЕНЕНИЯ | РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки, | РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки"; … Выборка = Запрос.Выполнить().Выбрать(); // Строка 506
Захват ресурсов в разном порядке
Рассмотрим пример взаимоблокировки, возникшей в результате захвата ресурсов в разном порядке:
В этой взаимоблокировке можно видеть следующие характерные признаки взаимоблокировок данного вида (возникающие по причине захвата ресурсов в разном порядке):
1. Во взаимоблокировке участвуют несколько (в данном случае два) ресурсов. При этом они захватываются в разном порядке.
2. Процессы являются разнотипными, то есть выполняются разные прикладные действия и, соответственно, разный код конфигурации.
Рассмотрим действия, которые выполняют эти два процесса, и блокировки, которые при этом устанавливаются.
1. Действия и блокировки процесса 1:
1.1. Процесс 1 выполняет запрос контроля остатков в строке 506 модуля набора записей регистра накопления «ТоварыКПередачеСоСкладов». Рассмотрим фрагменты кода процедуры «КонтрольСвободныхОстатков_Реализация», обращая внимание только на обращения к таблице «ТоварыНаСкладах.Остатки».
Процедура КонтрольСвободныхОстатков_Реализация() … Запрос = Новый Запрос; … ТекстЗапроса = " |ВЫБРАТЬ // Запрос, контролирующий остатки на складах | Док.Номенклатура.Представление КАК НоменклатураПредставление, | … |ЛЕВОЕ СОЕДИНЕНИЕ | (ВЫБРАТЬ | … | ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки(, " + УсловияТаблицыОстатков.Полное + ") КАК ТоварыНаСкладахОстатки | … |ДЛЯ ИЗМЕНЕНИЯ | РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки"; … Выборка = Запрос.Выполнить().Выбрать(); // Строка 506
В результате выполнения этого запроса успешно устанавливается разделяемая блокировка на остатки регистра «ТоварыНаСкладах».
При двойном клике на первой блокировке процесса 1 откроется форма подробной информации о блокировке. Эта форма, в частности, содержит ссылку на выполнение интересующего нас запроса:
Открыв форму выполнения запроса, мы увидим полный стек вызовов кода конфигурации. Из этого стека становится понятно что интересующий нас запрос выполнялся при проведении документа «РеализацияТоваровУслуг» (в коде тестовой обработки соответствующая переменная имеет имя «Реализация»).
1.2. Следующее действие, выполненное процессом 1, имеет этот же стек вызова, но он заканчивается на строке «Реализация.Записать».
Это означает, что оба действия были выполнены в результате проведения одного документа «РеализацияТоваровУслуг», но первое действие было вызвано в явном виде из кода конфигурации, а второе – выполнено автоматически платформой.
Посмотрим, какой SQL-запрос был сгенерирован платформой при выполнении этого действия.
Это обновление таблицы остатков регистра накопления «ТоварыКПередачеОрганизаций». При выполнении этого действия была установлена эксклюзивная блокировка на таблицу остатков этого регистра. Однако эта блокировка находится в режиме ожидания, так как имеется несовместимая с ней блокировка процесса 2.
2. Действия и блокировки процесса 2:
Процесс 2 выполняет одно-единственное действие – запись и проведение документа «РасходныйОрдер». Оба действия, имеющие отношение к взаимоблокировке, выполняются платформой автоматически при проведении документа.
2.1. Первое действие: обновление остатков регистра накопления «ТоварыКПередачеОрганизаций»:
В результате этого действия устанавливается эксклюзивная блокировка на соответствующие остатки регистра «ТоварыКПередачеОрганизаций». Именно эта блокировка мешает процессу 1 установить аналогичную блокировку при выполнении действия 1.2.
2.2. Второе действие: обновление остатков регистра накопления «ТоварыНаСкладах»:
В результате этого действия устанавливается эксклюзивная блокировка на соответствующие остатки регистра «ТоварыНаСкладах». Однако в системе уже имеется несовместимая блокировка на эти же записи регистра, которая была установлена процессом 1 при выполнении действия 1.1. По этой причине блокировка процесса 2 устанавливается в режиме ожидания.
Итак, процесс 1 ожидает окончания транзакции процесса 2, в то время как процесс 2 ожидает окончания транзакции процесса 1. Получаем взаимоблокировку по причине захвата ресурсов в разном порядке. Процесс 1 захватил сначала регистр накопления «ТоварыНаСкладах», а затем регистр «ТоварыКПередачеОрганизаций». Процесс 2 захватил эти же регистры, но в обратном порядке.
Все запросы, попавшие во взаимоблокировку, выполняются при автоматической записи движений по регистрам при проведении документа. Проблема в том, что в документе «РеализацияТоваровУслуг» при записи движений в регистр «ТоварыКПередачеСоСкладов» захватывается таблица остатков регистра «ТоварыНаСкладах». Именно это является причиной взаимоблокировки, однако обойтись без чтения этой таблицы, очевидно, нельзя, поскольку это часть алгоритма обработки данных.
Можно было бы попробовать изменить порядок записи движений в документе «РасходныйОрдер», прописав в обработчике проведения этого документа следующий код:
Движения.ТоварыНаСкладах.Записать(); Движения.ТоварыКПередачеОрганизаций.Записать();
Таким образом мы избавились бы от рассматриваемой взаимоблокировки, но создали бы опасность возникновения других взаимоблокировок. Для полной уверенности в их отсутствии нам пришлось бы проанализировать весь код конфигурации в поисках транзакций, которые записывают движения по этим двум регистрам, и везде произвести запись в явном виде и в нужном порядке. Кроме того, пришлось бы принимать организационные меры для того, чтобы разработчики впредь всегда записывали движения по этим регистрам только в явном виде и только в заданном порядке.
Поэтому в данном случае рекомендуется другой способ устранения взаимоблокировки – установка искусственной блокировки. Поскольку процесс 2 (проведение документа «РасходныйОрдер») выполняет захват ресурсов автоматически, будем считать именно этот порядок правильным. Процесс 1 («РеализацияТоваровУслуг») захватывает ресурсы в неправильном порядке – сначала блокируется регистр «ТоварыНаСкладах», затем – «ТоварыКПередачеОрганизаций». Именно этот порядок должен быть изменен.
Для этого следует выполнить запрос с опцией «ДЛЯ ИЗМЕНЕНИЯ» к таблице остатков регистра «ТоварыКПередачеОрганизаций». Этот запрос должен быть выполнен перед запросом в строке 506 модуля набора записей регистра «ТоварыКПередачеСоСкладов». Условия запроса должны быть такими, чтобы считать (и, соответственно, заблокировать) все остатки, которые будут изменены при записи набора записей в действии 1.2.
Существует еще один вариант устранения этой взаимоблокировки. Блокировки, устанавливаемые процессом 1 (документ «РеализаяТоваровУслуг»), возникают только при работе в автоматическом режиме. Характерным признаком таких блокировок является значение поля режим = «Range…»:
Эти блокировки полностью исчезают при переходе в управляемый режим блокировки данных в транзакции. То есть данная взаимоблокировка будет невозможна при работе в управляемом режиме.
Блокировки данных в 1С:Предприятии 8
Данная статья является фрагментом книги П.С.Белоусов, А.В.Островерх «1С:Предприятие: от 8.0 к 8.1».
Дата выхода книги: ноябрь 2007.При одновременной работе нескольких пользователей с одной и той же информационной базой периодически возникают ситуации, когда два или более пользователей пытаются не только одновременно прочитать одни те же данные, но и внести в них какие-то изменения. Очевидно, что если в таких случаях не предусмотреть возможности подключения специальных механизмов системы, то могут возникнуть проблемы, связанные с целостностью и достоверностью данных, хранимых в информационной базе.
Описанию возникающих проблем, а также путей их решения в системе 1С:Предприятия 8.1 и посвящена данная статья.
Что такое блокировка
Прежде чем начать изучение механизмов 1С:Предприятия, обеспечивающих конкурентный доступ пользователей к данным, познакомимся с тем, что такое блокировки, когда они возникают и можно ли обойтись без блокировок.
В общем случае блокировка — это информация о том, что данный ресурс захвачен «кем-то», для выполнения какого-то действия.
Рассмотрим простой пример. Продавец продает яблоки. Так получилось, что продавец очень рассеян и ему приходится абсолютно все записывать. По этой же причине все яблоки у него пронумерованны.
Пришел покупатель Иванов и ему понравилось яблоко №4. Он хочет его купить. Иванов достает кошелек и отсчитывает деньги (рис. 1).
Рис. 1. Иванов хочет купить яблоко №4
Тем временем, продавец делает запись в своей книге: «Яблоко №4 — продано Иванову». Эта запись и есть блокировка (рис. 2).
Рис. 2. Продавец «заблокировал» яблоко №4
Обратите внимание, что на самом деле яблоко все еще находится у продавца, Иванов его не купил. Может быть и не сможет купить (например окажется, что не хватает денег). Но у продавца уже записано, что это яблоко нельзя предлагать другим покупателям до тех пор, пока Иванов не завершит процесс покупки. Этот процесс, состоящий из нескольких взаимосвязанных действий (выбор яблока, отсчитывание денег, передача денег продавцу, передача яблока покупателю) называется транзакцией. Блокировка должна быть установлена в момент выбора Ивановым яблока №4 и снята после завершения транзакции покупки.
Тем временем подходит Петров и тоже хочет купить яблоко. Он сможет купить любое яблоко, кроме яблока №4 (рис. 3).
Рис. 3. Петров сможет купить любое яблоко, кроме яблока №4
Таким образом смысл блокировки в том, чтобы запретить некоторые действия над общим ресурсом на некоторое ограниченное время. В данном случае Петрову запрещено выбирать яблоко №4 до тех пор, пока Иванов не завершил свою транзакцию покупки. То есть, Петров находится в состоянии ожидания на блокировке.
Из приведенного примера понятно, что блокировки — это необходимый механизм при конкурентном доступе к общим ресурсам. В самом деле, что бы было, если бы продавец не записал в своей книге, что яблоко из четвертой ячейки нельзя предлагать другим покупателям? Скорее всего произошел бы конфликт между Ивановым и Петровым, возможно при этом «досталось» бы и продавцу. В любом случае, если запись в книге продавца отсутствует, исход данной ситуации становится непредсказуемым. Неизвестно, кому достанется это яблоко, неизвестно у кого окажутся деньги, которые Иванов за него отдал и так далее.
Если же блокировка на яблоко №4 установлена, то это гарантирует однозначный исход: Иванов гарантированно сможет купить яблоко, если у него хватит денег. Если же он откажется от покупки, то только в этом случае яблоко из 4 ячейки сможет купить Петров.
Следует понимать, что в силу различных причин блокировки могут быть как «хорошими» (необходимыми), так и «плохими» (избыточными).
Рассмотрим еще один вариант развития событий, который поясняет откуда берутся «плохие» блокировки.
Покупатель Иванов хочет купить одно яблоко. Он перебирает все яблоки из ящика по одному, выбирая, какое лучше. При этом продавец записывает в своей книге все яблоки, которые понравились Иванову (рис. 4).
Рис. 4. Продавец блокирует все яблоки, которые нравятся Иванову
В это время подходит Петров и не может купить ни одного яблока, потому что они все заблокированы Ивановым (рис. 5).
Рис. 5. Петров не может купить ни одного яблока
Петров ждет некоторое время, обижается и уходит (рис. 6). Это событие соответствует ошибке «Превышение времени ожидания блокировки».
Рис. 6. Петров не дождался и ушел
А Иванов, в результате, выбирает одно единственное яблоко (самое лучшее) и покупает только его (рис. 7).
Рис. 7. Иванов покупает только одно яблоко
Таким образом все остальные яблоки были заблокированы зря. Если бы этих блокировок не было, то Петров, возможно, тоже купил бы яблоко.
В данном случае (в отличие от первого примера) Петров как раз столкнулся с «плохими» (избыточными) блокировками.
Подводя итог сказанному важно отметить, что «хорошие» блокировки обязательно должны присутствовать в прикладном решении. Именно благодаря им обеспечивается предсказуемость действий пользователей, целостность и непротиворечивость данных.
С «плохими» блокировками нужно бороться и в идеале их не должно существовать в прикладном решении. Причины возникновения плохих блокировок могут быть самыми разнообразными: прикладная логика, особенности работы той или иной СУБД и т.д. В данной статье мы познакомимся лишь с механизмами системы 1С:Предприятие 8.1, которые используют блокировки и дадим рекомендации по правильному их использованию. Тема же анализа существующих блокировок и их оптимизации достаточно сложная и объемная, и выходит за рамки данной статьи.
Объектные и транзакционные блокировки
В системе 1С:Предприятие 8 существуют два механизма, при работе которых используется термин блокировка. Зачастую это приводит к путанице и позволяет думать, что речь идет об одних и тех же блокировках или об одном и том же механизме. На самом деле это не так. Каждый из этих механизмов предназначен для обеспечения конкуретной работы пользователей, однако в своей, определенной области, и при этом блокировки, используемые одним и другим механизмами, имеют совершенно различный смысл (рис. 8).
Рис. 8. Объектные и транзакционные блокировки
Логическая модель данных 1С:Предприятия 8 предполагает, что на самом «верхнем» уровне абстрации пользователь имеет дело с объектами системы, как совокупностью неделимых данных. Такими объектами, например, являются элементы справочников, документы и др. Элемент справочника может содержать большое количество реквизитов, несколько табличных частей, но все эти данные необходимо изменять одновременно и согласованно. Механизм объектных блокировок как раз и позволяет осуществлять конкурентный доступ пользователей к данным 1С:Предприятия в терминах объектов информационной базы. Как правило в большинстве случаев это связано с интерактивной работой пользователей в формах: редактирование существующих объектов, удаление, создание новых и др.
В то же время все данные информационной базы хранятся в некоторой СУБД. А любая СУБД должна обеспечивать целостность и непротиворечивость хранимых данных. Для согласованного изменения данных в СУБД используется механизм транзакций, а для обеспечения конкурентного доступа к данным — механизм транзакционных блокировок .
Таким образом и тот, и другой механизмы обеспечивают конкурентную работу пользователей, однако области их действия и смысл устанавливаемых блокировок являются совершенно разными.
Далее рассмотрим работу этих двух механизмов более подробно.
Механизм объектных блокировок
Объектная пессимистическая блокировка
Пессимистическая блокировка объектов базы данных предназначена для того, чтобы запретить изменение данных определенного объекта другими сеансами или данным сеансом до тех пор, пока блокировка не будет снята этим объектом встроенного языка.
В основном механизм пессимистической блокировки используется системой 1С:Предприятие 8.1 для блокировки объектов, редактируемых в форме. В тот момент, когда пользователь начинает модификацию объекта в форме, расширение формы устанавливает пессимистическую блокировку. Если после этого другой пользователь, например, попытается выполнить редактирование того же объекта, ему будет выдано сообщение о том, что не удалось заблокировать объект. Когда пользователь, редактировавший объект, закроет форму объекта, расширение формы снимет пессимистическую блокировку.
Рассмотрим пример. Войдем в прилагаемую к работе информационную базу под пользователем Иванов , откроем форму элемента 1С:Предприятие 8.0. Управление торговлей справочника Номенклатура (код 12) и изменим цену продажи с 420,00 на 450,00 . Не сохраняя сделанные изменения, войдем в информационную базу еще раз, но теперь под именем пользователя Петров . Откроем форму того же элемента справочника и попробуем изменить значение какого-либо реквизита. Любая попытка изменения приведет к появлению специального окна с сообщением об ошибке (рис. 9):
Рис. 9. Пример работы пессимистической блокировки
Таким образом пессимистическая блокировка гарантирует, что пользователь, начав изменять данные объекта, сможет записать эти изменения в информационную базу.
В то же время, разработчик имеет возможность задействовать рассматриваемый механизм, используя средства встроенного языка. Для того, чтобы установить пессимистическую блокировку объекта, можно использовать метод объекта Заблокировать() .
ВНИМАНИЕ
Важным отличием версии 8.1 платформы 1С:Предприятие является то, что сам по себе факт установки блокировки не препятствует изменению или удалению объекта в базе данных. Поэтому для того, чтобы обеспечить невозможность изменения заблокированного объекта, операции изменения объекта в другом сеансе должна также предшествовать попытка блокировки того же самого объекта. Блокировка заблокированного объекта базы данных вызывает исключение, которое может быть обработано конструкцией Попытка . Исключение . КонецПопытки .Для снятия пессимистической блокировки разработчик может использовать метод объекта Разблокировать() , причем использовать его для того же самого экземпляра объекта, для которого ранее была установлена блокировка.
Рассматривая возможность взаимного влияния механизмов объектных и транзакционных блокировок, напомним, что объектные блокировки не влияют на операции над данными и на процесс течения транзакций (обратите внимание, на рис 8 они расположены на разных уровнях работы с данными). Если блокировка объекта вызвала исключение, то оно, как было указано выше, может быть обработано разработчиком конфигурации и не приведет к обязательному откату транзакции. С другой стороны, блокировки объектов, установленные в течение транзакции, сохраняются при фиксации транзакции и снимаются при откате транзакции.
Объектная оптимистическая блокировка
Оптимистическая блокировка запрещает запись объекта в базу данных, если после считывания объекта он был изменен в базе данных другими сеансами или другими программными объектами этого же сеанса.
Фактически, оптимистическая блокировка представляет собой проверку, которая выполняется перед записью объекта в базу данных. Эта проверка построена на анализе номера версии объекта, хранящейся в базе данных и номера версии, помещенной в память компьютера в момент считывания данных из информационной базы. Если при записи объекта номера его версий отличаются, то будет выдано предупреждение о том, что версия объекта изменилась или он был удален, то есть сработает оптимистическая блокировка.
Рассмотрим пример. Откроем два сеанса работы с прилагаемой к работе информационной базой: один под пользователем Иванов , а другой — под пользователем Петров . В обоих сеансах откроем откроем форму элемента Управление торговлей справочника Номенклатура (код 12). Теперь в сеансе, открытом от имени пользователя Иванов , изменим цену продажи с 420,00 на 450,00 и запишем сделанные изменения. После этого, в сеансе, открытом от имени пользователя Петров попробуем изменить значение какого-либо реквизита. Любая попытка изменения приведет к появлению другого окна с сообщением об ошибке (рис. 10):
Рис. 10. Пример работы оптимистической блокировки
Таким образом оптимистическая блокировка гарантирует, что пользователь изменяет актуальные данные объекта, которые хранятся в информационной базе, а не какой-то их предыдущий вариант.
Механизм транзакционных блокировок
Общие сведения о транзакциях и блокировках СУБД
Прежде чем перейти к рассмотрению механизмов платформы 1С:Предприятие, познакомимся в общих чертах с понятиями, которые будут использованы далее.
Понятие транзакционной блокировки неразрывно связано с понятием транзакции.Транзакция — это неделимая, с точки зрения воздействия на базу данных, последовательность операций манипулирования данными, выполняющаяся по принципу «все или ничего», и переводящая базу данных из одного целостного состояния в другое целостное состояние. Если по каким-либо причинам одно из действий транзакции невыполнимо или произошло какое-либо нарушение работы системы, база данных возвращается в то состояние, которое было до начала транзакции (происходит откат транзакции).
Возможные проблемы при многопользовательском доступе к одним и тем же данным
Работа в многопользовательской среде требует соблюдения определенного компромисса между требованиями предсказуемости, целостности и непротиворечивости данных информационной базы и требованиями параллельности работы.
Как известно, при одновременном чтении и изменении одних и тех же данных конкурирующими транзакциями могут возникнуть следующие проблемы одновременного доступа:
- Проблема потерянного изменения (англ. The Lost Update Problem) — если две транзакции изменяют одни и те же данные, взяв в качестве первоисточника начальное значение этих данных, то в системе останутся изменения внесенные той транзакцией, которая записала свои изменения последней, поскольку эти изменения заменят собой все изменения, внесенные до этого.
Пример. Рассмотрим следующий пример. Допустим в справочнике Номенклатура , Транзакция №1 обратилась к элементу 1С:Предприятие 8.0. Управление торговлей и решила изменить значение реквизита ЦенаПродажи с 420 на 450 . Одновременно Транзакция №2 решила у этого же товара изменить значение реквизита ЕдиницаИзмерения со Штука на Коробка . Распределение по времени описанных действий показано на рис. 11. Таким образом, в элементе справочника остались только те изменения, которые сделала Транзакция №2.
Вывод. Нельзя одновременно изменять одни и те же данные;
- Проблема «грязного» чтения (англ. The Uncommitted Dependency Problem) — если одна транзакция начнет считывать некоторые данные не дождавшись окончания внесения изменений, вносимых в эти данные другой транзакцией, то достаточно вероятен случай, когда прочитанные данные будут содержать неверную информацию.
Пример. Вернемся к примеру, рассмотренному выше. Допустим в справочнике Номенклатура , Транзакция №1 обратилась к элементу 1С:Предприятие 8.0. Управление торговлей и изменила значение реквизита ЦенаПродажи с 420 на 450 . Не дождавшись фиксации изменений, Транзакция №2 использовала значение реквизита для определения суммы продажи. Однако, первая транзакция решила не сохранять внесенные изменения (откат транзакции) и восстановила старые данные. Графическое представление действий транзакций показано на рис. 12. Таким образом, Транзакция №2 в своих расчетах использовала данные, не существующие в системе.Вывод. Нельзя читать уже измененные, но еще не записанные данные.
Рис. 12. Иллюстрация проблемы «грязного» чтения
- Проблема неповторяемого чтения (англ. The Inconsistent Analysis Problem) — если одна транзакция несколько раз считывает одни и те же данные, а вторая — вносит изменения в эти данные между циклами чтения данных первой транзакции, то при повторном считывании первая транзакция может получить другой набор данных.
Пример. Допустим, в нашем примере, Транзакция №1 два раза подряд обращается к элементу справочника 1С:Предприятие 8.0. Управление торговлей и каждый раз считывает значение реквизита ЦенаПродажи . Если в промежуток между первым и вторым чтением вклинится Транзакция №2 и изменит значение этого реквизита, то в результате получится, что первая транзакция работает с данными, которые с ее точки зрения самопроизвольно изменяются. Графическое представление данной проблемы показано на рис. 13.
Выводы. Нельзя повторно читать измененные и записанные данные, если эти же самые данные уже были прочитаны до внесения в них изменений;
Рис. 13. Иллюстрация проблемы неповторяемого чтения
- Проблема чтения фантомов (англ. The Phantom Read Problem) — если первая транзакция считывает данные и потом на их основе осуществляет определенные действия, а вторая транзакция в этот момент добавляет в эти данные новую информацию, то как и в предыдущем случае это может привести к некорректному результату.
- получения общей суммы продаж по отделу в целом путем суммирования отдельных продаж по каждому из сотрудников;
- определения на основании полученных данных процента премии;
- расчета суммы премии для каждого из сотрудников отдела.
Рис. 14. Иллюстрация проблемы фантомов
Строго говоря, список вышеперечисленных проблем не является окончательным.
Уровни изоляции транзакций
Итак, ради увеличения производительности системы мы должны допустить параллельное выполнение транзакций. При этом мы так же должны обеспечить необходимую нам степень целостности данных (то есть, ограничить параллельность транзакций при работе с одними ресурсами). Строгость этих ограничений может быть различной, в зависимости от решаемой задачи. Поэтому нам необходим механизм гибкой настройки этих ограничений. В современных СУБД такая возможность реализуется путем применения уровней изоляции транзакций . Например, MS SQL Server 2000 позволяет использовать следующие уровни изоляции транзакции:
- READ UNCOMMITED — незавершенное чтение. Низший уровень изоляции, обеспечивает максимальную параллельность выполнения транзакций. Данный уровень защищает изменяемые мной данные от изменений, которые могут внести конкурирующие транзакции. Если другой транзакции необходимо изменить те же самые данные, то она должна ожидать завершения изменения данных моей транзакцией. Однако чтение данных разрешено. Таким образом этот уровень изоляции допускает чтение незавершенных изменений данных.
- READ COMMITED — обеспечивает запрет «грязного» чтения. Если моя транзакция начала изменять данные, то конкурирующая транзакция не может не только измененить, но даже прочитать их до завершения моих изменений. После того, как мои изменения закончены, конкурирующие транзакции могут читать данные, не дожидаясь окончания моей транзакции в целом. Таким образом решается проблема неповторяемого чтения.
- REPEATABLE READ — обеспечивает повторяемость чтения данных. Если моя транзакция начинает читать данные, то другая транзакция не может их изменить до окончания моей транзакции.
- SERIALIZABLE — максимальная изоляция данных конкурирующих транзакций. Устанавливает все блокировки уровня REPEATABLE READ и добавляет к ним блокировку диапазонов возможных значений, т.е. блокирует не только уже существующие данные, но и запрещает возникновение новых данных, соответствующих условиям «моего» запроса. Эта дополнительная блокировка позволяет избавиться от проблемы фантомов.
В зависимости от используемого уровня изоляции, СУБД накладывает различные типы блокировок на различные объекты базы данных на различное время.
Режим автоматических блокировок
Режим автоматических блокировок в 1С:Предприятии 8.1 полностью аналогичен механизму транзакционных блокировок, использовавшемуся в версии 8.0. В этом режиме 1С:Предприятие целиком «полагается» на возможности, предоставляемые СУБД (рис. 15).
Рис. 15. Автоматические блокировки в транзакции 1С:Предприятия 8
Такой подход позволяет разработчику не задумываться о достаточно сложных вопросах блокирования нужных данных в транзакции. Однако СУБД не имеет информации о логической структуре данных 1С:Предприятия, и платформе приходится использовать достаточно высокие уровни изоляции транзакций СУБД для того, чтобы обеспечить целостность и непротиворечивость данных (табл. 3): Repeatable Read и Serializable для MS SQL Server, Serializable для IBM DB2 и блокировка таблиц целиком для PostgreSQL.
Таблица 3. Блокировки СУБД, используемые в режиме автоматических блокировок в транзакции
СУБД
Файловая база данных
MS SQL Server
IBM DB2
PostgreSQL
Repeatable Read или Serializable
Зачастую такой подход приводит к возникновению «плохих» (избыточных) блокировок и не позволяет достичь желаемой параллельности работы пользователей. В клиент-серверном варианте блокировка данных происходит на уровне записей, однако может быть заблокирована и вся таблица целиком (например, в результате выбора СУБД неоптимального плана выполнения запроса). Тип блокировок, устанавливаемых в том или ином случае, зависит от вида операции, используемого 1С:Предприятием уровня изоляции транзакций и определяется внутренними механизмами самой СУБД (например, MS SQL Server).
Режим управляемых блокировок
В 1С:Предприятии версии 8.1 реализован дополнительный режим работы, позволяющий использовать собственный менеджер транзакционных блокировок 1С:Предприятия, независимый от используемой СУБД (рис. 16).
Рис. 16. Управляемые блокировки в транзакции 1С:Предприятия 8.1
При работе в этом режиме система использует гораздо более низкий уровень изоляции транзакций для MS SQL Server и IBM DB2, и блокировку на уровне записей для PostgreSQL (см. таблицу 3). Это позволяет достичь более высокой параллельности работы пользователей.
Таблица 3. Блокировки СУБД, используемые в режиме управляемых блокировок в транзакции
СУБД
Файловая база данных
MS SQL Server
IBM DB2
PostgreSQL
Однако этот уровень изоляции транзакций СУБД уже не может сам по себе обеспечить целостность и непротиворечивость данных во всех случаях. Поэтому 1С:Предприятие 8.1 при модификации данных методами встроенного языка (например, метод Записать() у объектных данных) устанавливает собственные управляемые блокировки в транзакции, которые обрабатываются собственным менеджером транзакционных блокировок. Эти блокировки также могут быть установлены и разработчиком самостоятельно в тех местах кода, где требуется обеспечить неизменность считываемых в транзакции данных (разделяемая блокировка) или запретить чтение данных другими транзакциями (исключительная блокировка).
Управляемые блокировки 1С:Предприятия учитывают логическую структуру прикладного решения поэтому позволяют максимально точно блокировать необходимые области данных (в отличие от использовавшихся ранее блокировок СУБД, которым не известна логическая структура системы). Таким образом менеджер управляемых блокировок позовляет максимально избежать возникновения «плохих» (избыточных) блокировок, блокируюя только действительно необходимые области данных.
В результате любой запрос к данным прежде всего обрабатывается собственным менеджером транзакционных блокировок 1С:Предприятия 8.1 (см. рис. 16). Сюда войдут как управляемые блокировки, автоматически установленные платформой при вызове методов, изменяющих данные, так и блокировки, прописанные программистом в явном виде в коде конфигурации.
Если на уровне 1С:Предприятия 8.1 конфликт управляемых блокировок не обнаруживается, то запрос передается далее, на исполнение СУБД. СУБД также использует собственный механизм блокировок для определения конфликтующих транзакций, но уже с более низким уровнем изоляции транзакций, чем в режиме автоматических блокировок.Установка режима управления блокировками для объектов конфигурации
В структуре объектов конфигурации существует несколько возможностей для задания режима управления блокировками.
Прежде всего существует свойство Режим управления блокировкой данных самой конфигурации (рис. 17).Рис. 17. Список значений свойства «Режим управления блокировкой данных» в палитре свойств Конфигурации
При выборе значений Автоматический или Управляемый режим блокировок при чтении или записи данных любого объекта конфигурации будет определяться именно этим выбранным значением.
Например, если установлен режим Автоматический , то при записи, скажем, любого элемента справочника, будут использоваться автоматические блокировки, устанавливаемые СУБД. Собственнный менеджер блокировок задействован не будет. Поведение системы будет полностью аналогичным поведению версии 8.0.
Если же установлен режим Управляемый , то, независимо от того, какие режимы управления блокировками установлены для конкретных объектов конфигурации (об этом смотри далее), при записи, скажем, документа система всегда будет самостоятельно устанавливать необходимые управляемые блокировки, которые будут обрабатываться собственным менеджером транзакционных блокировок. Этот режим предназначен для работы всей конфигурации только с управляемыми блокировками в транзакции.
Если же для свойства конфигурации выбран режим Автоматический и управляемый , то для конкретного объекта конфигурации режим блокировки будет определяться значением свойства Режим управления блокировкой данных самого объекта конфигурации (рис. 18).
Рис. 18. Список значений свойства «Режим управления блокировкой данных» в палитре свойств объекта конфигурации
Этот режим предназначен для постепенного или частичного перевода конфигурации в режим управляемых блокировок. Он позволяет отдельным объектам метаданных работать с управляемыми блокировками (например, наиболее «проблемным» документам и регистрам), в то время как остальные объекты работают в режиме автоматических блокировок.
Важной особенностью работы в режиме Автоматический и управляемый является то, что не во всех ситуациях работа с данными объекта будет выполняться именно в том режиме, который для него указан. Рассмотрим эту особенность подробно.
Внутри одной транзакции, которая начата и не завершена 1С:Предприятием, может быть начата еще одна (или несколько) транзакций. Такая логика работы обеспечивается платформой автоматически, а также поддерживается средствами встроенного языка.
В 1С:Предприятии 8.1 при начале каждой транзакции явно (если она начата из встроенного языка) или неявно (если она начата в результате действий самой системы) указывается режим управления блокировками в данной транзакции (автоматический или управляемый). Таким образом может оказаться, что первая (объемлющая) транзакция открыта в одном режиме, а вторая — в другом режиме управления блокировками. Всего может быть четыре различных сочетания, которые представлены в таблице 3.
Таблица 3. Сочетания режимов управления блокировками в транзакции
Режим существующей транзакции
Режим начинаемой транзакции
Результат
Начинаемая транзакция будет выполнена в автоматическом режиме
Начинаемая транзакция будет выполнена в управляемом режиме
Начинаемая транзакция будет выполнена в автоматическом режиме
Будет вызвана исключительная ситуация
Указанная ранее особенность проявляется в последних двух строках таблицы. Если существующая транзакция начата в автоматическом режиме, то начинаемая транзакция таже будет выполнена в автоматическом режиме, даже в том случае, если явно (во втроенном языке) или неявно (в свойствах объекта конфигурации) для нее установлен управляемый режим блокировок.
Если же существующая транзакция начата в управляемом режиме, то начинаемая транзакция может быть выполнена только в том случае, если для нее также указан управляемый режим. Если для нее указан автоматический режим — будет вызвана исключительная ситуация.
Разберем эту особенность на двух примерах.
Например, запись элемента справочника выполняется из встроенного языка внутри транзакции, открытой разработчиком. В этом случае «первой» (явной) транзакцией будет транзакция, инициированная разработчиком, а «второй» (неявной) будет транзакция, открываемая платформой при выполнении метода Записать() объекта справочника.
Явная транзакция открывается разработчиком с помощью метода встроенного языка НачатьТранзакцию() . В отличие от версии 8.0 этот метод имеет параметр БлокировкаДанных , который указывает какой режим управления блокировками будет использоваться в данной транзакции. По умолчанию значение этого параметра равно Автоматический . Поэтому, если разработчик использует значение этого параметра по умолчанию, то независимо от того, какой режим установлен в свойствах записываемого справочника, его запись будет выполнена в автоматическом режиме (см. табл. 3, 1 и 3 строки).
Если же разработчик открывает транзакцию в управляемом режиме, то он должен быть уверен в том, что для записываемого в этой транзакции справочника, в свойствах метаданных указан управляемый режим блокировок в транзакции. В противном случае при записи элемента справочника будет вызвана исключительная ситуация (см. табл. 3, 2 и 4 строки).
Рассмотрим другой пример — интерактивное проведение документа, который выполняет движения по регистру накопления. В этом случае «первой» (неявной) транзакцией будет транзакция, открываемая системой при записи документа, а «второй» (также неявной) будет транзакция, открываемая системой при записи набора записей регистра накопления.
Далее все аналогично предыдущему примеру. Если для документа в метаданных установлен автоматический режим управления блокировками, то независимо от того, какой режим установлен в метаданных для регистра накопления, запись его набора записей всегда будет выполняться в автоматическом режиме.
Если же для документа установлен управляемый режим блокировок в транзакции, то для регистра накопления таже должен быть установлен управляемый режим, иначе при проведении документа будет вызвана исключительная ситуация.
Из этих примеров можно сделать следующий общий вывод. Если, например, стоит задача повысить параллельность работы при проведении отдельного документа, не переводя при этом всю конфигурацию в управляемый режим, то последовательность действий должна быть следующей:
- свойство конфигурации Режим управления блокировкой данных необходимо установить в значение Автоматический и управляемый ;
- свойство Режим управления блокировкой данных объекта метаданных документ необходимо установить в значение Управляемый ;
- у всех регистров, по которым данный документ выполняет движения, следует установить свойство Режим управления блокировкой данных в значение Управляемый ;
- проанализировать процедуру проведения документа на предмет наличия:
- явных вызовов транзакций
- неявных вызовов транзакций, которые выполняются системой при модификации данных каких-либо объектов конфигурации
- для явных вызовов — параметр метода НачатьТранзакцию() ;
- для неявных вызовов — свойство Режим управления блокировкой данных модифицируемого объекта конфигурации;
Установка управляемых блокировок
Средствами встроенного языка установка управляемых блокировок внутри явной или скрытой (неявной) транзакции происходит с помощью специального объекта БлокировкаДанных , описание доступных свойств и методов которого можно посмотреть в синтакс-помощнике в ветви Общие объекты (рис. 19).
а
Рис. 19. Набор свойств и методов объекта «БлокировкаДанных» доступных в Синтакс-помощнике
Новый экземпляр данного объекта может быть создан с помощью одноименного конструктора и представляет собой коллекцию элементов блокировки данных. Изначально эта коллекция пуста и задача разработчика состоит в добавлении в эту коллекцию некоторого количества элементов блокировки.
При добавлении нового элемента блокировки для него необходимо указать пространство блокировок, которое будет блокировать данный элемент. Пространства блокировок определены в платформе 1С:Предприятия 8.1 и соответствуют структуре прикладных объектов конфигурации. Допустимы следующие имена пространств блокировок и имена полей пространств блокировок (табл. 9):
Как видно из таблицы, для объектных данных (справочник, документ и др.) определено единственное пространство блокировки — сам объект данных. Для необъектных данных (например, регистры) определено по два пространства блокировок, которые имеют разный логический смысл.
Пространство блокировок с суффиксом НаборЗаписей используется в тех случаях, когда необходимо заблокировать сами записи данного объекта (например, при добавлении новых записей).
Пространство блокировок без суффикса используется тогда, анализируются некоторые данные этого объекта (например, остатки регистра), или когда выполняются какие-либо операции, приводящие к изменению существующих данных объекта (например, восстановление границы последовательности).
После того, как элемент блокировки, соответствующий некоторому пространству блокировок, добавлен, следует установить для этого элемента режим блокировки (разделяемая или исключительная) и определить значения полей блокировки, чтобы указать, какие же именно «записи» будут заблокированы (для каждого пространства блокировок в платформе определены имена полей, значения которых могут задаваться при установке тех или иных блокировок).ВНИМАНИЕ
Следует понимать, что, в данном случае речь не идет о реальных записях базы данных. Несмотря на то, что управляемые блокировки описываются в терминах объектов метаданных и их полей, эти блокировки никак не связаны с реальной структурой хранения данных 1С:Предприятия в СУБД. Это всего лишь записи о том, что заблокировано «нечто».
Иногда можно провести аналогию между управляемыми блокировками и реальными записями СУБД. Например, для объектных данных блокировка объекта с указанной ссылкой будет «соответствовать» блокировке всех записей, содержащих указанную ссылку, во всех таблицах этого объекта метаданных (в основной таблице и в таблицах его табличных частей).
Однако в других случаях провести такую аналогию достаточно затруднительно, да и не нужно. Например, блокировка регистра бухгалтерии с указанием значения вида субконто. Достаточно понимать, что накладывая такую блокировку мы запрещаем другим транзакциям каким-либо образом изменять «записи» регистра бухгалтерии, у которых значение вида субконто равно указанному нами. Как при этом данное условие «проецируется» на реальную структуру данных регистра бухгалтерии — для нас совершенно не важно.
При установке новых блокировок менеджер анализирует имеющиеся блокировки. Если оказывается, что «нечто», что мы пытаемся заблокировать, уже заблокировано ранее, сравниваются режимы существующей и новой блокировок. Если режимы совместимы — новая блокировка устанавливается. Если режимы не совместимы — новая блокировка ожидает снятия существующей блокировки.
Условия необходимо ставить именно на те поля, имена которых приведены в списке имен пространств блокировок. Для каждого пространства блокировок количество устанавливаемых условий не ограничено. Условия могут быть заданы или на равенство значения поля какому-либо значению, или на вхождение значения поля в указанный диапазон.
Существует два способа задания условий на поля пространств блокировки:
- с помощью явного задания имени поля и его значения;
- с помощью указания источника данных, содержащего необходимые значения
При явном задании имени поля и его значения необходимо использовать метод УстановитьЗначение() объекта ЭлементБлокировкиДанных . В этом случае имя и значение указывают в качестве параметров метода, например так, как показано в листинге 1:
Листинг 1. Пример установки условия блокировки записей с помощью явного указания имени поля и его значения
// Создать объект блокировка данных БлокировкаДанных = Новый БлокировкаДанных; // Добавить новый элемент блокировки, блокирующий «нечто» в данных регистра накопления Остатки номенклатуры ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрНакопления.ОстаткиНоменклатуры"); // Установить режим блокировки - исключительный. Другие транзакции, устанавливающие управляемые // блокировки, не смогут даже начать чтение этих данных ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; // Указать, что именно мы блокируем в данных регистра Остатки номенклатуры - все «записи», у которых // значение измерения Склад равно значению, содержащемуся в переменной Склад ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
Для значений типа Дата или Число в качестве значения может быть задан некоторый диапазон значений. Диапазон значений передается методу с помощью объекта встроенного языка — Диапазон . Данный объект позволяет задать верхнюю и нижнюю границы диапазона, причем в диапазон включаются и границы диапазона (листинг 2).
Листинг 2. Пример установки условия блокировки записей с помощью задания диапазона
// Создать объект блокировка данных БлокировкаДанных = Новый БлокировкаДанных; // Добавить новый элемент блокировки, блокирующий «нечто» в данных регистра накопления Продажи ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрНакопления.Продажи"); // Установить режим блокировки - разделяемый. Эти данные гарантировано не будут изменены другими // транзакциями до окончания существующей транзакции ЭлементБлокировки.Режим = РежимБлокировкиДанных. Разделяемый; // Указать, что именно мы блокируем в данных регистра Продажи - все «записи», у которых // значение измерения Контрагент равно значению, содержащемуся в переменной Контрагент ЭлементБлокировки.УстановитьЗначение("Контрагент", Контрагент); // Создать объект Диапазон, описывающих интервал от начала месяца, к которому принадлежит указанная дата, // до указанной даты Диапазон = Новый Диапазон(НачалоМесяца(Дата), Дата); // Указать, что именно мы блокируем в данных регистра Продажи - все «записи», у которых // значение измерения Контрагент равно значению, содержащемуся в переменной Контрагент, // и значение поля Период содержится в указанном диапазоне ЭлементБлокировки.УстановитьЗначение("Период", Диапазон);
При указании источника данных сначала необходимо задать свойство ИсточникДанных объекта ЭлементБлокировкиДанных , после чего, используя метод ИспользоватьИзИсточникаДанных (), настроить соответствие полей области блокировки данных полям источника данных (листинг 3).
Листинг 3. Пример установки условия блокировки записей с помощью источника данных
Копировать в буфер обмена
// Создать объект блокировка данных БлокировкаДанных = Новый БлокировкаДанных; // Добавить новый элемент блокировки, блокирующий «нечто» в данных регистра накопления Остатки номенклатуры ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрНакопления.ОстаткиНоменклатуры"); // Установить режим блокировки - исключительный. Другие транзакции, устанавливающие управляемые // блокировки, не смогут даже начать чтение этих данных ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; // Указать, что именно мы блокируем в данных регистра Остатки номенклатуры - все «записи», у которых // значение измерения Склад равно значению, содержащемуся в переменной Склад ЭлементБлокировки.УстановитьЗначение("Склад", Склад); // Указать источник данных, который содержит данные для установки ограничений на другие поля этого // элемента блокировки - в данном случае таблица значений СписокНоменклатуры ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры; // Указать, что именно мы блокируем в данных регистра Остатки номенклатуры - все «записи», у которых // значение измерения Склад равно значению, содержащемуся в переменной Склад, // и у которых значение измерения Номенклатура равно какому-либо значению, содержащемуся в колонке // Номенклатура указанного источника данных ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
В качестве источника данных можно указывать результат запроса, табличную часть, набор записей или таблицу значений. При установке соответствия полей, именами полей источника будут являться имена колонок результата запроса, имена реквизитов табличной части, имена измерений или имена колонок таблицы значений соответственно. Заметим, что объект Диапазон также может являться значением поля источника данных.
Для установки всех созданных нами блокировок используется метод объекта БлокировкаДанных - Заблокировать() . На рисунке 20 показано действие данного метода в случае использования его внутри транзакции и вне ее.
Рис. 20. Схема вызова метода «Заблокировать()» объекта «БлокировкаДанных»
Как следует из рисунка, если этот метод выполняется внутри транзакции (явной или неявной), то блокировки устанавливаются в момент вызова метода. При окончании транзакции они будут сняты автоматически. Если же метод Заблокировать() выполняется вне транзакции, то блокировки установлены не будут.
Рекомендации по модификации конфигураций при переходе к режиму управляемых блокировок
При переводе созданных ранее прикладных решений с версии 1С:Предприятие 8.0 на версию 1С:Предприятие 8.1 они требуют определенной степени доработки с точки зрения перехода к работе в режиме управляемых блокировок. Дадим ряд рекомендаций, позволяющих определить последовательность действий разработчика в случае реализации вышеупомянутого перевода:
- Конвертируем конфигурацию из версии 8.0 в конфигурацию версии 8.1. Режим управляемых блокировок - автоматический.
- Если в процессе эксплуатации информационной базы возникают проблемы с параллельностью работы пользователей - например, часто стали появляться сообщения о превышении времени ожидания блокировки или о конфликтах взаимных блокировок, то составляем список документов, работа с которыми приводит к появлению вышеупомянутых проблем.
- Постепенно переводим конфигурацю в управляемый режим. Устанавливаем свойство Режим управления блокировкой данных всей конфигурации в целом в режим Автоматический и управляемый .
- Для указанных в списке видов документов переводим свойство Режим управления блокировкой данных в значение Управляемый . Также в управляемый режим переводим все регистры, по которым эти документы выполняют движения и все транзакции (явные и неявные), открываемые в процессе проведения документа.
- Анализируем тексты модулей каждого из указанных видов документов. Нас интересуют операции чтения данных. Причем не все, а только те, где выполняется чтение некоторых данных, на основании которых затем модифицируются эти же, или другие данные. Очевидно, что читаемые данные в этом случае не должны быть изменены до окончания транзакции проведения документа, а значит перед чтением их требуется заблокировать.
- Устанавливаем управляемые блокировки на найденные нами данные. При этом разделяемая блокировка устанавливается для того, чтобы данные не были изменены другими транзакциями. Исключительная блокировка, помимо этого, обеспечивает запрет не только изменения этих данных, но даже их чтения другими транзакциями, устанавливающими управляемые блокировки. Можно сказать, что исключительная управляемая блокировка является средством борьбы с конфликтами блокировок (deadlock) и может использоваться аналогично ключевому слову ДЛЯ ИЗМЕНЕНИЯ языка запросов в режиме автоматических блокировок.
ВНИМАНИЕ
В режиме управляемых блокировок, за счет использования другого уровня изоляции транзакций СУБД, конструкция ДЛЯ ИЗМЕНЕНИЯ языка запросов не работает. Таким образом, если в транзакции встречаются запросы, содержащие эту конструкцию, перед их выполнением необходимо устанавливать исключительную управляемую блокировку на читаемые данные. Это позволит в управляемом режиме обеспечить поведение, аналогичное поведению в автоматическом режиме.
Следует помнить, что чтение данных другими транзакциями будет невозможно только в том случае, если в других транзакциях устанавливаются несовместимые управляемые блокировки. Если управляемые блокировки в других транзакциях не устанавливаются, то чтение будет возможно. Это аналогично тому, как конструкция ДЛЯ ИЗМЕНЕНИЯ препятствует чтению данных не любыми запросами, а только теми, которые тоже используют конструкцию ДЛЯ ИЗМЕНЕНИЯ .
- При установке управляемой блокировки необходимо стремиться, чтобы блокировка была установлена только на те записи, которые будут обработаны системой в результате отработки программного кода. Другими словами, при установке управляемой блокировки желательно ставить условия на те же самые поля и по тем же самым значениям, которые были указаны, например, в тексте запроса к данным информационной базы. Например, см. текст листинга 4.
В качестве примера, приведем фрагмент программного кода из текста обработчика ОбработкаПроведения() , расположенного в модуле документа РасходнаяНакладная (листинг 4). При выполнении указанного программного кода система, используя механизм запросов, сначала читает информацию из регистра накопления ОстаткиНоменклатуры , а потом записывает в тот же самый регистр вновь сформированные данные. Согласно нашим рекомендациям, мы должны установить на записи регистра исключительную блокировку, запрещающую другим транзакциям, в которых устанавливаюся управляемые блокировки, не только запись, но и чтение, изменяемых при проведении накладной записей. В данном случае исключительная блокировка нужна для предотвращения возможного конфликта блокировок (deadlock).
Разбирая программный код, обратите внимание на соответствие условий при блокировке данных и в тексте запроса.
Листинг 4. Пример установки исключительной блокировки при проведении документа «РасходнаяНакладная»
Копировать в буфер обмена
БлокировкаДанных = Новый БлокировкаДанных; // Выбрать пространство блокировок РегистрНакопления.ОстаткиНоменклатуры, т.к. мы собираемся анализировать // остатки регистра ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрНакопления.ОстаткиНоменклатуры"); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; // Заблокировать «записи» с указанным значением измерения склад, т.к. в запросе есть следующее условие: // . И Склад = &Склад) . ЭлементБлокировки.УстановитьЗначение("Склад", Склад); // Заблокировать «записи» со значениями номенклатуры из табличной части, соответствующими условию // . РасходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга = ЛОЖЬ . // эти данные необходимо получить запросом к табличной части ЗапросИсточник = Новый Запрос; ЗапросИсточник.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ | РасходнаяНакладнаяСписокНоменклатуры.Номенклатура КАК Номенклатура |ИЗ | Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры |ГДЕ | РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка | И РасходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга = ЛОЖЬ"; ЗапросИсточник.УстановитьПараметр("Ссылка", Ссылка); ИсточникНоменклатуры = ЗапросИсточник.Выполнить(); ЭлементБлокировки.ИсточникДанных = ИсточникНоменклатуры; ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура"); БлокировкаДанных.Заблокировать(); Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ . . . . . . . . . . | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(&МомВремени, | Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ | РасходнаяНакладнаяСписокНоменклатуры.Номенклатура | ИЗ | Документ.РасходнаяНакладная.СписокНоменклатуры КАК | РасходнаяНакладнаяСписокНоменклатуры | ГДЕ | РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка | И РасходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга = ЛОЖЬ) | И Склад = &Склад) КАК ОстаткиНоменклатурыОстатки . . . . . . . . . . |"; . . . . . . . . . . Пока Выборка.Следующий() Цикл . . . . . . . . . . // регистр ОстаткиНоменклатуры Расход Движение = Движения.ОстаткиНоменклатуры.Добавить(); Движение.ВидДвижения = ВидДвиженияНакопления.Расход; Движение.Период = Дата; Движение.Номенклатура = Выборка.Номенклатура; Движение.Склад = Склад; Движение.Количество = Выборка.Количество; СуммаСписания = ?(Выборка.КоличествоОстаток = Выборка.Количество, Выборка.СуммаОстаток, Выборка.СуммаОстаток / Выборка.КоличествоОстаток * Выборка.Количество); Движение.Сумма = СуммаСписания; КонецЦикла; Движения.ОстаткиНоменклатуры.Записать();
Обратите внимание, что при создании источника данных для установки блокировок по номенклатуре мы использовали результат запроса к табличной части. Казалось бы, в качестве источника данных можно было просто использовать табличную часть ( ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры ), но при этом мы бы заблокировали лишние данные, ведь нас интересует только та номенклатура из табличной части, которая не является услугой ( РасходнаяНакладнаяСписокНоменклатуры.Номенклатура.Услуга = ЛОЖЬ) . Этот момент как раз хорошо иллюстрирует тот факт, что к установке управляемых блокировок нужно относиться внимательно и не устанавливать «плохих» (избыточных) блокировок.
Теперь расмотрим подробнее вопрос необходимости установки именно исключительной управляемой блокировки.
Если бы мы не использовали никакой управляемой блокировки, то наш запрос, читающий данные, начал бы выполняться в любом случае. После его окончания (еще до окончания транзакции!) СУБД сняла бы блокировку с прочитанных данных. Это значит, что другая транзакция тут же могла бы эти данные изменить (таковы особенности блокировок на уровне изоляции Read Committed). Поэтому управляемая блокировка необходима для того, чтобы гарантировать, что прочитанные данные не будут изменены до окончания нашей транзакции.
Каким образом управляемая блокировка препятствует изменению данных? В результате выполнения метода Заблокировать() в менеджере транзакционных блокировок появляются записи о тех данных, которые мы блокируем (естественно, если они не конфликтуют с существующими блокировками) (рис. 21).
Рис. 21. Установка управляемых блокировок
При любой попытке модификации данных информационной базы система автоматически будет пытаться установить исключительные управляемые блокировки для модифицируемых данных. Таким образом, если окажется, что другая транзакция пытается модифицировать те данные, которые мы уже заблокировали, менеджер транзакционных блокировок не даст ей установить нужные блокировки, т.к. исключительная блокировка, которую пытается установить другая транзакция, не совместима с нашей разделяемой блокировкой (рис. 22).
Рис. 22. Невозможно установить исключительную блокировку на номенклатуру 2
Значит система не сможет изменить данные, заблокированные нами до тех пор, пока наша транзакция не закончится и наша блокировка не будет снята автоматически при завершении транзакции (рис. 23).
Рис. 23. Исполнение кода продолжается после завершения нашей транзакции
Если бы мы использовали только разделяемую управляемую блокировку, то мы могли бы получить конфликт блокировок (deadlock) с другой транзакцией.
Например, наша и другая транзакция начали читать остатки из регистра Остатки номенклатуры . Списки номенклатуры, для которых читаются остатки в одной и другой транзакции, имеют одинаковые элементы. При этом система в нашей и в другой транзакции устанавливает разделяемую блокировку на читаемые данные. Две разделяемые блокировки на один и тот же ресурс («пересекающиеся» элементы номенклатуры) совместимы друг с другом (рис. 24).
Рис. 24. Разделяемые блокировки совместимы друг с другом
Затем мы закончили читать данные и хотим записать в этот регистр «новые остатки» для прочитанной номенклатуры. Мы не можем это сделать, т.к. для этого система должна установить исключительную блокировку на записываемые данные, но этому мешает разделяемая блокировка, установленная на часть этих данных другой транзакцией. Наша транзакция становится в очередь, ожидая снятия разделяемой блокировки, установленной другой транзакцией (рис. 25).
Рис. 25. Наша транзакция не может установить исключительную блокировку на номенклатуру 2
В это время другая транзакция также закончила чтение данных и хочет записать в этот регистр «новые остатки» для прочитанной номенклатуры. Она не может это сделать, т.к. для этого система должна установить исключительную блокировку на записываемые данные, но этому мешает разделяемая блокировка, установленная на часть этих данных нашей транзакцией. Другая транзакция становится в очередь, ожидая снятия разделяемой блокировки, установленной нашей транзакцией транзакцией (рис. 26).
Рис. 26. Конфликт блокировок: другая транзакция также не может установить блокировку на номенклатуру 2
В результате получается конфликт блокировок (deadlock), который может быть разрешен только принудительной отменой одной из транзакций.
Поэтому в данном случае необходима исключительная блокировка (рис. 27).
Рис. 27. Установка исключительных блокировок
В этом случае, когда другая транзакция, использующая управляемые блокировки, попытается начать чтение данных, которые мы заблокировали, она не сможет этого сделать. Менеджер тразакционных блокировок не даст ей установить никакую управляемую блокировку (ни разделяемую, ни исключительную) на наши данные, т.к. они не совместимы с нашей исключительной блокировкой (рис. 28).
Рис. 28. Другая транзакция не может установить блокировку на номенклатуру 2
В результате исполнение кода в другой транзакции будет остановлено и система будет ожидать снятия нашей исключительной блокировки. А наша транзакция сможет завершится, т.к. другая транзакция не имеет возможности заблокировать те записи, которые понадобятся нам для модификации (рис. 29).
Рис. 29. Наша транзакция успешно завершается