Ошибка блокировки узла обмена

Проблема блокировок при работе с планом обмена ☑ 0

mzelensky

06.04.21

12:46

Доброго времени суток!

Есть план обмен, работа с которым реализована следующим образом:

1) Получение данных из узла, присвоение выбранным данным «Номера сообщения» (для последующей очистки)

Функция ЗаполнитьСтруктуруИзмененийДляУзла_Справочники(УзелПланаОбмена, ИмяТаблицы)

    
    РезультатФункции = Новый Структура(«ДанныеРегистрации, НомерИсходящегоСообщения»);

    
    ОтобразитьСостояние(«Выборка изменений …»);

    
    Если Метаданные.Справочники.Найти(ИмяТаблицы) <> Неопределено Тогда

        Если ЗначениеЗаполнено(УзелПланаОбмена) Тогда

            Запрос = Новый Запрос;

            Запрос.Текст =»ВЫБРАТЬ ПЕРВЫЕ 3000

            |    Изменения.Ссылка КАК Объект

            |ИЗ

            |    Справочник.» + ИмяТаблицы + «.Изменения КАК Изменения

            |ГДЕ

            |    Изменения.Узел = &Узел»;

            Запрос.УстановитьПараметр(«Узел», УзелПланаОбмена);

            
            ДанныеРегистрации = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку(«Объект»);

            
            //

            Если ДанныеРегистрации.Количество() > 0 Тогда

                
                ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();

                Запись = Новый ЗаписьXML;

                Запись.УстановитьСтроку();

                ЗаписьСообщения.НачатьЗапись(Запись, УзелПланаОбмена);

                
                ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения, ДанныеРегистрации);

                
                РезультатФункции.Вставить(«ДанныеРегистрации», ДанныеРегистрации);

                РезультатФункции.Вставить(«НомерИсходящегоСообщения», ЗаписьСообщения.НомерСообщения);

                
                ЗаписьСообщения.ЗакончитьЗапись();

                Запись.Закрыть();

                
                ВыборкаИзменений = Неопределено;

                ЗаписьСообщения  = Неопределено;

                Запись              = Неопределено;

                
            КонецЕсли;

        
        КонецЕсли;

    КонецЕсли;

    
    Возврат РезультатФункции;

    
КонецФункции

2) Происходит выгрузка сведений

3) Удаление из плана обмена успешно выгруженных данных (и не поменявшихся за момень выгрузки)

Функция ОчиститьИзмененияДляУзла_Справочники(УзелПланаОбмена, ИмяТаблицы, НомерСообщения, ДанныеУдаляемыеИзРегистрации)

    
    РезультатФункции = Истина;

    
    ОтобразитьСостояние(«Очистка изменений …»);

    
    Если Метаданные.Справочники.Найти(ИмяТаблицы) <> Неопределено Тогда

        Если ЗначениеЗаполнено(УзелПланаОбмена) И ЗначениеЗаполнено(НомерСообщения) И ЗначениеЗаполнено(ДанныеУдаляемыеИзРегистрации) Тогда

            
            Запрос = Новый Запрос;

            Запрос.Текст =»ВЫБРАТЬ

            |    Изменения.Ссылка КАК Объект,

            |    Изменения.НомерСообщения КАК НомерСообщения

            |ИЗ

            |    Справочник.» + ИмяТаблицы + «.Изменения КАК Изменения

            |ГДЕ

            |    Изменения.Узел = &Узел

            |    И Изменения.НомерСообщения > 0

            |    И Изменения.НомерСообщения <= &НомерСообщения

            |    И Изменения.Ссылка В(&ДанныеУдаляемыеИзРегистрации)

            |»;

            Запрос.УстановитьПараметр(«Узел», УзелПланаОбмена);            

            Запрос.УстановитьПараметр(«НомерСообщения», НомерСообщения);

            Запрос.УстановитьПараметр(«ДанныеУдаляемыеИзРегистрации», ДанныеУдаляемыеИзРегистрации);

            
            Выборка = Запрос.Выполнить().Выбрать();

            Пока Выборка.Следующий() Цикл

                ПланыОбмена.УдалитьРегистрациюИзменений(УзелПланаОбмена, Выборка.Объект);    

            КонецЦикла;    

            
        Иначе

            РезультатФункции = Ложь;    

        КонецЕсли;          

    Иначе

        РезультатФункции = Ложь;    

    КонецЕсли;

    
    Возврат РезультатФункции;

    
КонецФункции

1

mzelensky

06.04.21

12:48

После запуска этого механизма стала возникать ошибка блокировки:

Ошибка при вызове метода контекста (ВыбратьИзменения): Конфликт блокировок при выполнении транзакции:

Microsoft SQL Server Native Client 11.0: Транзакция (идентификатор процесса 55) вызвала взаимоблокировку ресурсов блокировка с другим процессом и стала жертвой взаимоблокировки. Запустите транзакцию повторно.

Т.е. блокировку вызывает вот эта строчка:

ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения, ДанныеРегистрации);

Нужна она мне для того, чтобы выбираемым данным назначить номер сообщения, по которому потом будет происходить зачистка.

2

mzelensky

06.04.21

12:52

Поиски в интернете показали, что Да «ПланыОбмена.ВыбратьИзменения» вызывает блокировку.

Но почему она длительная и мешает и как можно адекватно обойти ее — не понятно

3

fisher

06.04.21

12:57

Еще бы она не вызывала блокировку. Она же пишущая.

А вот почему другой процесс в нее упирается и какой именно процесс — вопрос.

Релиз, платформа, режим блокировок?

4

mzelensky

06.04.21

13:05

(3) «Релиз, платформа, режим блокировок» — платформа 8.3.15, режим блокировок «Автоматический»

«какой именно процесс в нее упирается» — могу предположить .что механизм, который регистрирует изменения в узел.

Понятно, что она пишущая. Не понятно, почему она на столько «блокирующая».

5

mzelensky

06.04.21

13:06

Как лучше обойти проблему с присвоением номера выбираемым данным я не нашел. Во всех источниках применяется примерно такая же логика.

6

FIXXXL

06.04.21

13:25

(1) сделай обмен почаще, что бы порцайки не такие большие были

таблица плана обмена блокируется вся и наглухо… очень узкое место во всей концепции обменов на Планах

7

fisher

06.04.21

13:31

В режиме блокировок «Автоматический» для дедлока достаточно, чтобы оба процесса успели параллельно прочитать что-то нужное друг-другу для записи.

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

Но даже для первого варианта вроде эти звезды свести непросто. Хотя «Автоматический» еще эскалировать любит. Такое ощущение, что у тебя какие-то массированные пишущие регламенты крутятся, которые одно и то же могут перезаписывать. В итоге и таблица изменений постоянно большая и куча изменений тех же данных постоянно прилетает. Если это так, то попробуй развести их по времени хотя бы.

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

8

H A D G E H O G s

06.04.21

13:32

Добавить в

Запрос.Текст =»ВЫБРАТЬ ПЕРВЫЕ 3000

            |    Изменения.Ссылка КАК Объект

            |ИЗ

            |    Справочник.» + ИмяТаблицы + «.Изменения КАК Изменения

            |ГДЕ

            |    Изменения.Узел = &Узел»;

ДЛЯ ИЗМЕНЕНИЯ

9

H A D G E H O G s

06.04.21

13:33

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

10

fisher

06.04.21

13:39

(9) Тогда надо такое сделать одним запросом по всем выгружаемым справочникам. Тогда гарантия будет.

11

fisher

06.04.21

13:41

А зачем так вообще сделано? Зачем выгружать только справочники?

Почему вообще не свести это к единственному ВыбратьИзменения()?

12

Serg_1960

06.04.21

13:44

Автор реплику справочников базы данных делает?

13

fisher

06.04.21

13:45

А ЗаполнитьСтруктуруИзмененийДляУзла_Справочники() случаем не в транзакции вызывается?

14

mzelensky

06.04.21

13:47

(11) А разница?

15

mzelensky

06.04.21

13:48

(13) Уже проверил — программно не увидел такого.

16

mzelensky

06.04.21

13:49

Есть ли какой-то принципиально другой вариант выбрать данные из плана обмена с присвоением номера сообщения?

Я вот что-то не вижу альтернатив. Прям альтернатива — убрать к чертям эти планы обмена и делать все на обычном РС.

17

fisher

06.04.21

13:52

(15) Странно. Тогда действительно разницы быть не должно. Тогда действительно может помочь совет (8)

18

mzelensky

06.04.21

13:57

(17) Попробую (8)

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

Эти планы для регистрации элементов к выгрузке на сайт. Регламент выгрузки стартует каждые 3 минуты. Обычно очередь не очень большая, до 1000 элементов. Но иногда бывают «большие обмены», для этого ограничил размер порции 3000 элементами.

Механизм работает уже давно и довольно стабильно, но вот нашел одну «дырку», закрыть которую решил внедрением «ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения, ДанныеРегистрации);» (ранее этой конструкции не было и из плана обмена удалялись все выгруженные элементы, без контроля НомераСообщения). «Дырку» закрыл, но появились ошибки блокировки.

19

Serg_1960

06.04.21

14:02

Каждые три минуты у Вас вносятся изменения в тысячу и более записей справочников? Извини за вопрос не в тему: а что это у вас такое вообще?

20

fisher

06.04.21

14:06

(18) Только раз вышестоящей транзакции нет, то для (8) нужно запрос и регистрацию обрамить в транзакцию.

21

mzelensky

06.04.21

14:06

(19) К чему удивления? Ты не видел интернет-магазины у которых «новый заказ» падает каждые 2-3 секунды? В заказе от 1 до 10 единиц товара. Дальше идет обработка всего этого.

+ Розничные магазины — 30 штук.

+ Оптовый отдел.

22

fisher

06.04.21

14:06

Тьфу. Запрос и выборку изменений.

23

fisher

06.04.21

14:07

(21) Обычно при этом принято изменять документы, а не справочники.

24

mikecool

06.04.21

14:07

емнип, но могу ошибаться — транзакция активна до окончания перебора выборки изменений

25

mzelensky

06.04.21

14:08

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

26

mzelensky

06.04.21

14:09

(24) У меня вообще нет перебора выборки изменений

ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();

                Запись = Новый ЗаписьXML;

                Запись.УстановитьСтроку();

                ЗаписьСообщения.НачатьЗапись(Запись, УзелПланаОбмена);

                
                ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения, ДанныеРегистрации);

                
                РезультатФункции.Вставить(«ДанныеРегистрации», ДанныеРегистрации);

                РезультатФункции.Вставить(«НомерИсходящегоСообщения», ЗаписьСообщения.НомерСообщения);

                
                ЗаписьСообщения.ЗакончитьЗапись();

                Запись.Закрыть();

                
                ВыборкаИзменений = Неопределено;

                ЗаписьСообщения  = Неопределено;

                Запись              = Неопределено;

27

Serg_1960

06.04.21

14:09

Я понял, не нужно повторять.

28

fisher

06.04.21

14:11

(25) > возникают события, при которых элементы справочников нужно зарегистрировать к выгрузке во внешнюю системы

Зачем? Они поменялись? Почему? Или это банальная рекурсивная выгрузка всей используемой в заказах НСИ? Тогда зря.

29

mzelensky

06.04.21

14:12

(28) Сейчас не в этом вопрос. Давай не будем углубляться.

30

mikecool

06.04.21

14:13

(26) значит пришло время регистрацию изменений переделать на регистр сведений

31

mzelensky

06.04.21

14:16

(30) Да, было бы здорово перевести на РС.

Если победить блокировки не удастся, то, видимо, придется именно так и сделать.

Вообще механизм планов обмена какой-то тупиковый. Метода написана 10 лет назад и никакой эволюции.

32

fisher

06.04.21

14:20

(29) Тогда просто попробуй (8), обрамив чтение и выборку в транзакцию. По-идее, это должно заставить процессы телебонькать таблицу изменений по очереди, а не на брудершафт.

(30) На РС у тебя тоже могут возникнуть похожие проблемы

(31) Метода была написана еще в 7.7

Эволюция автомата калашникова не может происходить бескомпромиссно. К более продвинутому инструменту у тебя будет еще больше вопросов.

33

Ёпрст

06.04.21

14:29

(0) а зечем тебе механизм сообщений ?

Просто сам в коде удаляй регистрацию, если на сайт загрузилось и привет.

Тупо, план обмена регистрирует все изменения, ты их выгружаешь запросом на сайт и потом, по этой же выборке удаляешь регистрацию после обмена с сайтом. И Никаких номеров сообщений..

34

Kassern

06.04.21

14:30

(33) а что будет, если в момент выгрузки изменений зарегистрируются новые? Без сообщений, данные изменения просто похерятся

35

Cyberhawk

06.04.21

14:30

Выбирай не по 3000, а по 1000

36

Ёпрст

06.04.21

14:31

(34) И че ? Улетят при следующем обмене. Он отправит на сайт, только то, что запрос выгреб и по этой же выборке удалит изменения.

37

Ёпрст

06.04.21

14:31

И ничего там не херится.

38

Serg_1960

06.04.21

14:32

У автора есть простой выход: не пытаться втиснуть свой распухший обмен в регламент «каждые три минуты», а делать очередной обмен через три минуты после окончания предыдущего.

«Ну вот и славно! До свиданья… Да, а кислород попробуйте… и ванны.» — и запись всех изменений порциями в транзакции.

39

fisher

06.04.21

14:34

(33) У него так и было. Смотри (18). Но что-то он провтыкал, что таким образом решил полечить.

40

Ёпрст

06.04.21

14:34

+ если по какой-то причине, на сайт загрузить не удалось, то просто не удаляешь регистрацию изменений.

Я у себя именно так и делал. И никаких номеров сообщений. Только узел обмена и привет.

41

Kassern

06.04.21

14:36

(37) вот у тебя есть документ прихода, ты запустил выгрузку по плану обмена, формируешь файлик с остатками в разрезе номенклатуры и бац, в этот момент отменяют данный приход, но ты это изменение по документу не отработал и взял очистил полностью все изменения в плане обмена после успешной выгрузки. Далее ты изменения по этому документу не получишь, пока заново документ не перезапишешь. Поэтому и придумали номера сообщений, когда ты получаешь изменения, новые записи в план обмена уже попадают в разрезе нового номера сообщения.

42

Kassern

06.04.21

14:38

(41) а далее весь букет неприятностей связанный с не верным остатком и отмененными заказами

43

Ёпрст

06.04.21

14:40

(41) какой нах приход и тырнет магазин ?

44

Serg_1960

06.04.21

14:42

*(38) хочу напомнить: автор формирует искусственно «порции» измененных данных, случайным образом попавших в выборку — имхо, на три минуты (а может быть и более) можно забыть про целостность и не противоречивость данных…

45

mzelensky

06.04.21

14:45

(33)(39)(40)

Возможна следующая ситуация:

В плане обмена зареганы товары «Т1», «Т2» и «Т3». Стартует обмен, выбирает все 3 элемента — посылает на выгрузку и ожидает ответа от внешней системы о успешном получении (все синхронно). Далее успешно выгруженные данные удаляются из плана обмена.

А в момент между «выбирает все 3 элемента — посылает на выгрузку» и «успешно выгруженные данные удаляются из плана обмена» возникает изменение элемента «Т2» (т.е. возникает потребность снова ег овыгрузить). И вот если НЕ использовать «номера сообщений», то в конце система просто затрет все 3 элемента. И Второй раз элемент «т2» уже не выгрузится.

46

Kassern

06.04.21

14:46

(45) о чем я и писал в (41)

47

mzelensky

06.04.21

14:46

(46) ну да.

48

Ёпрст

06.04.21

14:47

(45) да не вопрос, если этого боишься, то сразу грохай изменения. Потом выгружай на сайт, если не удачно — зарегь их. Все данные у тебя будут в выборке запроса.

49

mzelensky

06.04.21

14:47

И решить это можно либо «Номером сообщения» — если использовать планы обмена. Либо обычной «Дата регистрации в очереди», если использовать РС.

50

Kassern

06.04.21

14:47

в свое время настраивал крупный хаб по обмену остатков поставщиков, где матрицы товаров исчислялись десятками тысяч. Несколько крупных оптовиков обменивались остатками между собой. Вот там номер сообщения для обмена был критически важен

51

mzelensky

06.04.21

14:49

(48) Плохая политика. Мне кажется так менее надежно.

52

Ёпрст

06.04.21

14:49

(51) чем ? Что за пару мс у тебя что-то не зарегится ? У тебя там по 100’000 справочников летит всегда в обмене ?

53

Kassern

06.04.21

14:50

(48) что значит удачно, неудачно? Выгрузка в основном всегда удачная, только в момент ее реализации могут быть еще изменения которые при очистке изменений в плане не попадут в следующую выгрузку, или я чего то не понимаю?

54

mzelensky

06.04.21

14:50

(44) Вот это вообще не понял. О какой целостности и противоречивости речь?

55

Ёпрст

06.04.21

14:51

(53) это значит, что http запрос вернул не 200.

56

Ёпрст

06.04.21

14:51

тырнет у тебя отвалился, еще чего

57

mzelensky

06.04.21

14:52

(53) Не удачная — это обработка ситуации, что на «принимающей» стороне произошел сбой\ошибка и данные не загрузились. Такое тоже бывает. Например, сайт «упал»

58

mzelensky

06.04.21

14:54

(52) Я полагаюсь на следующее — «гарантированно приняли данные — тогда удалили из очереди». А если удалять сразу, то больше вариантов потерять данные, если где-то что-то пойдет не так.

59

Kassern

06.04.21

14:54

(57) в этом случае вообще никаких проблем нет, не очищаешь изменения и все. Я же говорю про случаи, когда в момент выгрузки происходят изменения, чем более нагруженная выгрузка по времени выполнения, тем больше шансов на такие моменты

60

Kassern

06.04.21

14:57

(59) я прост не вижу проблемы в использовании номеров сообщений, получаешь изменения по последнему номеру, обрабатываешь их, если удачно отправил — очищаешь изменения с этим номером.

61

Serg_1960

06.04.21

14:58

(54) Это просто для понимания: если есть изменения — то они должны быть переданы все и сразу, иначе у Вас может возникнуть ситуация, когда логически связанные между собой изменения будут «разорваны» — окажутся в различных сеансах обмена данными. Самый негативный момент — когда может возникнуть ситуация, что ранее зарегистрированное изменение не попадет ни в первую, ни во вторую «порцию» сеанса обмена — выборка таблицы изменений ведь по природе своей — случайна.

62

Kassern

06.04.21

14:59

(61) это что то новенькое, первый раз такое слышу…можно на реальном примере?

63

Serg_1960

06.04.21

15:07

(62) Это совсем не новость :( На реальном примере? Например, рИБ-обмен, особенность обмена: документы и их движения — автономны и независимы друг от друга. И всегда есть вероятность между обменами получить новые движения без изменения самого документа и/или измененный документ со «старыми» движениями. Само собой разумеется, что  после очередного обмена всё само собой нормализуется.

64

Kassern

06.04.21

15:13

(0) Блокировки управляемые у тебя?

Почитай вот эту статью, может разрешишь проблему с блокировками, тут в общих словах более менее разжевано, как работает план обмена со скулем

https://infostart.ru/1c/articles/561460/

65

Kassern

06.04.21

15:16

(64) + еще вот тут есть схожая проблема и решение

https://forum.infostart.ru/forum9/topic230643/

66

Serg_1960

06.04.21

15:34

Попытка автора читать и писать изменения «порциями» никоем образом не изменяет ситуацию с блокировками.

67

Kassern

06.04.21

16:00

(66) насколько я понял, блокировка происходит при записи номера сообщения, когда получаешь выборку изменений. При управляемых блокировках, таблица не должна блокироваться, поэтому и ошибки с блокировками быть не должно

68

Serg_1960

06.04.21

18:08

(67) Там нет единой таблицы как таковой. Имхо, в (64) об этом прямо сказано: ПланыОбмена.ВыбратьИзменения() поочередно обрабатывают каждую из таблиц изменений метаданных, а SQL Server, соответственно, для каждой из этих таблиц открывает/закрывает транзакцию — отсюда и эксклюзивные транзакционные блокировки таблицы.

69

Garykom

06.04.21

18:12

(25) хрень какая то зачем сами элементы справочников повторно выгружать?

не логичнее документы?

70

Garykom

06.04.21

18:12

(69)+ или у вас остатки в номенклатуре? :)

Механизм обмена данными платформы 1С:Предприятие 8 основан на объектах конфигурации, которые называются «план обмена». Этот механизм является основным и призван решать следующие задачи: регистрация изменений объектов и обеспечение обмена сообщениями между узлами распределённой информационной системы. Данная статья предполагает, что читатель уже знаком с основными понятиями и имеет опыт использования планов обмена.

Работа с РИБ в рамках данной статьи рассмотрена не будет, так как РИБ полностью основан на описанных в статье механизмах.

Структуры данных.

План обмена на уровне СУБД это таблица, которая имеет название вида «_Node[N]», где N — это внутренний числовой код типа данных 1С:Предприятие 8. В данном случае этот код будет соответствовать плану обмена.

Создадим тестовый план обмена. В конфигураторе 1С (изображение слева) и в SQL Server Management Studio (изображение справа) это будет выглять следующим образом:

Как мы видим по структуре таблицы справа, план обмена очень похож на справочник. Это тоже ссылочный тип данных. Однако, от справочников его отличает то, что он не может иметь групп и быть иерархическим.

  Наименование поля      Имя реквизита   Описание поля
 _IDRRef  Ссылка  Ссылка на узел плана обмена
 _Version  Версия записи таблицы СУБД
 _Marked  ПометкаУдаления  Пометка удаления
 _Code  Код  Код узла
 _Description  Наименование  Наименование узла
 _SentNo  НомерОтправленного    Номер отправленного сообщения
 _ReceivedNo  НомерПринятого  Номер принятого сообщения
 _PredefinedID  Идентификатор предопределённого элемента  

Следует отметить, что при создании плана обмена, платформа автоматически создаёт предопределённый узел, соответствующий текущей информационной базе, в которой он создаётся. Реквизиты «Код» и «Наименование» по умолчанию имеют пустые значения, их необходимо заполнить самостоятельно. Для того, чтобы убедиться в этом, выполним следующий код в SQL Server Management Studio:

После того, как мы добавили план обмена, необходимо включить в его состав какой-нибудь объект, чтобы начать ослеживать изменения его данных. Включим в наш тестовый план обмена справочник «Номенклатура». После сохранения конфигурации платформа создаёт для этого справочника служебную таблицу регистрации изменений. Такую таблицу платформа создаёт для каждого включённого в план обмена объекта. Если объект включается в состав нескольких планов обмена, то таблица для регистрации изменений на уровне СУБД используется одна и та же.

Структура таблицы «_ReferenceChngR71» справочника «Номенклатура».

Наименование поля Имя Реквизита Описание поля
 _NodeTRef  Узел  Код типа плана обмена, например, у плана обмена «Тестовый» этот код равен значению «69».
 _NodeRRef  Узел  Ссылка на узел плана обмена
 _MessageNo  НомерСообщения   Номер сообщения, в котором данное изменение было выгружено для соответствующего узла плана обмена
 _IDRRef  Ссылка  Ссылка на элемент справочника «Номенклатура», который изменился

Таким образом, таблица регистрации изменений состоит из трёх обязательных полей и одного и более полей, которые необходимы для поиска изменившихся данных в основной таблице объекта. Для ссылочных типов данных таких, как справочники или документы, достаточно одного поля — «Ссылка». Однако для табличных типов данных, как, например, регистры сведений или регистры накопления, этих полей может быть несколько. Это зависит от установленного для измерений таких объектов свойства «Основной отбор». Более подробно об этом можно прочитать в книге «Профессиональная разработка в системе 1С:Предприятие 8. Издание 2.» на странице 484.

По умолчанию планы обмена используют автоматическую регистрацию изменений объектов, которые включены в их состав. Однако это поведение можно изменить. В таком случае придётся использовать программную (ручную) регистрацию. Отключить авторегистрацию можно так, как это показано на картинке ниже.

Индексы таблиц регистрации изменений.

Для полноты картины приведу здесь информацию об индексах, которые создаёт платформа для таблиц регистрации изменений. Комментировать ничего не буду, так как, по моему и так всё понятно. Их всего два:

1. Ключ + Узел (кластерный).

2. Узел + Номер сообщения + Ключ.

Момент выполнения регистрации изменений.

Интересным является то, в какой момент при записи объекта в информационную базу выполняется регситрация его изменения. Ниже я привожу последовательность возникновения событий, которая возникает при проведении документа. При этом включена автоматическая регистрация изменений для этого документа в плане обмена. При ручной регистрации мы можем вставить код регистрации изменений в любом месте нижеприведёной последовательности событий.

1. Начало транзакции записи документа.

2. ПередЗаписью.

3. ПередЗаписью (подписка на событие).

4. ПриЗаписи.

5. ПриЗаписи (подписка на событие).

6. ОбработкаПроведения.

7. ОбработкаПроведения (подписка на событие).

8. Регистрация изменения объекта во всех планах обмена, в состав которых он входит, а также для всех узлов этих планов обмена.

9. Фиксация транзакции записи документа.

Почему важно понимать в какой момент происходит регистрация изменений? И почему я привёл в пример именно документ? Дело в том, что регистрация изменений выполняется в одной транзакции с записью и проведением документа. Это обеспечивает согласованность этих двух операций: проведения и регистрации. Следовательно, любая логика регистрации изменений влияет на продолжительность этой транзакции, а также на момент выполнения запроса на блокировку записей таблицы регистрации изменений. В высоко нагруженных системах такой запрос часто приводит к ожиданию на блокировках, которые ранее уже наложила, например, процедура выгрузки изменений. Далее в статье я расскажу об этом более подробно.

Регистрация изменений.

Для регистрации изменения выполним следующий код 1С:

Если мы воспользуемся SQL Server Profiler и посмотрим, что происходит при вызове процедуры «ЗарегистрироватьИзменения», то мы увидим следующий код (для удобства восприятия код немного «причёсан»):

Как мы видим, на 2-ом шаге транзакции 1С сразу же пытается наложить эксклюзивную блокировку на запись регистрации изменения элемента справочника «Номенклатура» по соответствующему узлу плана обмена. Кроме этого, предпринимается попытка установить своеобразный флаг, сигнализирующий о том, что это изменение необходимо выгрузить в новом/следующем сообщении обмена. Этим флагом является реквизит «НомерСообщения» (поле «_MessageNo»), значение которого устанавливается равным NULL.

Обратите внимание, что если мы изменим один и тот же объект несколько раз, то запись изменений для этого объекта будет только одна. Это ключевая особенность планов обмена! Фактически это делает такую запись дефицитным ресурсом, за захват которого конкурируют разные транзакции. Если быть более точным, то таким ресурсом является поле «_MessageNo» этой записи.

По сути, если вы знакомы с проблемой блокировок при обновлении таблицы итогов регистров накопления, то здесь вы увидите схожую ситуацию. Ситуация аналогичная таблицам итогов возникает для планов обмена периодически и только в тех случаях, когда происходит выгрузка изменений. Однако, если в случае с регистрами накопления мы можем использовать режим разделения итогов, чтобы распраллелить хотя бы запись в таблицу итогов, то здесь этой возможности нет. Более подробно об этом будет рассказано ниже.

Ещё одним нюансом, который следует учитывать в высоко нагруженных системах, является то, что даже если мы просто перезапишем объект, ничего в нём не изменив, регистрация изменения этого объекта всё-равно будет выполнена. По этому для таких систем, в качестве оптимизации производительности, можно рекомендовать использование ручной регистрации изменений с контролем фактического изменения данных объекта.

Кроме этого очень важно то, что изменение регистрируется для всех узлов плана обмена. То есть если бы в нашем плане обмена было 10 узлов, то изменение одного объекта потребовало бы создания 10 записей, 100 узлов — 100 записей и так далее. И это только для одного изменения. Этот факт тоже важно учитывать.

Просмотр изменений.

Теперь попробуем изменить что-нибудь в нашем справочнике «Номенклатура» и посмотрим как это отразится в таблице регистрации изменений. Для этого можно воспользоваться следующими запросами 1С или T-SQL:

Запрос = Новый Запрос();
Запрос.Текст = "ВЫБРАТЬ * ИЗ Справочник.Номенклатура.Изменения";
SELECT * FROM [_ReferenceChngR71]

Однако, мы ничего не увидим … Дело в том, что регистрация изменений выполняется только в контексте узлов плана обмена. Это ещё одна ключевая особенность этого объекта конфигурации: все изменения регистрируются только в контексте какого-нибудь узла обмена. Поэтому добавляем новый узел в созданный ранее план обмена «Тестовый», и пытаемся изменить что-нибудь в справочнике «Номенклатура» снова. После этого проверяем таблицы изменений ещё раз и видим, что там появились записи.

Удаление регистрации изменений.

Теперь, чтобы удалить регистрацию изменения, выполним такой код 1С:

На этот раз 1С ограничивается одной командой SQL, пытаясь наложить всю ту же самую эксклюзивную блокировку по тому же самому ключу, что в общем-то ожидаемо и вполне нормально:

Запись изменений в сообщение обмена.

Для того, чтобы выгрузить изменения в другой узел распределённой информационной системы, необходимо эти изменения сначала прочитать, а затем записать в сообщение обмена. Механизм обмена сообщениями тесно интегрирован с планами обмена. В данном случае имеется в виду то, что запись сообщения обмена в файл сопровождается изменением реквизита «НомерСообщения» в таблице регистрации изменений объекта и ревизита «НомерОтправленного» в таблице плана обмена для соответствующего узла, для которого выполняется выгрузка данных. Чтобы реализовать всё это необходимо использовать объект «ЗаписьСообщенияОбмена». Именно этот объект позволяет нам управлять номером отправляемого сообщения: получать новый порядковый номер, записывать его в сообщение обмена, в таблицу регистрации изменений и реквизит узла плана обмена.

Чтобы исследовать код, который необходимо выполнить для записи сообщения обмена, я настроил технологический журнал 1С на регистрацию событий TLOCK, EXCP и TTIMEOUT. Эти события позволяют анализировать установление управляемых блокировок и ошибок, связанных с неудачными попытками установить их. Кроме этого я настроил SQL Server Profiler, чтобы посмотреть какие запросы платформа 1С генерирует на уровне СУБД.

Версия платформы 1С:Предприятие 8.3.7.2027. Управляемый режим блокировок.

// 0. Подготавливаем необходимые объекты.
ФайлСообщенияОбмена = "C:exportmessage.xml";
ЗаписьXML = Новый ЗаписьXML();
ЗаписьXML.ОткрытьФайл(ФайлСообщенияОбмена);

УзелОбмена = ПланыОбмена.Тестовый.НайтиПоНаименованию("Тестовый узел", Истина);

// 1. Создаём объект "ЗаписьСообщенияОбмена".
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();

// 2. Начинаем запись сообщения для выбранного узла в файл XML.
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелОбмена);

В книге «Профессиональная разработка в системе «1С:Предприятие 8» (издание 2) от 2012 года на странице 491 написано следующее:

При выполнении этого метода сообщению присваивается номер, определяемый как номер предыдущего отправленного сообщения, увеличенный на 1 (информация берётся из узла-получателя). Производится запись в XML-документ заголовка сообщения, а также записывается начало элемента XML, соответствующего телу сообщения. Устанавливается блокировка на запись базы данных, соответствующая узлу плана обмена.

Если посмотреть ТЖ 1С, то там мы не увидим установки блокировки. Значит управляемая блокировка не устанавливается. На уровне СУБД мы увидим обычный запрос, который характерен для вызова метода ссылки «ПолучитьОбъект»:

SELECT
T1._IDRRef,
T1._Version,
T1._Marked,
T1._Code,
T1._Description,
T1._SentNo,
T1._ReceivedNo,
T1._PredefinedID
FROM
dbo._Node69 T1 -- План обмена "Тестовый"
WHERE
T1._IDRRef = 0x9BD9408D5C93CC8E11E6A9E8C5C37A96 -- Узел обмена "Тестовый узел"

Постольку поскольку наша конфигурация находится в режиме управляемых блокировок, то такой запрос не является блокирующим. Блокировок нет. В книге ошибка? Давайте попробуем зарегистрировать какое-нибудь изменение по нашему узлу. Воспользуемся кодом из раздела «Регистрация изменений». Но сначала убедимся, что изменение уже есть, выполнив код SQL:

Получается в данный момент мы пытаемся выгрузить сообщение, которое ранее уже выгружалось под номером 13. Повторная выгрузка. В данный момент первая сессия 1С остановлена в точке останова после выполнения метода «НачатьЗапись». Теперь откроем вторую сессию 1С и попробуем зарегистрировать изменение этого объекта ещё раз в тот момент как оно отправляется:

ПланыОбмена.ЗарегистрироватьИзменения(УзелОбмена, СсылкаДляОбмена);

Теперь проверим, что изменилось:

Ошибок блокировки не было. Поле «_MessageNo» изменилось, сигнализируя нам, что его необходимо выгрузить. Так какая же блокировка имелась ввиду в книге? Неужели всё-таки ошибка? Ошибки никакой нет. Чтобы убедиться в этом выполним в сессии № 2 следующий код 1С:

Узел = ПланыОбмена.Тестовый.НайтиПоНаименованию("Тестовый узел", Истина);
Объект = Узел.ПолучитьОбъект();
Объект.Заблокировать();

В результате мы получим следующее сообщение об ошибке (эту же ошибку мы увидим в ТЖ, событие EXCP):

{ВнешняяОбработка.ИсследованиеПлановОбмена.МодульОбъекта(103)}: Ошибка при вызове метода контекста (Заблокировать): Не удалось заблокировать запись. Действие (изменение, удаление или блокировка записи) не выполнено.
Ошибка блокировки объекта. Объект уже заблокирован:
компьютер: Zhichkin, сеанс: 45, начат: 24.11.2023 в 1:00:46, приложение: Толстый клиент

Получается, что автор книги имел в виду объектную блокировку узла, а не блокировку записи базы данных. Следовательно, для того, чтобы убедиться в том, что выполняется выгрузка изменений по узлу, необходимо выполнить выше приведённый код и, если возникнет ошибка блокировки, то значит так оно и есть. Посмотрим что будет происходить дальше:

// 3. Выбираем изменения для выгрузки
Выборка = ПланыОбмена.ВыбратьИзменения(УзелОбмена, ЗаписьСообщения.НомерСообщения);
BEGIN TRANSACTION

UPDATE
[_ReferenceChngR71] -- Таблица регистрации изменений
SET
[_MessageNo] = 26 -- Очередной номер отправленного сообщения
WHERE
[_NodeTRef] = 0x00000045 -- План обмена "Тестовый"
AND
[_NodeRRef] = 0x9BD9408D5C93CC8E11E6A9E8C5C37A96 -- Узел "Тестовый узел"
AND
[_MessageNo] IS NULL -- Устанавливаем номер для ещё не отправленных сообщений

-- Выбираем все изменения, в том числе отправленные в других сообщениях
SELECT
[_IDRRef]
FROM
[_ReferenceChngR71]
WHERE
[_NodeTRef] = 0x00000045
AND
[_NodeRRef] = 0x9BD9408D5C93CC8E11E6A9E8C5C37A96

COMMIT TRANSACTION

Этот код будет выполнен для всех типов объектов, которые включены в состав плана обмена. Каждый тип в своей собственной транзакции. На время транзакции будет установлена эксклюзивная транзакционная блокировка SQL Server на все записи узла, которые ещё не отправлялись. Это означает, что повторное изменение уже заблокированных объектов будет ожидать окончания этой транзакции, а, в случае превышения времени ожидания, будет сгенерировано сообщение об ошибке:

При этом если мы зарегистрируем по этому узлу и типу объекта новое изменение, то оно успешно запишется. Более того, если это произойдёт до команды SELECT, то в выборку изменений попадут и эти новые изменения. Что интересно номер отправленного у них будет равен NULL, хотя это совершенно не так.

Кроме этого если записей будет более 5000, то может произойти эскалация блокировок SQL Server. В таком случае может оказаться заблокированной вся таблица. Чтобы этого не происходило, администраторы баз данных часто отключают на уровне SQL возможность возникновения такой эскалации, однако это не запрещает эскалации до уровня страниц (имеют размер 8 Kb), что может приводить к избыточной блокировке записей не попадающих в условия отбора команды UPDATE, но находящихся на одной странице вместе с нужными записями.

Таким образом к негативным последствиям выполнения операции выборки изменений можно отнести следующие:

1. Конфликт блокировок при одновременной работе пользователей и выгрузки изменений. Это может происходить, например, при частом перепроведении одних и тех же документов. При этом следует иметь в виду, что, чем больше изменений накопилось к моменту выгрузки, тем дольше выполняется транзакция и тем выше вероятность возникновения блокировок. В высоко нагруженных информационных системах это обстоятельство может приводить к большим проблемам.

2. Избыточные блокировки при больших объёмах изменений из-за эскалации блокировок SQL Server. Усугубляет последствия первого пункта.

3. Рассогласованность нумерации сообщений для новых регистраций объектов, которые не попали в первоначальный UPDATE. Следствием этого может быть повторная выгрузка одних и тех же данных.

Все выбранные изменения обрабатываются в оперативной памяти. Это происходит в следующем цикле:

// 4. Обработка выбранных изменений
Пока Выборка.Следующий() Цикл
Данные = Выборка.Получить();
ЗаписатьXML(ЗаписьXML, Данные);
КонецЦикла;

На этом этапе на уровне SQLServer ничего интересного не происходит: выполнение метода Выборка.Получить() получает данные объекта по его ссылке так, как если бы мы вызвали метод Ссылка.ПолучитьОбъект() или Набор.Прочитать(). Единственным недостатком можно назвать то, что это выполняется в цикле для каждого объекта, что трудно назвать оптимальным решением. Каждый объект записывается в сообщение обмена в формате XML. Это самый длительный этап выгрузки данных. К сожалению его практически невозможно распараллелить, так как запись ведётся в один файл.

В случае успешной записи сообщения обмена, завершаем сеанс выгрузки данных:

// 5. Завершаем сеанс выгрузки данных
ЗаписьСообщения.ЗакончитьЗапись();
UPDATE
[_Node69] -- План обмена "Тестовый"
SET
[_Marked]       = 0x00,
[_Code]         = N'1',
[_Description]  = N'Тестовый узел',
[_SentNo]       = 26, -- Номер только что отправленного сообщения обмена
[_ReceivedNo]   = 0,
[_PredefinedID] = 0x00000000000000000000000000000000
WHERE
[_IDRRef] = 0x9BD9408D5C93CC8E11E6A9E8C5C37A96 -- Узел "Тестовый узел"
AND
[_Version] = 0x00000000000077E8

Завершение сеанса выгрузки заключается в том, что для соответствующего узла плана обмена записывается номер только что отправленного сообщения обмена и вызывается метод объекта «Записать». В результате этого на узел плана обмена устанавливается управляемая блокировка — в техническом журнале 1С фиксируется событие TLOCK. Это очень короткая транзакция.

Чтение изменений из сообщения обмена.

Чтение изменений выполняется аналогичным записи сообщения образом. Важной отличительной особенностью является то, что вместо обновления номера отправленного сообщения выполняется удаление регистрации изменений для узла, от которого приняли сообщение обмена. Это нужно только в том случае, если обмен двунаправленный. В противном случае это не нужно. Кроме этого при завершении чтения сообщения обновляется не номер отправлденного сообщения узла плана обмена, а номер принятого. Пример кода загрузки сообщения обмена выглядит следующим образом:

ЧтениеXML = Новый ЧтениеXML();
ЧтениеXML.ОткрытьФайл(ПолноеИмяФайла);
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML, ДопустимыйНомерСообщения.Больший);
Пока ВозможностьЧтенияXML(ЧтениеXML) Цикл
Данные = ПрочитатьXML(ЧтениеXML);
Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;
Данные.ОбменДанными.Загрузка = Истина;
Данные.Записать();
КонецЦикла;
//ПланыОбмена.УдалитьРегистрациюИзменений(ЧтениеСообщения.Отправитель, ЧтениеСообщения.НомерПринятого);
ЧтениеСообщения.ЗакончитьЧтение();

Заключение.

Плюсы планов обмена:

    1. Простота программирования и настройки.

Минусы планов обмена:

    1. Избыточные блокировки данных.

    2. Конфликты блокировок транзакций.

    3. Регистрация одного изменения для каждого узла. На мой взгляд это избыточно, а кроме того интегрирует логику маршрутизации сообщений в подсистему регистрации изменений. Нарушается архитектурный принцип разделения ответственности между программными компонентами.

    4. Гранулярность обработки данных — узел обмена.

    5. Дублирование выгрузки данных.

    6. Плохие возможности для распараллеливания процессов.

    7. Тесная интеграция механизма регистрации изменений и инфраструктуры сообщений (их нумерация) диктует свои правила и ограничивает возможности реализации каких-то своих оптимизационных решений. См. также пункт 3.

При синхронизации возникла ошибка, которая уже 2 дня продолжается.
Объясните, пожалуйста, как исправлять эту ошибку, чтобы обмен снова заработал.
Базы клиент-серверные. Релиз УТ 11.4.12.91 Релиз БП 3.0.77.106

Ошибка установки блокировки на обмен данными.
Возможно, обмен данными выполняется другим сеансом.

Подробности:
Ошибка блокировки объекта. Объект уже заблокирован:
компьютер: sc-serv2, сеанс: 38029, начат: 15.09.2020 в 9:25:39, приложение: Фоновое задание
{ОбщийМодуль.ОбменДаннымиXDTOСервер.Модуль(1475)}:         ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
{Обработка.КонвертацияОбъектовXDTO.МодульОбъекта(209)}:   ОбменДаннымиXDTOСервер.ОткрытьФайлВыгрузки(КомпонентыОбмена, ИмяФайлаОбмена);
{ОбщийМодуль.ОбменДаннымиСервер.Модуль(7191)}:            ОбработкаОбменаДаннымиXML.ВыполнитьВыгрузкуДанных();
{ОбщийМодуль.ОбменДаннымиСервер.Модуль(6964)}:         ЗаписатьСообщениеСИзменениямиДляУзла(СтруктураНастроекОбмена, СтруктураНастроекОбмена.ОбработкаТранспортаСообщенийОбмена.ИмяФайлаСообщенияОбмена());
{ОбщийМодуль.ОбменДаннымиСервер.Модуль(381)}:   ВыполнитьОбменДаннымиЧерезФайловыйРесурс(СтруктураНастроекОбмена, ТолькоПараметры);
{ОбщийМодуль.ОбменДаннымиСервер.Модуль(3891)}:         ВыполнитьДействиеОбменаДляУзлаИнформационнойБазы(Отказ, УзелИнформационнойБазы,
{Обработка.ВыполнениеОбменаДанными.МодульМенеджера(34)}:   ОбменДаннымиСервер.ВыполнитьОбменДаннымиДляУзлаИнформационнойБазы(
{(1)}:Обработки.ВыполнениеОбменаДанными.ВыполнитьЗапускОбменаДанными(Параметры[0],Параметры[1])
{ОбщийМодуль.ОбщегоНазначения.Модуль(4883)}:   Выполнить ИмяМетода + «(» + ПараметрыСтрока + «)»;
{ОбщийМодуль.ДлительныеОперации.Модуль(1026)}:      ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяПроцедуры, ПараметрыВызова);
{ОбщийМодуль.ДлительныеОперации.Модуль(1016)}:      ВызватьПроцедуру(ВсеПараметры.ИмяПроцедуры, ВсеПараметры.ПараметрыПроцедуры);

Содержание
1. Ошибка 1С: “Конфликт блокировок при выполнении транзакции”. В чем причина?
2. Ошибки в 1С из-за блокировок
2.1 Пример необходимой блокировки в 1С
2.2 Пример избыточной блокировки в 1С 
2.3 Как избавиться от избыточных блокировок в 1С

Ошибка 1С: “Конфликт блокировок при выполнении транзакции”. В чем причина?

Этот вопрос возник у нас на проекте по внедрению ЗУП2.5 с численностью 20000 и средним количеством одновременных пользовательских сессий 200.

На этапе опытной эксплуатации при расчете зарплаты пользователи начали интенсивно работать с документами «Начисление зарплаты сотрудникам организаций». Объем документов был порядка 2500 строк.  У пользователей начали появляться сообщения «Конфликт блокировок при выполнении транзакции», и расчет приходилось запускать заново.

1.png

Стали разбираться. Оказалось, мы столкнулись  с эффектом «Избыточной блокировки». Обычно этот эффект появляется при параллельном проведении документов, во время него самым первым документом блокируется большой объем записей регистров на все время проведения документа. Эта блокировка задерживает проведение остальных документов, мешает параллельной работе пользователей и замедляет рабочий процесс. Вообще блокировка данных при проведении документов вещь полезная, она сохраняет целостность данных и гарантирует правильность выполнения расчетных алгоритмов. Но бывает так, что либо объем заблокированных данных чрезмерен, либо время блокировки слишком велико. В результате мы имеем многопользовательскую систему, которая по сути является однопользовательской: вместо параллельного проведения документов — последовательное.

Ошибки в 1С из-за блокировок


Пример необходимой блокировки в 1С

Представим такую ситуацию – есть два документа «Начисление зарплаты сотрудникам организаций», в которых указан одинаковый налоговый период, а на закладке НДФЛ указаны одинаковые сотрудники. Рассмотрим случай, когда блокировка вообще отсутствует. Если последовательно запускать расчет этих документов, то в первом сумма НДФЛ посчитается правильно, а во втором будет равна нулю, т.к. рассчитанный и фактически начисленный НДФЛ на момент проведения второго документа будут совпадать.

 Но если запустить эти документы параллельно, то они одновременно начислят НДФЛ, не подозревая о существовании друг друга, и в результате налог удвоится. Если блокировка настроена верно, то первый документ, запущенный на долю секунды раньше второго, успеет первым прочитать и заблокировать данные о фактически исчисленном налоге в регистре «НДФЛ расчеты с бюджетом» по сотруднику Пушкину А.С. Из этого запроса будет видно, что фактический налог за январь пока не начислялся и значит надо выполнить движение по регистру. Блокировка будет отпущена только после завершения записи в регистр. Второй документ, дойдя до запроса чтения фактически начисленного налога будет поставлен системой на ожидание до тех пор, пока первый документ не закончит транзакцию проведения, после чего он прочитает в запросе, что налог уже начислен и движение по регистру выполнять не надо. Это необходимая блокировка.

Конечно, этот пример притянут за уши для простоты объяснения. На самом деле логика ЗУП 2.5 такова, что для задвоения НДФЛ пользователям не нужно прикладывать особых усилий. НДФЛ рассчитывается до проведения документа, а при проведении содержимое табличной части просто заносится в регистры без всякой проверки. Пользователям между расчетом и проведением предоставляется возможность посмотреть будущий результат и при необходимости поправить руками. Конечно это большой плюс в пользу гибкости ЗУПа, но предъявляет высокие требования к профессиональному уровню расчетчиков. Поэтому вопрос предотвращения задвоения НДФЛ решается организационным путем или с помощью дополнительных проверочных отчетов. Конечно, в ЗУП2.5 есть регистры, которые рассчитываются и записываются одновременно при проведении документа, например «НДФЛ к зачету», но этот пример пришлось бы дольше объяснять ;).

Пример избыточной блокировки в 1С

А теперь представим другую ситуацию. При проведении документа выполняется запрос, который должен отобрать документы, в которых присутствует сотрудник из этого документа. Но запрос написан так, что сервер SQL вынужден находить нужные документы методом перебора. Для технических специалистов это означает, что вместо CLUSTERED INDEX SCAN выполняется TABLE SCAN, т.е. вместо сканирования таблицы индексов происходит сканирование самой таблицы. Причем в процессе перебора блокируются все записи, к которым прикоснулся запрос, даже те, в которых не присутствуют искомые сотрудники. И эта блокировка будет действовать до конца завершения проведения документа, что будет препятствовать параллельному проведению документов с другими сотрудниками. Это избыточная блокировка.

Как избавиться от избыточных блокировок в 1С

Лечение избыточных блокировок может идти двумя путями. Первый — это оптимизация запросов, выполняемых внутри транзакций и добавление необходимых табличных индексов в конфигураторе. Второй — это перевод выполнения SQL-запросов на более низкий уровень изоляции, когда при выполнении запросов записи в таблицах блокируются только на момент выполнения самого запроса, либо не блокируются вовсе. А необходимые блокировки устанавливаются средствами объекта «БлокировкаДанных» и выполняются на стороне сервера 1С.

Теперь немного теории про уровни изоляции на SQL сервере:

1.      В автоматическом режиме в транзакциях используется уровень изоляции SERIALIZABLE. Этот уровень накладывает блокировки типа X (запрещает чтение и запись) до конца транзакции на все данные, которых коснулись запросы или произошла запись данных.

2.      В управляемом режиме в транзакциях используется уровень изоляции ReadCommitted. Этот уровень на записанные данные также устанавливает блокировки типа X до конца транзакции. Но при выполнении запросов на данные накладывает блокировки типа S (запрещает запись и проверяет нет ли в этот момент параллельных записей), при завершении запроса блокировки снимаются не дожидаясь завершения транзакции.

3.      Если база данных переведена в режим  ReadCommitted SNAPSHOT, то в управляемом режиме при чтении данных не накладывается блокировка типа S, есть только блокировка типа X при записи.

Тоже самое чуть более подробно в таблице:

Обычно лечение начинают с понижения уровня изоляции, т.к. это не особо трудозатратно и дает быстрый результат. Достаточно перевести конфигурацию из «Автоматического» режима управления блокировкой данных в «Управляемый», и транзакции начнут выполняться на уровне изоляции типа ReadCommitted, вместо SERIALIZABLE или Repeatable Read.

Чтобы переключить базу данных в режим READ COMMITTED SNAPSHOT (RCSI) необходимо в «SQL Server Management Studio» в свойствах базы данных установить параметр «Is Read Committed Snapshot On» в значение «True»:

2.png 

В некоторых источниках предлагают установить параметр «Allow Snapshot Isolation» в значение «True», но в этом нет необходимости, т.к. это приведет к включению другого режима изоляции SNAPSHOT, который не поддерживается платформой 1С (На момент написания статьи релиз платформы 8.3.9).

Режим управления блокировкой данных задается для неявных транзакций, которые выполняются при записи или при проведении документов, т.е. внутри  предопределенных процедур типа ПриЗаписи() или ОбработкаПроведения(). Но большинство «тяжелых» вычислений в типовой конфигурации ЗУП2.5 происходит при выполнении команды «Рассчитать». При этом в модуле объекта запускается процедура РассчитатьВсе(), внутри которой неоднократно повторяется конструкция НачатьТранзакцию() …ЗафиксироватьТранзакцию(). Это явно указанные транзакции, внутри которых происходит запись и очистка регистров и выполняются запросы. Нам необходимо убедиться, что при переключении конфигурации в управляемый режим в процедуре «РассчитатьВсе()» транзакции также начинают выполняться на уровне ReadCommitted.

Для этого проведем небольшой эксперимент: 

• Запустим SQL Server Profiler.

• Запустим «NEW TRACE».

• Выполним подключение к серверу SQL.

• В окне «Trace Properties» на закладке «General» выберем «Use the template» = «Blank», а на закладке «Events Selections» раскроем группу «Stored Procedures» и выберем «RPC:Complited». По кнопке «Column Filters» укажем имя базы и длительность выполнения запросов более 1.

3.png 4.png 
• Кнопку RUN пока нажимать не будем, т.к. нам надо сначала запустить базу данных в режиме отладки и остановить выполнение расчета документа «Начисление зарплаты сотрудникам организаций» перед выполнением самого массивного запроса. Например, это будет команда
«Результат = Запрос.ВыполнитьПакет();» в функции «ПолучитьДанныеНДФЛПоРегистратору» в общем модуле «ПроведениеРасчетов». Здесь происходит выполнение основного запроса для расчета НДФЛ. Поставим на ней точку останова отладчика и запустим расчет в документе.
5.png
·         После того как отладчик остановится, нажмем кнопку RUN в Профайлере.

·         Теперь сделаем один шаг в отладчике кнопкой F11. Когда запрос будет выполнен и отладчик перейдет на следующий шаг, остановим чтение Профайлера кнопкой «Pause Selected Trace».

·         Теперь найдем самый длительный запрос по колонке Duration и внимательно изучим текст запроса. Если при обращении к реальной (а не временной) таблице после слова WITH стоит SERIALIZABLE, то мы имеем дело с автоматическим режимом блокировки. Если ничего не стоит – то с управляемым.

6.png 7.png 

Если в хинте запроса (Hint – это параметр после слова WITH, позволяющий влиять на план выполнения запроса) не указан уровень изоляции, то выполняется уровень изоляции, установленный по умолчанию для текущей SQL-сессии. Определить уровень изоляции, действующий по умолчанию для текущих сессий можно в «SQL Server Management Studio» с помощью команды

SEL ECT CASE transaction_isolation_level 

WHEN 0 THEN ‘Unspecified’ 

WHEN 1 THEN ‘ReadUncommitted’ 

WHEN 2 THEN ‘ReadCommitted’ 

WHEN 3 THEN ‘Repeatable’ 

WHEN 4 THEN ‘SERIALIZABLE’ 

WHEN 5 THEN ‘SNAPSHOT’ END AS TRANSACTION_ISOLATION_LEVEL 

FR OM sys.dm_exec_sessions

В управляемом режиме для всех сессий будет указан режим ReadCommitted.

Таким образом мы получили экспериментальное подтверждение того, что после перевода в управляемый режим всей конфигурации, в этом режиме начинают работать не только процедуры записи и проведения, но и процедуры расчета документов ЗУП. А это значит, что количество сообщений о конфликте блокировок будет существенно снижено, а параллельность работы с базой данных увеличится.

После того, как включили управляемый режим блокировки мы должны убедиться, что избавившись от избыточных блокировок, мы не ушли в другую крайность и не потеряли необходимые блокировки, которые защищают систему от нарушения целостности данных при активной параллельной работе пользователей.

Настройка  управляемых блокировок – это тема для отдельной статьи. Вкратце скажу, что программно управляемые блокировки устанавливаются с помощью объекта «БлокировкаДанных». Сами управляемые блокировки работают уже не на уровне SQL сервера, как в случае с автоматическими блокировками, а на уровне сервера 1С. Для определения необходимых и достаточных управляемых блокировок надо понимать логику программы одновременно на уровне бизнес-процессов и на уровне архитектуры таблиц СУБД.

Но на мой взгляд, для таких конфигураций, как ЗУП2.5 вообще нет смысла использовать какие-либо блокировки, лучше использовать проверочные отчеты для выявления нарушения целостности данных — на практике это самый быстрый способ расчета зарплаты. Особенно на крупных предприятиях, где точно есть сотрудники с внутренним совмещением в обособленных подразделениях, а за каждым ОП закреплен отдельный расчетчик, что и является причиной задвоения НДФЛ. Какой бы не был вышколенный персонал, сама идеология конфигурации допускает возможность задвоения НДФЛ. Поэтому лучше не мешать пользователям работать параллельно во время массированных месячных расчетов, а по завершении точечно и быстро исправить небольшой процент ошибок, чем заставлять их сидеть и нервничать в очереди из-за страха допустить хотя бы одну ошибку. В этом проекте мы использовали самописный отчет «Проверка НДФЛ», который отображал сотрудников с некорректным НДФЛ.

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

Валерий Федоров

Руководитель проектов ООО “Кодерлайн”

Иногда при работе в 1С может возникнуть ошибка «Конфликт блокировок при выполнении транзакции: превышено максимальное время ожидания предоставления блокировки». Рассмотрим как исправить данную ошибку.

Содержание

  • Конфликт блокировок при выполнении транзакции в 1С: причины и пути их устранения
    • Причина 1. Одновременная работа пользователей с большим объемом данных
    • Причина 2. Зависшие блокировки в 1С
    • Причина 3. Ошибка в конфигурации

Причина 1. Одновременная работа пользователей с большим объемом данных

При одновременной работе нескольких пользователей с одной и той же информационной базой могут возникнуть ситуации, когда несколько пользователей пытаются внести в данные какие-то изменения. И в таких случаях, если не предусмотреть возможности подключения специальных механизмов системы, могут возникнуть проблемы с информационной базой, ее целостностью. При обращении нескольких сеансов к одним и тем же данным одновременно используются механизмы блокировок и транзакций.

Блокировка — это информация о том, что данный объект занят пользователем для выполнения какого-то действия. Смысл блокировки в запрете определенных действий над объектом на ограниченное время. Благодаря этим запретам сохраняется целостность и непротиворечивость информационной базы, предсказуемость работы пользователей.

Механизм объектных блокировок — обеспечивает конкурентный доступ пользователей к данным 1С, как правило, это работа пользователей в формах — создание новых объектов, их редактирование, удаление и др.

Для согласованного изменения данных в 1С применяется механизм транзакций, а для конкурентного доступа к данным — механизм транзакционных блокировок. Если транзакции нужно получить доступ к элементу, то она блокирует элемент и продолжает удерживать блокировку элемента до тех пор, пока не освободит элемент. Это происходит либо в ходе выполнения транзакции, либо по ее окончании как успешном, так и не успешном.

Поэтому, при одновременном чтении и изменении одних и тех же данных могут возникнуть проблемы одновременного доступа, о чем программа 1С и сообщает в ошибке. Это временная ошибка и когда другой пользователь завершит работу с этим объектом, ошибка не будет появляться.

Причина 2. Зависшие блокировки в 1С

Иногда возникают такие ситуации, что пользователь уже давно закрыл документ, вышел из программы, а его сеанс по-прежнему остается в системе. Т.е. какой-то сеанс поставил блокировку и не убрал ее. Такие ситуации чаще встречаются при использовании сетевых ключей защиты.

Оптимальный вариант решения проблемы — это найти и завершить сеанс, заблокировавший необходимые данные. В этом нам помочь может раздел Администрирование — Обслуживание, где можем посмотреть активных пользователей и при необходимости заблокировать их работу, т.е. отключить.

При большом количестве компьютеров, проблему можно решить завершив подобный сеанс через консоль администратора. С ее помощью можно управлять подключенными сеансами определенной информационной базы, управлять блокировками. В консоли администрирования можно посмотреть такие зависшие сеансы и удалить их. Эта утилита входит в состав 1С Предприятия.

Запустить ее можно из папки common 1CV8Servers.

Причина 3. Ошибка в конфигурации

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

Если такого рода ошибки появляются часто, то разобравшись в причине, из-за которой возникает конфликт блокировок в 1С, необходимо их ликвидировать — определить уязвимые места в системе, оптимизировать конфигурацию, а также работу пользователей.

См. также:

  • Недостаточно памяти 1С: как исправить
  • Неверный формат хранилища данных 1С 8.3: как исправить
  • Ошибка формата потока 1С 8.3: как исправить
  • Ошибка СУБД: файл базы данных поврежден в 1С 8.3
  • Не найден файл внешней компоненты в 1С 8.3: как исправить

Если Вы еще не являетесь подписчиком системы БухЭксперт8:

Активировать демо-доступ бесплатно →

или

Оформить подписку на Рубрикатор →

После оформления подписки вам станут доступны все материалы по 1С Бухгалтерия, записи поддерживающих эфиров и вы сможете задавать любые вопросы по 1С.

Подписывайтесь на наши YouTube и Telegram чтобы не пропустить
важные изменения 1С и законодательства

Помогла статья?

Получите еще секретный бонус и полный доступ к справочной системе БухЭксперт8 на 14 дней бесплатно

Понравилась статья? Поделить с друзьями:

Интересное по теме:

  • Ошибка блокировки средств
  • Ошибка бмв tur offen
  • Ошибка блокировки руля пассат б6
  • Ошибка бмв 040
  • Ошибка бмв s0772

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии