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

29.05.13

База данных - 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

См. также

Блокировки SQL базы данных 1С 7.7

HighLoad оптимизация Платформа 1С v7.7 Конфигурации 1cv7 Абонемент ($m)

Конфигурация на 1С 7.7, показывающая блокировки на MS SQL сервере и доменных пользователей по SPID. Используется 1С++ и классы.

1 стартмани

09.11.2021    4613    8    ShoDm    17    

11

Ускорение работы 1С 7

HighLoad оптимизация Платформа 1С v7.7 Конфигурации 1cv7 Бесплатно (free)

Недорогое повышение скорости работы 1С

31.05.2013    12996    ins-post    22    

-1

Удаление нулевых значений в промежуточных регистрах

Чистка данных HighLoad оптимизация Платформа 1С v7.7 Конфигурации 1cv7 Абонемент ($m)

По статье "Зачем в 1С нужно периодически пересчитывать итоги по регистрам?" http://infostart.ru/public/177171/ Обработка для 7.7, чтобы посмотреть что же творится в БД для SQL

1 стартмани

13.03.2013    23178    53    maxpiter    15    

8

Не гибкие блокировки в "1С:Предприятие 7.7", но чуть-чуть "Управляемые".

HighLoad оптимизация Оперативный учет 7.7 Бухгалтерский учет 7.7 Расчет 7.7 Конфигурации 1cv7 Россия Бесплатно (free)

Обратились ко мне с вопросом по теме форума: http://forum.mista.ru/topic.php?id=558772 Автор темы: "DennizzM". Название: "v7: 1c v7.7 ошибки транзакции - как отловить виновника?" Текст с сокращениями: "Вопрос наверняка не новый... Итак - есть база 1c v7.7 (самописная конфа). Периодически у пользователей возникает ошибка при проведении транзакции. База работает под терминалом. Нагрузка на дисковую подсистему небольшая, CPU на нуле, RAM до черта свободного. Вопрос вот в чем - как отловить инициатора первой транзакции которая всех держит? Итак - как мне выкрутиться? ;) ...я не имею права и не могу лезть внутрь конфы и модифицировать ее.".

13.07.2011    26197    hogik    16    

11
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. 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 31.01.08 10:09 Сейчас в теме
кстати, думаю можно еще уменьшить время SQL-блокировок и повысить параллелизм, сменив стратегию: перейти от явного инкремента uidd к использованию identity, и соответственно от select-updlock-update к insert-select-delete...
хотя... транзакции короткие, может это и несущественно
3. Вадимко 214 31.01.08 11:11 Сейчас в теме
Спасибо за каменты и проявленный интерес
Я тоже так проверяю существование, но не всегда, просто скопипастил из своего же :)
Насчет идентити тоже соглашуся
п. 2- это настолько несущественно и настолько отличается от "а ля ПиБи" что... :)
Если не трудно напиши свой вариант и выложи сюда
//
Джонии, спасибо за плюс :)
5. JohnyDeath 301 31.01.08 12:25 Сейчас в теме
(3) Джонии, спасибо за плюс :)
Я всё связвнное с 1с++ плюсую неглядя! ;)
8. 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 Ответить
4. Вадимко 214 31.01.08 11:28 Сейчас в теме
Кстати, если вы заметили, в таблице всего одна строка, поэтому и апдейт
Таблица по сути заменяет константу, идентити тут имхо и нафик не нужно
В общем буду рад прочитать советы, возражения, критику и т.п.
6. Abadonna 3958 31.01.08 12:29 Сейчас в теме
1С++ - два плюса, а ставишь один ;)))
7. JohnyDeath 301 31.01.08 12:54 Сейчас в теме
9. int3 31.01.08 14:29 Сейчас в теме
упс, октоторп тут лишний :)
читать так:
|if object_id('tempdb..uidd') is null
10. int3 31.01.08 14:49 Сейчас в теме
и опять я промахнулся, всё с тестами над темпдб :)
|if object_id('uidd') is null
11. Вадимко 214 31.01.08 16:24 Сейчас в теме
Спасибо, но мне не нужно хранить ВСЕ ИДД
Я храню только последний
А значнеие ИДД базы запихнул чтобы не считывать отдельно константу :)
ЗЫ. Если кто будет применять метод int3 отключайте на время выполнения запроса инициилизации RecordSet.РежимRPC(0) если он был включен
12. int3 31.01.08 17:07 Сейчас в теме
(11) Откуда же там будут ВСЕ ИДД? Это тоже "константа", а точнее простой генератор :)
13. Вадимко 214 31.01.08 21:15 Сейчас в теме
Ты что-то перемудрил :)
Проверь на практике, номер не увеличивает
А если транзакцию не откатить - строки будут размножаться
14. int3 31.01.08 22:57 Сейчас в теме
(13) перед тем как запостить, как раз и проверил. отсюда и рудименты типа "tempdb..#uidd" :)
и даже в теории такое поведение описывается, так что возвращаю совет "проверить на практике" ;)
15. Вадимко 214 31.01.08 23:31 Сейчас в теме
Доперло, посмотрю как токо появится время :)
Оставьте свое сообщение