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

26.08.24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 

 

Вступайте в нашу телеграмм-группу Инфостарт

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

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

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

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

58000 руб.

04.08.2015    190735    460    307    

460

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 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" с помощью правил конвертации будет максимально комфортным! | Можно бесплатно проверить перенос на вашем сервере!

50050 руб.

25.02.2015    189917    369    292    

426

SALE! 15%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 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. Не требуется синхронного обновления правил после обновления другой конфигурации, участвующей в обмене. Типовой обмен через планы обмена кнопкой Синхронизация вручную или автоматически по расписанию, или вручную обработкой.

27633 руб.

12.06.2017    162274    980    324    

483

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

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

58000 руб.

15.04.2019    85474    229    180    

166

SALE! 10%

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

Переносите справочную информацию, остатки и документы из УПП 1.3 в Бухгалтерию 3.0 с помощью готовых правил. Переносится более 50 видов документов. Простой интерфейс и понятные настройки.

42000 37800 руб.

15.12.2021    35029    261    64    

199

Перенос данных 1C Программист 1С:Предприятие 8 1С:Комплексная автоматизация 1.х 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Перенос данных из УПП 1.3 в УНФ | из КА 1.1 в УНФ | из УТ 10.3 в УНФ | Перенос разработан в формате КД 2 (правила конвертации объектов) | Выгружаются все возможные виды документов, начальных остатков и вся нормативно-справочная информация | Есть фильтр по организациям при выгрузке данных | Есть несколько алгоритмов выгрузки начальных остатков товаров на выбор | Можно проверить перед покупкой на своем сервере!

58000 руб.

17.10.2019    45161    60    117    

62

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

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

50600 руб.

21.05.2019    59026    81    134    

73

Внешние источники данных Кадровый учет Файловый обмен (TXT, XML, DBF), FTP Перенос данных 1C Программист 1С:Предприятие 8 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

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

85400 руб.

05.10.2022    13962    16    8    

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