Еще раз про планы обмена и блокировки

26.08.24

База данных - HighLoad оптимизация

Если вы используете планы обмена, вы наверняка сталкиваетесь с блокировками. И хоть на инфорстарте полно статей про планы обмена и про блокировки, хотелось бы поделиться нашим опытом борьбы с ними.

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

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

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

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

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

Я не буду здесь в деталях пересказывать уже процитированную статью, но если кратко, то любимый метод плана обмена ВыбратьИзменения() устанавливает блокировку на все измененные данные. Как и почему? Ну он пробегается по всем таблицам изменений и бежит в них делать Update, заменяя Null на текущий номер сообщения. Обычная же запись в справочник или регистр, включенный в состав плана обмена, тоже пытается сделать Update по соответствующей записи в таблице изменений, прописав туда Null. Ну а теперь представим, что с данными идет частая работа, документ, например, провели, отменяем сразу по какой-то причине, то есть оба регистра СтатусыСлушателей и ДанныеСлушателей помечены как измененные. Начинается снова проведение и примерно одновременно фоновое задание вызвало ВыбратьИзменения(). Но в проведении мы сначала пишем в Статусы, а потом в Данные, а ВыбратьИзменения() обходит таблицы в обратном порядке. Вот вам и дедлок. Да, можно узнать, в каком порядке ВыбратьИзменения() обходит таблицы и писать при проведении в том же, но у нас вырожденный пример, а в реальной базе с кучей связанных справочников и регистров вы наверняка не сможете в каждой транзакции писать данные ровно в том порядке, в котором это делает план обмена.

Я не сказал, но, конечно, речь идет об управляемых блокировках. Что нам в данном случае советует 1с? Да, конечно, ставить платформенные управляемые блокировки. Например, при проведении, мы можем сделать так (здесь регистры независимые, у них есть измерение Слушатель).

БлокировкаДанных = Новый БлокировкаДанных;                                          

ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.ДанныеСлушателей");	
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = Ссылка.Список;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Слушатель", "Слушатель");

ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.СтатусыСлушателей");	
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = Ссылка.Список;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Слушатель", "Слушатель");

БлокировкаДанных.Заблокировать();

Вставляем этот код, думаем, что теперь-то мы заблокировали оба регистра, и дедлока не будет... и это не помогает.

Здесь хочется сформулировать мысль, которая при чтении документации была не очевидной. Да, там написано про платформенную блокировку (это вот которая БлокировкаДанных.Заблокировать()) а также про блокировку СУБД (которую накладывает движок базы данных по операциям Update). Так вот почему-то было ощущение, что платформенные блокировки накладываются средствами блокировок СУБД (сейчас специалисты по оптимизации тихонько хихикают, ну что же). Так вот нет, совсем нет, и, надеюсь, что эта мысль будет важна для начинающих читателей. Платформенные блокировки и блокировки СУБД никак не связаны! Если почитать технологических журнал, у платформенных блокировок будет специальное событие TLock. Ну а СУБД, это вам, скорей, в анализатор событий СУБД.

Поэтому мало накладывать платформенную блокировку при проведении, надо тогда и перед ВыбратьИзменения(). Вопрос только, на что ее накладывать? Ну, например, можно сделать вот так:

	Запрос = Новый Запрос;
	Запрос.Текст ="ВЫБРАТЬ
	              |	ДанныеСлушателейИзменения.Слушатель КАК Слушатель
	              |ИЗ
	              |	РегистрСведений.ДанныеСлушателей.Изменения КАК ДанныеСлушателейИзменения";
		
	ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.ДанныеСлушателей");	
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки.ИсточникДанных = Запрос.Выполнить().Выгрузить();
	ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Слушатель", "Слушатель");	

Смотрите, что происходит. До вызова ВыбратьИзменения() мы обратились запросом к таблице изменений (что не накладывает никаких блокировок), взяли все, что изменилось, и дальше накладываем блокировку по измерению. Достаточно брать разделяемую, поскольку документы накладывают исключительную.

Только вот я сейчас на один регистр наложил блокировку. А, может, лучше на оба? Или, может, на все, что могло поменяться? Можно и на все, вот ниже работающий метакод:

	Для Каждого Элемент Из Метаданные.ПланыОбмена.ОбменССайтом.Состав Цикл
		Если СтрНачинаетсяС(Элемент.Метаданные.ПолноеИмя(), "Справочник.") Или
			СтрНачинаетсяС(Элемент.Метаданные.ПолноеИмя(), "Документ.") Или
			СтрНачинаетсяС(Элемент.Метаданные.ПолноеИмя(), "ПланВидовХарактеристик.") Тогда
			// ссылочный тип
			Запрос = Новый Запрос;
			Запрос.Текст = "ВЫБРАТЬ
				|	ОбъектИзменения.Ссылка КАК Ссылка
				|ИЗ
				|	" + Элемент.Метаданные.ПолноеИмя() + ".Изменения КАК ОбъектИзменения";
			ЭлементБлокировки = БлокировкаДанных.Добавить(Элемент.Метаданные.ПолноеИмя());
			ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
			ЭлементБлокировки.ИсточникДанных = Запрос.Выполнить().Выгрузить();
			ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Ссылка", "Ссылка");	
		ИначеЕсли 
			СтрНачинаетсяС(Элемент.Метаданные.ПолноеИмя(), "РегистрНакопления.") Или
			СтрНачинаетсяС(Элемент.Метаданные.ПолноеИмя(), "РегистрСведений.") И Элемент.Метаданные.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.ПодчинениеРегистратору Тогда
			// блокировка по регистратору
			Запрос = Новый Запрос;
			Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
	               |	РегистрИзменения.Регистратор КАК Регистратор
	               |ИЗ
	               |	" + Элемент.Метаданные.ПолноеИмя() + ".Изменения КАК РегистрИзменения";
			ЭлементБлокировки = БлокировкаДанных.Добавить(Элемент.Метаданные.ПолноеИмя() + ".НаборЗаписей");
			ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
			ЭлементБлокировки.ИсточникДанных = Запрос.Выполнить().Выгрузить();
			ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Регистратор", "Регистратор");
		ИначеЕсли СтрНачинаетсяС(Элемент.Метаданные.ПолноеИмя(), "РегистрСведений.") Тогда
			// блокировка по первому измерению
			Для Каждого Измерение Из Элемент.Метаданные.Измерения Цикл
				ИмяИзмерения = Измерение.Имя; 
				Прервать;
			КонецЦикла;
			Запрос = Новый Запрос;
			Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
	               |	РегистрИзменения." + ИмяИзмерения + " КАК " + ИмяИзмерения + "
	               |ИЗ
	               |	" + Элемент.Метаданные.ПолноеИмя() + ".Изменения КАК РегистрИзменения";
			ЭлементБлокировки = БлокировкаДанных.Добавить(Элемент.Метаданные.ПолноеИмя());
			ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
			ЭлементБлокировки.ИсточникДанных = Запрос.Выполнить().Выгрузить();
			ЭлементБлокировки.ИспользоватьИзИсточникаДанных(ИмяИзмерения, ИмяИзмерения);
		КонецЕсли;
	КонецЦикла;

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

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

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

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

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

И напоследок статьи на тему, что штудировались чтобы решить проблему (правда, удалось решить только после переписки в комментариях)

 

 

планы обмена управляемые блокировки блокировки СУБД блокировки дедлок

См. также

SALE! 10%

Перенос данных 1C Программист Платформа 1С v8.3 1С:Управление производственным предприятием 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Платные (руб)

Перенос документов, начальных остатков и справочной информации из УПП 1.3 в ERP 2 | из УПП 1.3 в УТ 11 | из УПП в КА 2 | Правила конвертации (КД 2) | Более 360 предприятий выполнили переход с использованием этого продукта! | Сэкономьте время - используйте готовое решение для перехода! | Позволяет перенести из УПП 1.3 в ERP / УТ 11 / КА 2 всю возможную информацию | В переносе есть фильтр по организации и множество других опциональных параметров выгрузки | Есть несколько алгоритмов выгрузки остатков на выбор

50722 45650 руб.

04.08.2015    165175    384    276    

369

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Россия Платные (руб)

Правила в универсальном формате обмена для ERP 2.5, КА 2.5, УТ 11.5, БП 3.0, Розница, УНФ, для последних версий конфигураций. Ссылки на другие конфигурации в описании публикации. Правила совместимы со всеми другими версиями конфигураций новыми и старыми, поддерживающими обмен и синхронизацию в формате EnterpriseData. Не требуется синхронного обновления правил после обновления другой конфигурации, участвующей в обмене. Типовой обмен через планы обмена кнопкой Синхронизация вручную или автоматически по расписанию, или вручную обработкой.

26280 руб.

12.06.2017    140195    783    295    

410

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Управление производственным предприятием 1С:Бухгалтерия 3.0 Россия Бухгалтерский учет Управленческий учет Платные (руб)

Перенос данных из 1С:Управление производственным предприятием 1.3 в 1С:Бухгалтерия предприятия 3.0 с помощью правил обмена. Переносятся остатки, документы (обороты за период), справочная информация. Правила проверены на конфигурациях УПП 1.3 (1.3.234.x) и БП 3.0 (3.0.161.x). Правила подходят для версии ПРОФ и КОРП.

28000 руб.

15.12.2021    23170    160    48    

117

Зарплата Внешние источники данных Бюджетный учет Перенос данных 1C Системный администратор Программист Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 8 учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

84000 руб.

19.08.2020    24418    23    1    

25

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Программист Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Платные (руб)

Перенос данных из ERP в БП 3 | из КА 2 в БП 3 | из УТ 11 в БП 3 | из ЕРП в БП 3 | Сэкономьте время - используйте готовое решение для перехода! | Перенос разработан в формате КД 2 (правила конвертации данных) | Переносятся все возможные виды документов, начальных остатков и нормативно-справочная информация| Можно опционально выгружать каждую пару "номенклатура+характеристика" как отдельную номенклатуру | Есть выгрузка настроек счетов учета и зарплатных данных из ERP / КА 2 | Можно проверить на вашем сервере перед покупкой

50722 45650 руб.

15.04.2019    71542    180    149    

120

SALE! 10%

Перенос данных 1C Взаиморасчеты Оптовая торговля Логистика, склад и ТМЦ Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Управление торговлей 10 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Управленческий учет Платные (руб)

Можно проверить до покупки, оставьте заявку! Воспользовались более 268 компаний! Перенос данных из УТ 10.3 в УТ 11 | из УТ 10.3 в КА 2 | из УТ 10.3 в ERP. Предлагаем качественное и проверенное временем решение для перехода с УТ 10.3. Можно перенести начальные остатки, нормативно-справочную информацию и все возможные документы. При выгрузке можно установить отбор по периоду, организациям и складам. При выходе новых релизов конфигураций 1C оперативно выпускаем обновление переноса данных.

50722 45650 руб.

24.04.2015    194193    149    242    

279

SALE! 10%

Перенос данных 1C Системный администратор Программист Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Платные (руб)

Правила переноса данных из БП 3.0 в УТ 11 | из БП 3.0 в КА 2 | из БП 3.0 в ERP | Сэкономьте свое время - используйте готовое решение для перехода! | Постоянно работаем над развитием переноса данных | Обновляем на новые релизы 1С | Есть фильтр выгрузки по организациям | Переносятся начальные остатки на выбранную дату, документы за период времени и вся возможная справочная информация | Перенос сделан на технологии КД 2 (правила конвертации данных) Воспользовались более 122 предприятий! |

50722 45650 руб.

31.10.2014    236074    99    334    

304

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Программист Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Россия Бухгалтерский учет Управленческий учет Платные (руб)

Перенос данных из ERP в ЗУП 3 | из КА 2 в ЗУП | Готовые правила конвертации данных (КД 2) для переноса остатков, документов с движениями и справочной информации 3 | Есть перенос начальной задолженности по зарплате и начальной штатной расстановки на выбранную дату | Обороты за прошлые годы (данные для расчета среднего) переносятся свернуто в документ "Перенос данных" | Есть фильтр по организациям | Документы за текущий период переносятся сразу с движениями, поэтому не потребуется делать перерасчеты | Перенос можно проверить перед покупкой, обращайтесь!

48278 43450 руб.

03.12.2020    36047    90    63    

86
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. zhichkin 1519 26.08.24 14:48 Сейчас в теме
Спасибо за статью. Материал сложный. На мой взгляд имеет смысл подумать о том, каким образом можно было бы выразить суть статьи графически в виде каких-нибудь наглядных схем, таблиц или диаграмм. Например, какая-нибудь таблица последовательности захвата ресурсов (блокировок) конкурирующими транзакциями/процессами.
Кроме этого, считаю, что ещё было бы справедливо указать ссылку на вот эту статью: Распределенные взаимоблокировки в 1С системах
Sander80; rozer; starik-2005; +3 Ответить
6. Sander80 100 27.08.24 22:35 Сейчас в теме
(1) Попробую вставить ссылку, если модерацию пройдёт. Я постарался подобрать все, что читал, но, естественно, мог пропустить что-то из важного. Впрочем, оно теперь первым комментарием висит, так что не потеряется)
2. partizand 136 26.08.24 21:35 Сейчас в теме
Интересно. А вы знаете, что если сформировать движения в свойствах документа Движения и выполнить Записать(), то движения по регистрам запишутся строго в порядке их следования в конфигурации. Раз уж не знали про блокировки. Если так сделать во всех документах, то это позволит избежать дедлока по разному порядку захвата ресурсов.
И возможно выбратьИзменения пишет в том же порядке.
Sander80; +1 Ответить
5. Sander80 100 27.08.24 22:33 Сейчас в теме
(2) Движения это все же для подчиненных регистров. В нашей большой базе (иэя для статьи логику упростил и поменял) документ пишет много куда, потом вызываются всякие подписки, в общем, вариант, что запишется все в порядке конфигурации было бы сложно профорсировать
3. partizand 136 26.08.24 21:50 Сейчас в теме
И да. Я думаю вам сначала нужно найти причину блокировок. Вплоть до строки. А потом уже думать над решением.
Управляемые блокировки перед ВыбратьИзменения выглядят избыточно.
7. Sander80 100 27.08.24 22:37 Сейчас в теме
(3) В своем случае я нашел это через технологический журнал. Но не знаю, стоит ли это пытаться выложить здесь, это совсем не будет читаться без специфики базы
4. RustIG 1728 27.08.24 10:40 Сейчас в теме
в типовых уже решили вопрос с блокировками - например, посмотрите как реализована отмена проведения и проведение документов в УТ 10.3
Sander80; +1 Ответить
8. Sander80 100 27.08.24 22:38 Сейчас в теме
(4) Спасибо, изучу. Ну и, видимо, прочим страждущим пригодится, думаю я тут не единственный, кто на обмена с блокировками борется
Оставьте свое сообщение