Начнем так: свойство БлокироватьДляИзменения – ничего не блокирует.
Хотя казалось бы – ровно для этого и существует :)
Еще один пример того, что можно искренне считать, что понимаешь как что-то работает – и не догадываться о заблуждении :))
Как работает свойство БлокироватьДляИзменения набора записей регистра накопления и бухгалтерии, известно далеко не всем специалистам по 1С.
С одной стороны, вроде бы многие знают, когда это свойство нужно использовать (если не знаете – предварительно прочитайте
статью про контроль остатков).
С другой стороны, КАК это работает, понимают немногие. Показательно будет привести цитату из справки по БлокироватьДляИзменения:
В результате это приводит к неправильному использованию данного инструмента для блокировок. В этой статье докопаемся до истины и покажем внутренние детали реализации этого механизма платформы «1С:Предприятие 8.3».
отдельная статья на нашем сайте.
Использование платформы “1С:Предприятие 8.2” и MS SQL Server
Допустим, что 2 пользователя одновременно проводят 2 документа продажи товаров (пусть это будут столы). При этом в конфигурации используется новая методика контроля остатков (сначала запись движений, потом чтение остатков).
Если у регистра накопления с остатками товаров включено разделение итогов, можем получить следующую ситуацию:
В представленной ситуации 2 пользователя списывают товар «Стол» со склада «Основной», при этом на складе осталось всего 10 столов.
Так как включено разделение итогов, то оба пользователя параллельно делают движения в таблице итогов, накладывая при этом исключительную управляемую блокировку и X-блокировку СУБД на свои строки. Блокировка будет наложена по полям Склад + Товар + Разделитель, и так как разделитель (splitter) разный, то обе блокировки установятся успешно.
Далее каждый из пользователей пытается выполнить контроль остатков с помощью запроса, и здесь начинается самое интересное.
Запрос к регистру пытается прочитать все строки, где склад равен «Основной» и товар равен «Стол», без условия по разделителю. При чтении запрос накладывает S-блокировку СУБД.
Иванов может прочитать свою строку, но не может прочитать строку, которую занял пользователь Петров (так как S и X-блокировки не совместимы). В итоге Иванов ждет Петрова. Петров, в свою очередь, может прочитать свою строку, но не может прочитать строку, занятую Ивановым, и тоже встает в очередь.
В результате мы получаем взаимную блокировку: Иванов ждет Петрова, а Петров ждет Иванова. Как решить эту проблему, рассмотрим чуть позже. А сейчас перейдем к следующему сценарию.
Использование платформы “1С:Предприятие 8.3” или использование в качестве СУБД версионника
Кратко отметим, что особенность СУБД версионника в том, что при чтении он не блокирует данные. При использовании 8.3 без режима совместимости с 8.2, MS SQL Server тоже может работать как версионник.
В данных условиях оба пользователя смогут прочитать остаток, так как чтение будет не блокирующим и в итоге мы получим минус на складе.
Как исправить проблемы?
И ситуация взаимоблокировки, и, тем более, отрицательные остатки на складе не могут считаться корректной работой программы.
Чтобы не допустить этих проблем, как раз и было придумано свойство набора записей регистров накопления и бухгалтерии – БлокироватьДляИзменения.
Если до записи движений установить данное свойство в значение «Истина», то блокировка будет накладываться без учета разделителя. То есть поведение будет таким, как будто разделитель итогов у регистра выключен.
Следует отметить что блокировка будет установлена в любом случае, разница лишь в том, что если БлокироватьДляИзменения имеет значение «Истина», то блокировка будет без учета разделителя, иначе – с учетом разделителя. По умолчанию значение свойства БлокироватьДляИзменения равно «Ложь».
Допустим программист внес исправления в код:
Движения.ТоварыНаСкладах.БлокироватьДляИзменения=Истина; Движения.Записать();
При этом строку «БлокироватьДляИзменения=Истина» можно писать в любом месте кода, но обязательно перед записью движений. Данная строка просто говорит платформе: «когда будешь записывать движения, не учитывай разделитель итогов».
Следует понимать, что сам разделитель итогов при этом никуда не исчезает, просто блокировка будет на поля Склад + Товар без учета разделителя.
Рассмотрим поведение системы, если использовать данное свойство.
При наложении блокировки без учета разделителя оба пользователя попытаются обратиться к одной строке. В результате Петров встанет в очередь (исключительные управляемые блокировки несовместимы) и будет ожидать, пока строка не освободится.
При этом ожидание будет происходить на управляемых блокировках, то есть на сервере 1С. Сервер СУБД даже не будет «знать» о том, что транзакция Петрова ожидает своей очереди.
Данное ожидание на блокировке будет являться необходимым – оно обеспечивает корректную бизнес-логику приложения.
Таким образом мы предотвратили возможную взаимную блокировку или минус на складе с помощью свойство БлокироватьДляИзменения.
Несколько вопросов и заблуждений по теме
Часто нам задают вопросы по использованию свойства БлокироватьДляИзменения. Рассмотрим наиболее популярные из них.
Вопрос 1
Почему БлокироватьДляИзменения не устанавливают в истину в документе «Приходная накладная»? Ведь если я перепровожу документ ПриходнаяНакладная, то должен блокировать поля, по которым был приход, иначе можно товар списать в минус. Пример: было – Стул 2 шт, перепровожу, меняя Стул на Тумбу, и в этот момент списываю Стул в минус.
Ответ
БлокироватьДляИзменения ставят там, где у регистра включено разделение итогов и идет контроль остатков после записи. В приходе же при первом проведении контроль остатков не выполняется. Если при перепроведении приходной накладной в конфигурации реализован контроль остатков, то имеет смысл использовать свойство БлокироватьДляИзменения.
Вопрос 2
Нужно ли использовать БлокироватьДляИзменения в файловом режиме?
Ответ
В файловом режиме блокировка всегда идет на всю таблицу, следовательно режим разделения итогов там бесполезен, поэтому и БлокироватьДляИзменения ставить не обязательно, но желательно. Следует помнить, что файловая база со временем может стать клиент-серверной, и тогда данное свойство необходимо будет использовать.
Вопрос 3
Нужно ли устанавливать БлокироватьДляИзменения при очистке движений?
Ответ
В большинстве случаев не нужно, так как обычно после очистки движений контроль остатков не выполняется. Если контроль сразу после очистки выполняется, тогда БлокироватьДляИзменения ставить нужно.
Вопрос 4
Нужно ли использовать свойство БлокироватьДляИзменения при автоматических блокировках?
Ответ
При попытке использовать БлокироватьДляИзменения в автоматическом режиме блокировок система выдаст сообщение об ошибке: «Ошибка записи! Блокировка для изменения запрещена для автоматического режима блокировки». Данное свойство можно использовать только для управляемого режима блокировок.
Резюмируя, можно дать следующую рекомендацию: если по регистру при любой записи всегда есть контроль остатков, тогда разделение итогов по нему лучше отключить, так как оно не имеет смысла – везде придется устанавливать свойство БлокироватьДляИзменения.
Если же по регистру контроль остатков нужен лишь в определенных документах, тогда в этих документах нужно поставить «БлокироватьДляИзменения=Истина», чтобы предотвратить описанные выше проблемы.
Выводы
Свойство набора записей БлокироватьДляИзменения само по себе ничего не блокирует, оно лишь определяет, будет ли в момент записи при блокировке учитываться разделитель или нет.
Следует использовать данное свойство только при выполнении всех 3 условий:
- Используется новая методика контроля остатков
- У регистра включено разделение итогов
- Используется управляемый режим блокировок.
Надеемся, теперь механика работы платформы будет ясна и белых пятен в знаниях специалистов 1С станет меньше :)
Более детально эта тема раскрыта в курсе:
Поддержка – до 4 месяцев. Объем курса – 35 учебных часов.
Об авторе
Автор статьи – Андрей Бурмистров
E-mail: andbkt@mail.ru
Skype: Andreynikus
VK: https://vk.com/andreynikus
Андрей Бурмистров – автор и тренер курса «Ускорение и оптимизация систем на 1С:Предприятие 8.3 и подготовка на 1С:Эксперт по технологическим вопросам»
PDF-версия статьи для участников группы ВКонтакте
Мы ведем группу ВКонтакте – http://vk.com/kursypo1c.
Если Вы еще не вступили в нее – сделайте это сейчас, и в блоке ниже (на этой странице) появятся ссылки на скачивание материалов.
Вы можете скачать эту статью в формате PDF по следующей ссылке: Ссылка доступна для зарегистрированных пользователей)
В тексте: “Следует отметить что блокировка будет наложена ВНЕ зависимости от значения данного свойства”, по смыслу, скорее: “… В зависимости от значения данного свойства”. Поправьте, пожалуйста, статья очень полезна и легка для понимания, но при прочтении такие радикальные ошибки ухудшают восприятие. Логика первого высказывания противоречит логике последующего.
Альберт, здравствуйте.
Никакой ошибки тут нет, блокировка действительно будет установлена не зависимо от того какое значение примет свойство БлокироватьДляИзменения, именно это и имелось ввиду.
Фразу “… блокировка будет наложена в зависимости от значения данного свойства”, можно понять не верно, например так что блокировка будет либо установлена либо нет, а это не так.
Немного переформулировал предложение, надеюсь теперь будет меньше недопониманий.
Андрей, спасибо огромное! Статья с новой формулировкой просто прекрасна )
Если используется старая методика проведения. И перед чтением данных мы очищаем старые движения документа. Нужно использовать свойство БлокироватьДляИзменения? В интернете встречал разные мнения на этот счет.
Главная задача моего курса, самостоятельно научиться находить ответы на такие вопросы. В интернете можно много чего написать, а вы возьмите и посмотрите на практике что происходит при удалением движений с включенным режимом разделения итогов. Для этого достаточно в тех. журнале включить событие TLOCK и посмотреть какие логи пишутся при удалении движений.
А происходит то, что если блокироватьДляИзменения не ставить, то движения смогут удалиться параллельно, но когда дело в модуле дойдет до контроля остатков, все равно возникнет ожидание т.к. так транзакция наложит явную управляемую блокировку перед выполнением запроса. Явная управляемая блокировка не учитывает разделитель поэтому возникнет ожидание.
А если БлокироватьДляИзменения поставить при удалении движений, тогда параллельно удалить движения не получиться, возникнет ожидание.
Ожидание и так и так возникнет, разница лишь в том в каком месте кода, при удалении движений или при контроле остатков.
С одной стороны, вроде бы многие знают, когда это свойство нужно использовать (если не знаете – предварительно прочитайте статью про контроль остатков(c).
Ссылка ведет на статью(“Новая” и “старая” методики контроля отрицательных остатков при проведении документов в системе 1С:Предприятие 8.3), где в примере нет кода использующего указанное в теме свойство, кажется логичным, давать ссылку на другую, более подробную статью(“Методика оперативного проведения и управляемые блокировки в 1С:Предприятие 8.3 (обновление 2017 года)”)
Данная статья исключительно про данное свойство, остальные статьи освещают близкие но все же другие темы.
Слышал что данное свойство так же устанавливает блокировку на те сочетания значений измерений которые были сформированы “предыдущими” движениями документа. То есть предположим у нас есть проведенный документ, и при перепроведении пользователь поменял состав табличной части. В результате БлокироватьДляИзменения “наложит блокировку” на те движения которые были записаны и те которые были ранее. Так ли это?
Повторю еще раз. Свойство набора записей БлокироватьДляИзменения ничего само по себе не блокирует, оно лишь дает указания учитывать или не учитывать разделение итогов при записи т.е. в момент блокировки. Сама блокировка случается непосредственно в момент записи движений.
При перепроведении момент блокировки старых записей определяется свойством документа “Удаление движений”. Если стоит “Удалять автоматически”, тогда удаление (и соответственно блокировка) происходит еще до начала обработки проведения. В иных случаях удаление (и блокировка) старых строк идут в момент записи новых движений. Вы можете самостоятельно это проверить проведя опыты и посмотрев какие блокировки накладываются с помощью соответствующей обработки.
“Сама блокировка случается непосредственно в момент записи движений.”
Так откуда она берётся тогда? От сырости? Если блокировка не зависит от нашего желания, то какая тут тогда она управляемая?
> Так откуда она берётся тогда?
Блокировка берется из метода Записать()
Если работа идет в управляемом режиме блокировок, то метод Записать() ставит и управляемую блокировку (неявную) и блокировку СУБД.
Вообще любая модификация данных в управляемом режиме блокировок, приводит к установке неявной управляемой блокировке и блокировке на уровне СУБД.
Добрый вечер, спасибо за статью.
Прошу вас уточнить, какая логика получения значения остатка при Запрос.Выполнить() в случае включенного разделителя и использования версионника (рис.2)?
Пользователи получат сразу две записи (с разделителем)? Или каждый получит свою разделенную запись? (Иванов получит 4, а Петров -5).
Мне непонятен принцип успешного прохождения контроля отрицательных остатков в этом случае.
Благодаря включенному разделению итогов, мы можем одновременно записать в регистр одинаковые значения по набору измерений, например одновременно списать один товар с одного склада. В момент записи строки накладывается X блокировка.
При использовании версионирования, если транзакция пытается прочитать строку заблокированную X блокировкой, то будет прочитана версия строки до наложения X блокировки.
В нашем случае Иванов прочитает остаток 4 (10-6), Петров прочитает остаток 5 (10-5). В результате обе транзакции успешно сделают движения т.к. включен разделитель, и обе пройдут контроль остатков, ведь каждая из транзакций думает что остатка хватает.
Если же разделитель (дополнительную колонку Splitter) не учитывать, поставив БлокироватьДляИзменения=Истина, то параллельной записи одинаковых значений по набору измерений уже не получится, что и демонстрирует рисунок 3. Ведь в этом случае получается что мы пишем данные одновременно в одну строку, а это можно делать только по очереди.
В результате мы приходим к тому, что при контроле остатков запись в регистр по совпадающим значениям набора измерений регистра, можно делать только последовательно.
Если остались вопросы, пишите.
Здравствуйте, все равно не очень понятен этот момент. Выше Вы пишите “Запрос к регистру пытается прочитать все строки, где склад равен «Основной» и товар равен «Стол», без условия по разделителю.” Следовательно, когда будет читать данные Иванов у него будет 2 строки: Стол с количеством 4 и Стол с количеством -5.
НО! “При использовании версионирования, если транзакция пытается прочитать строку заблокированную X блокировкой, то будет прочитана версия строки до наложения X блокировки.” Исходя из написанного, Иванов читая строку добавленную Петровым, вместо -5 прочитает ее как нулевую строку? Ведь до наложения Х блокировки, никакой строки не было, никакие данные не менялись. Так получается?
Ирина, здравствуйте! Да, вы все верно поняли.
Иванов прочитает 4 шт. в своей строке + 0 шт. (значение до изменения) в строке Петрова, итого 4 > 0 бинго!
Петров прочитает -5 шт. в своей строке + 10 шт. в строке Иванова, итого 5 > 0 и контроль остатков проходит.
Можно прям сделать регистр и все это пощупать руками с точкой останова после запроса, меняя режим совместимости базы, очень помогает понять такие вещи.
Спасибо, с этим понятно, есть еще такой момент: при использовании Платформы 1С:Предприятие 8.2 написано: «Иванов может прочитать свою строку, но не может прочитать строку, которую занял пользователь Петров (так как S и X-блокировки не совместимы).» Но ведь при выполнении чтения остатка, на эти данные будет наложена S блокировка (как для Иванова, так и Петрова), как тогда здесь появится несовместимость?
Даже если учитывать, что в ту секунду, когда читает данные Иванов, у Петрова еще стоит Х-блокировка, но в следующую секунду, когда Петров начнет читать данные у него также установится S-блокировка, для него- то уже не будет проблемы прочитать строку Иванова.
Видимо, я не совсем понимаю последовательность установок блокировок СУБД, поясните пожалуйста этот момент или может подскажите, где про это поподробнее почитать?
Если пользователь А поставил X блокировку, то он же сможет и прочитать свои собственные данные (грубо говоря поставить S там где была X) , но это не значит что он заменил X блокировку S блокировкой. Просто в пределах одной транзакции у пользователя полная совместимость блокировок.
Для пользователя Б блокировка X поставленная пользователем А по прежнему сохраняется, отсюда и конфликт.
Если не понятно, пишите, постараюсь описать подробнее.
На что, тогда влияет неявная исключительная блокировка (которая подразумеваем полную изолированность данных, как на чтение, так и на запись) , накладываемая самой системой, ведь по идее она длится до окончания транзакции, а в момент чтения транзакция она еще не зафиксирована(из справочной информации 1С:ИТС “Все обработчики событий, расположенные в модулях объектов….вызываются в транзакции”) я так понимаю, что пока процедура “ОбработкаПроведения” не завершится, то транзакция будет открыта? Или не так?
Не совсем понял в чем именно вопрос?
Да, любая исключительная блокировка будет держаться с момента установки до окончания транзакции.
В случае проведения документа, транзакция открывается до момента окончания процедуры проведения.
Имею ввиду(1С:Предприятие 8.3), что неявная исключительная блокировка накладывается в тот момент, когда мы записываем данные по 3-м полям “Склад + Товар + Разделитель” . Далее читаем все строки, через запрос, и данное чтение будет без учета разделителя итогов, то есть уже по 2-м полям “Склад+Товар”. Суть моего вопроса была в том, что неявная исключительная блокировка, не запретит чтение строки Иванова для второй транзакции(Петровым), потому что накладывалась она по 3-м полям, а читаем по 2-м полям? Или я чего-то не понимаю?
Блокировка накладывается полностью на всю строку таблицы т.е. на все поля (они же колонки или столбцы) одной записи.
Поэтому если строка заблокирована то не так важно какие именно столбцы (поля) подошли под это условие, важно что строка заблокирована полностью со всеми колонками.
В реляционных СУБД нельзя заблокировать ячейку т.е. пересечение столбца и строки, можно только всю строку целиком.
Поэтому условия Склад+Товар и Склад+Товар+Разделитель будут попадать на одну и ту же строку в таблице и конфликтовать, отсюда и ожидание на блокировке.
Я одного понять не могу(Предприятие 8.3), при записи данных у нас накладывается неявная исключительная блокировка, которая длится до конца транзакции, но когда после записи мы начинаем читать данные из регистра, Иванов можем прочитать строку Петрова, а Петров строку Иванова(с оговоркой, что будут прочитаны данные, которые были до Х-блокировки), так куда делась исключительная блокировка и на что она тогда влияет? Где ее полная изолированность данных?
Так работает версионирование, это уже не тема данной статьи, но я отвечу, раз пошла такая пьянка :)
Например в MS SQL при включенном версионировании, при изменении строки создается ее копия (версия), которая помещается в отдельное хранилище в TempDB.
Все дальнейшие модификации транзакция производит уже с этой копией, при этом оригинальная строка осталась свободной ее спокойно можно читать и накладывать S блокировки другим транзакциям.
На копию строки в TempDB и накладывается X блокировка.
Если же вторая транзакция тоже захочет изменить эту же строку (поставить X блокировку), то не сможет этого сделать т.к. система видит что ее копия уже есть в TempDB с установленной X блокировкой, следовательно вторая транзакция не сможет поставить свою X и будет ожидать первую.
Благодаря механизму версионирования пишущие не блокируют читающих, а читающие пишущих, при этом X блокировки как и раньше будут блокировать друг друга.
Спасибо Вам огромное, за такие полные и по существу ответы! За такой короткий промежуток времени, благодаря вашим ответам узнала много новой и полезной информации для себя. Никаких сомнений об необходимости прохождения курса по оптимизации вообще больше нет! Спасибо Вам за ваш труд.
Вам спасибо за интересные вопросы.
Спасибо! Полезно узнать :)
Спасибо за статью.
Слегка некорректное название свойства и отсутствие информации со стороны 1С…
Следовало бы назвать это свойство, например: “СнятьКонтрольРазделенияИтоговПриЗаписи”.
Возможно название связано с тем, что в автоматическом режиме для предотвращения взаимоблокировки, при старом способе контроля остатков, использовалась опция запроса ДЛЯ ИЗМЕНЕНИЯ. А свойство БлокироватьДляИзменения выполняет примерно ту же функцию. Это лишь предположение, правду мы вряд ли когда-нибудь узнаем :)
Спасибо!!
+Спасибо за статьи.
Добрый день. А возможно запись в независимый непереодический РС сразу двум пользователям, если ключевые поля разные? Надо для этого использовать блокировки?
Если значения измерений разные, тогда запись пройдет параллельно.
Блокировки не нужны значит, если стоит режим “Управляемых блокировок”. Платформа все сама сделает?
У нас в базе документоборота 1.4 частые блокировки на запись в регистр сведений.
Был использован способ через запись менеджерЗаписи, изменил на наборЗаписи.
Реквизиты там БизнесПроцесс и Завершен(булево).
Второй вопрос, что блокировки возникали при создание новых процессов. Почему тогда они могли возникать, когда БизнесПроцес новый и значит блокировки быть не должно,или при записи в непереодический РС надо как то самим управлять ими?
Спасибо за ответы.
Если контроль остатков идет после записи, тогда явные управляемые блокировки ставить не нужно, они и так будут установлены при записи. Если контроль до записи, тогда надо будет ставить явные управляемые блокировки. Рекомендую вам ознакомиться с этими статьями: http://курсы-по-1с.рф/news/2017-02-15-two-programming-articles-update/
По словесному описанию я ничего не могу сказать по поводу расследования блокировок. Ставьте сервис анализа блокировок или ЦУП, снимайте данные тогда будет что обсуждать.
При создании нового элемента, блокировок никаких нет, они появляются при записи этого объекта в базу данных. Опять же пока нет данных от инструментов, ничего нельзя сказать.
спасибо. Я имел ввиду конечно при записи, когда туда записывался новый Бизнеспроцесс.
А если база файловая? На ней же блокируется вся таблица, правильно я помню/понимаю?
Т.е. для файловой базы при записи в один регистр, даже если измерения разные, мешающие блокировки будут в любом случае ?
Надо переходить на SQL?
Все верно, это как раз вопрос №2 в статье.
Присоединюсь. Спасибо за полезный и понятный материал.
Спасибо!
Осталось только понять логику разработчиков УТ и ERP. Флаг “Разрешить разделение итогов” то стоит то не стоит … а в модуле набора записей БлокироватьДляИзменения выставляется в Истину…
Добрый день.
Тоже интересует этот вопрос.
Почему 1С ставит флаг “БлокироватьДляИзменения” в истину, хотя «Разрешить разделение итогов» отсутствует. (Розница 2.2 – РН “ТоварыНаСкладах”)
Если разделение итогов выключено, то БлокироватьДляИзменения не имеет смысла. Возможно данный код в типовой просто был откуда-то скопирован и данную строку просто забыли убрать. Типовые тоже люди пишут, а люди как известно иногда ошибаются.
Спасибо, интересно!
Подскажите, а почему на рис.1 у Петрова в зеленой строке количество минус 5?
Таблица отображает таблицу итогов регистра накопления. Было 10 шт. товара, Иванов списал 6 в строке стало 4. Параллельно Петров списывал 5 шт. и т.к. разделитель включен, вместо попытки обновления записи захваченной Ивановым, добавилась новая запись со значением -5.
Так почему не 5, а -5? Всё равно же от 10-ти отнималось.
Потому что идет списание, а не поступление, поэтому вычитаем.
Так ведь 6 тоже списываем, но там не пишем -4.
Верно не пишем, потому что одна строка в таблице итогов уже была и там было 10. Поэтому один сеанс просто обновляет эту строку, вычитаем 6 и вместо 10 получается 4.
Второй сеанс при включенном разделителе добавляет новую строку и пишет там -5.
Почему первый работает с существующей строкой, а второй нет? Как определяется кто “первый”?
P. S. Может второй тоже отнимает количество, как и первый, только у первого есть строка и предыдущей записью (итогом), а у второй нет (вернее итог = 0) и она отнимает 5 от 0?
Платформа работает по следующему алгоритму:
Если в таблице итогов уже есть нужная строка, то она меняется.
Если строки в таблице итогов нет, то она добавляется.
Та транзакция которая быстрее выполнила запрос на обновление и захватит уже существующую строку. Кто первый определяется кто первый по времени выполнил запрос на обновление, на какую-то долю секунды все равно кто-то будет первый.
Если разделение итогов не используется, то вторая транзакция встанет в очередь и будет ждать пока закончится первая.
Если разделение используется, тогда вторая транзакция обновит другую существующую строку итогов (если она есть), а если таковой нет, то создаст новую строку что и показано в этом примере.
Спасибо за статью. То чего действительно не хватало для полноты картины. Одним пробелом меньше в голове=)
Отлично, Дмитрий! :)