gifts2017

Ускоряем регистрацию объектов в МОД (для SQL)

Опубликовал Вадим 1С911.BY (Вадимко) в раздел Администрирование - Оптимизация БД (HighLoad)

Менеджер обмена данными - популярный и очень своеобразный продукт
В статье рассматривается решение одной из его проблем - получения нового уникального IDD
После оптимизации проведения ключевых документов у нас начались блокировки констант
Переделывать будем вот эту функцию:

Функция
ПолучитьНовыйИДД
() //Предусматриваем одновременное обращение к константе нескольких пользователей
Блокировка
=1
;
	
Пока
Блокировка
=1 Цикл Попытка
НачатьТранзакцию
()
;
			
Константа.
УникальныйIDD
=Константа.
УникальныйIDD;
//блокируем
Блокировка
=0
;
//константа заблокирована не была - можем работать Исключение//константа заблокирована !!
ОтменитьТранзакцию
()
;
			
//Ожидание для возможности работы других пользователей (по совету Олега Яковлева из ЧПТФ "ЮСИ")
Стр
=
ТекущееВремя
()
;
			
Пока
Стр
=
ТекущееВремя
() Цикл КонецЦикла
;
		
КонецПопытки
;
	
КонецЦикла
;
	
//Увеличиваем счетчик в константе на единицу Константа.
УникальныйIDD
=
Число
(Константа.
УникальныйIDD
)+1
;
	Рез
=
Прав
("0000000"+
СокрЛП
(Константа.
IDD
),7)+
прав
("0000000000"+
СокрЛП
(Константа.
УникальныйIDD
),10)
;
	ЗафиксироватьТранзакцию
()
;
	
Возврат
Рез;
	
КонецФункции

До совета Олега Яковлева она, видимо, вообще не давала жизни пользователям :)

Итак приступим...
Будем использовать 1С++
В глобальном модуле в процедуру ПриначалеРаботыСистемы() добавим следующие строки для создания таблицы и заполнения ее значениями:

//таблица IDD для МОДа
ТекстЗапроса 
= "
|if not exists 
(
select name from dbo
.
sysobjects where name 
=
'UIDD' and xtype 
=
'U '
)
|create table UIDD 
(
idd char
(7),
uidd numeric
(10,0))
|
";
RecordSet
.
ВыполнитьИнструкцию
(
ТекстЗапроса
)
;
	
//начальное заполнение значений таблицы
ТекстЗапроса 
= "
|if 
(
select count
(*)
from UIDD 
(
nolock
)) = 0
|insert into UIDD values 
(
:ИДД
,
:УникальныйИДД
)
|
";
RecordSet
.
УстановитьТекстовыйПараметр
("ИДД", Константа.
IDD
)
;
RecordSet
.
УстановитьТекстовыйПараметр
("УникальныйИДД",
Число
(Константа.
УникальныйIDD
))
;
	
RecordSet
.
ВыполнитьИнструкцию
(
ТекстЗапроса
)
;


Теперь переделанный текст функции (спасибо за обсуждение на 1cpp.ru коллегам):

Функция
ПолучитьНовыйИДД
() Экспорт
ТекстЗапроса 
= "
|begin tran  
	|select right
(
'
0000000
' 
+
LTrim
(
idd
), 7) +
right
(
'
0000000000
' 
+
LTrim
(
str
(
uidd 
+ 1, 10)),10)
|from UIDD 
(
updlock
)
|update UIDD
	|set uidd 
=
uidd 
+ 1
|commit tran  
	|
";
ТЗ 
=
RecordSet
.
ВыполнитьИнструкцию
(
ТекстЗапроса
)
;
	
	
Возврат
ТЗ
.
ПолучитьЗначение
(1,1)
;
КонецФункции

Слово Экспорт добавили не зря
В обработках конфигурации мы найдем изобилие конструкций с блокировкой константы, которые также нужно переделать
Пример переделанного кода (оригинальные строки кода разработчиков закоментированы)

Для
ы
=1
по 
Метаданные.
Справочник
() Цикл
НачатьТранзакцию
()
;
		
//Блокировка=1; //Пока Блокировка=1 Цикл // Попытка // НачатьТранзакцию(); // Константа.УникальныйIDD=Константа.УникальныйIDD;//блокируем // Блокировка=0;//константа заблокирована не была - можем работать // Исключение//константа заблокирована !! // ОтменитьТранзакцию(); // //Ожидание для возможности работы других пользователей (по совету Олега Яковлева из ЧПТФ "ЮСИ") // Стр=ТекущееВремя(); // Пока Стр=ТекущееВремя() Цикл // КонецЦикла; // КонецПопытки; //КонецЦикла;
КолПрогрессора
=
КолПрогрессора
+10
;
		Прогрессор
(
КолПрогрессора
)
;
//**************** прогрессор
Ст
=Метаданные.
Справочник
(
ы
).
Идентификатор;
		
Если (
ст
<>"ПравилаЗагрузки")
и 
(
ст
<>"ПравилаВыгрузки")
и 
(
ст
<>"ПериферийныеБазы") Тогда
Спр
=СоздатьОбъект("Справочник."+
Ст
)
;
			М_Состояние
("Обработка "+
Ст
)
;
			Спр
.
ВыбратьЭлементы
(0)
;
			
Пока
Спр
.
ПолучитьЭлемент
()>0 Цикл Если (
Сокрлп
(
Спр
.
IDD
)="")ИЛИ(
ФлНовый
=1) Тогда //Константа_УникальныйIDD=Константа_УникальныйIDD+1; //Спр.IDD=Сокрлп(Константа_IDD+прав("0000000000"+Константа_УникальныйIDD,10));
Спр
.
IDD 
=
ПолучитьНовыйИДД
()
;
					
Попытка
Спр
.
Записать
()
;
					
Исключение
Сообщить
("Ошибка на : "+
Спр
.
Вид
()+", владелец "+
Спр
.
Владелец
+", элемент="+
Спр
.
ТекущийЭлемент
()+", код="+
Спр
.
Код
+" . Внимание: найдите данный элемент справочника вручную и измените код !!")
;
					
КонецПопытки
;
				
КонецЕсли
;
				
				
Если
РежимИниц
=1 Тогда
ПриИзмененииОбъектаМОД
(
Спр
,
Спр
,
'
01.01.1976
'
,0)
;
//для периодических реквизитов надо задавать период выгрузки
ПриИзмененииОбъектаМОД
(
Спр
,
Спр
,
РабочДата
,0)
;
//у нас период равен: 01.01.1976 - рабочая дата КонецЕсли
;
				
			
КонецЦикла
;
		
КонецЕсли
;
		
//Константа.УникальныйIDD=Строка(Число(Константа_УникальныйIDD)+1);
ЗафиксироватьТранзакцию
()
;
	
КонецЦикла
;



Не забывайте о том, что МОД при нахождении неуникальных IDD присваивает уникальные значения УЖЕ СУЩЕСТВУЮЩИМ объектам
Лично я это пока что закомментировал :)

Вот и все
Жду отзывов и предложений


Оригинал статьи
http://1c911.by/stati_1s/statya-uskoryaem-registraciyu-obektov-v-mod-dlya-sql.htm
Изменения и новые публикации смотрите на http://1c911.by/stati-1s.htm

См. также

Подписаться Добавить вознаграждение

Комментарии

1. int3 (int3) 31.01.08 10:00
Мелкософт в буксе очень не рекомендует прямое обращение к системным таблицам, т.к. их формат может меняться даже при установке сервис-пака, не говоря уже о других версиях
Проверку на существование таблицы я делаю обычно так:
Код
ТекстЗапроса = "
|if object_id('UIDD') is null
|create table UIDD (idd char(7), uidd numeric(10,0))
|";
Показать полностью

да и в ПолучитьНовыйИДД() стоит проверять состояние @@ERROR после запросов, мало ли что...
Но в целом, много лучше исходного "фирменного" ужаса :)
2. int3 (int3) 31.01.08 10:09
кстати, думаю можно еще уменьшить время SQL-блокировок и повысить параллелизм, сменив стратегию: перейти от явного инкремента uidd к использованию identity, и соответственно от select-updlock-update к insert-select-delete...
хотя... транзакции короткие, может это и несущественно
3. Вадим 1С911.BY (Вадимко) 31.01.08 11:11
Спасибо за каменты и проявленный интерес
Я тоже так проверяю существование, но не всегда, просто скопипастил из своего же :)
Насчет идентити тоже соглашуся
п. 2- это настолько несущественно и настолько отличается от "а ля ПиБи" что... :)
Если не трудно напиши свой вариант и выложи сюда
//
Джонии, спасибо за плюс :)
4. Вадим 1С911.BY (Вадимко) 31.01.08 11:28
Кстати, если вы заметили, в таблице всего одна строка, поэтому и апдейт
Таблица по сути заменяет константу, идентити тут имхо и нафик не нужно
В общем буду рад прочитать советы, возражения, критику и т.п.
5. Евгений Мартыненков (JohnyDeath) 31.01.08 12:25
(3) Джонии, спасибо за плюс :)
Я всё связвнное с 1с++ плюсую неглядя! ;)
6. Аркадий Кучер (Abadonna) 31.01.08 12:29
1С++ - два плюса, а ставишь один ;)))
7. Евгений Мартыненков (JohnyDeath) 31.01.08 12:54
8. int3 (int3) 31.01.08 14:28
(3) примерно так:
Код
//инициализация таблицы
ТекстЗапроса = "
|if object_id('tempdb..#uidd') is null
|create table uidd(uidd numeric(10,0) identity(:УникальныйИДД,1))
|";
RecordSet.УстановитьТекстовыйПараметр("УникальныйИДД",   Число(Константа.УникальныйIDD));
RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);

//использование
Функция ПолучитьНовыйИДД() Экспорт
                ТекстЗапроса = "
|begin tran
|insert into uidd default values
|select right('0000000000' + LTrim(str(@@identity, 10)),10)
|rollback tran
|";
   
   ТЗ = RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);
   Возврат Константа.IDD+ТЗ.ПолучитьЗначение(1,1);
КонецФункции
Показать полностью

различается стратегия получения автоинкрементного значения, возрастает параллелизм
но, как уже и говорилось, возможно это "ловля блох", т.к. транзакции действительно короткие
насчет же "нужно ли идентити" скажу следующее: идентити инкрементируется внетранзакционно, без блокировки остальных, в отличие от явного инкремента значения - это может уменьшить время ожидания блокировки, хотя оно и так наверное невелико :)
Вадимко; +1 Ответить
9. int3 (int3) 31.01.08 14:29
упс, октоторп тут лишний :)
читать так:
|if object_id('tempdb..uidd') is null
10. int3 (int3) 31.01.08 14:49
и опять я промахнулся, всё с тестами над темпдб :)
|if object_id('uidd') is null
11. Вадим 1С911.BY (Вадимко) 31.01.08 16:24
Спасибо, но мне не нужно хранить ВСЕ ИДД
Я храню только последний
А значнеие ИДД базы запихнул чтобы не считывать отдельно константу :)
ЗЫ. Если кто будет применять метод int3 отключайте на время выполнения запроса инициилизации RecordSet.РежимRPC(0) если он был включен
12. int3 (int3) 31.01.08 17:07
(11) Откуда же там будут ВСЕ ИДД? Это тоже "константа", а точнее простой генератор :)
13. Вадим 1С911.BY (Вадимко) 31.01.08 21:15
Ты что-то перемудрил :)
Проверь на практике, номер не увеличивает
А если транзакцию не откатить - строки будут размножаться
14. int3 (int3) 31.01.08 22:57
(13) перед тем как запостить, как раз и проверил. отсюда и рудименты типа "tempdb..#uidd" :)
и даже в теории такое поведение описывается, так что возвращаю совет "проверить на практике" ;)
15. Вадим 1С911.BY (Вадимко) 31.01.08 23:31
Доперло, посмотрю как токо появится время :)
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа