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

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 всю возможную информацию | В переносе есть фильтр по организации и множество других опциональных параметров выгрузки | Есть несколько алгоритмов выгрузки остатков на выбор

55778 50200 руб.

04.08.2015    166775    334    278    

375

SALE! 10%

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

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

35000 31500 руб.

15.12.2021    24192    171    51    

130

SALE! 15%

Перенос данных 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    141848    799    297    

420

SALE! 10%

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

Перенос данных из 1С:Управление торговлей 10.3 в 1С:Управление торговлей 11.5 с помощью правил обмена. Переносятся остатки, документы (обороты за период), справочная информация. Правила проверены на конфигурациях УТ 10.3 (10.3.88.x) и УТ 11.5 (11.5.19.x).

35000 31500 руб.

23.07.2020    51572    228    70    

187

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 | Можно проверить на вашем сервере перед покупкой

55778 50200 руб.

15.04.2019    72212    182    150    

124

SALE! 10%

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

Перенос данных из 1С:Управление производственным предприятием 1.3 в 1С:Бухгалтерия предприятия 3.0 с помощью правил обмена | Можно выполнить переход с УПП на БП 3 или запускать выгрузку данных за выбранный период времени | Переносятся документы, начальные остатки и вся справочная информация | Есть фильтр по организации и множество других параметров выгрузки | Поддерживается несколько сценариев работы: как первичный полный перенос, так и перенос только новых документов | Перенос данных возможен в "1С: Бухгалтерия 3.0" версии ПРОФ, КОРП или базовую | Переход с "1С: УПП1.3" / "1С:КА 1.1" на "1С:БП3.0" с помощью правил конвертации будет максимально комфортным! | Можно бесплатно проверить перенос на вашем сервере!

48278 43450 руб.

25.02.2015    171312    302    257    

378

SALE! 10%

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

Правила переноса кадровых и расчетных данных и справочной информации из "1С:УПП1.3" или "1С:КА 1.1" в "1С:ЗУП 3.1 | Разработан в формате КД 2 (правила конвертации данных) | При выгрузке есть фильтр по организациям | Обновляется при выходе новых релизов 1С | Развитие алгоритмов | Расчетные документы переносятся в документ "Перенос данных" | Создаются документы "Начальная штатная расстановка" и "Начальная задолженность по зарплате", переносятся кадровые документы

55778 50200 руб.

29.10.2018    56304    59    105    

61

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

Регулярный обмен, выгрузка, перенос из КА 1.1, УПП 1.3, УТ 10.3 для обмена с любыми конфигурациями, поддерживающими обмен в формате EnterpriseData (КД3) - БП 3.0, ERP, КА 2, УТ 11, Розница 2, УНФ 1.6 и другими. Правила для старых и доработанных конфигураций не требуют синхронного обновления и совместимы с новыми и будущими конфигурациями. Обмен по расписанию, через папку, FTP, почту.

15300 руб.

18.02.2016    187032    590    509    

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