Медленно работает 1С? Проблема замедления массового проведения документов в 1С MSSQL   

Периодически в форумах и в обращениях ИТ специалистов звучат высказывания, что при длительной работе в одной сессии 1С v7.7 происходит общее замедление производительности, т.е. начинают медленно проводиться документы в 1С 7.7.

Попытаемся описать эту проблему подробней. Эта проблема актуальна:

  1. При выполнении интенсивных обработок (например, массовом перепроведении документов 1с).
  2. При длительной работе сессий. Например, когда пользователь вообще не выходит из 1С-Предприятия а администратор длительно не проводит с сервером и базой никаких работ.

 

Каков источник этой проблемы медленного проведения в 1С 7.7 и для кого она особенно актуальна?
Первопричина кроется в ошибке MSSQL 2000 описанной по ссылке:
http://support.microsoft.com/?scid=kb;e ... &spid=2852

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

Для того, что бы в этом убедиться, необходимо вставить следующий скрипт в QA и запустить его на исполнение:

use pubs 
go

set nocount on
declare @i int
set @i = 0
declare @tt as datetime
set @tt = getdate()

while @i < 100000
begin
if @i % 10 = 0
begin
if exists (select * from tempdb.dbo.sysobjects where id =object_id('tempdb.dbo.#T1'))
drop table #T1
CREATE TABLE #T1(Id int NULL)
end
exec sp_executesql N'Insert into #T1 values(@P1)', N'@P1 int', @i
select @i = @i + 1
if @i % 10000 = 0 and @i > 0
begin
print datediff(ms,@tt,getdate())
select @tt = getdate()
end
end

 

Разберем этот скрипт подробнее:

  1. Организовывается цикл
  2. Через каждые десять шагов создается и удаляется временная таблица
  3. Через каждые 10000 шагов выводится временной интервал выполнения этих 10000 шагов

 

Запустив этот скрипт, мы увидим, что время на выполнение одинаковых операций по мере исполнения увеличивается:
4013
4563
6596
9936
14733
37936
29953
38296
54826
67626

Например, в этом результате исполнения данного скрипта мы видим, что если на первом шаге время выполнения было 4 секунды, то на 10-м 67 сек.
То есть более чем в 10 раз! Не правда ли, есть о чем задуматься?

Но в данном случае производится операция insert в sp_executesql. А их в модулях 1С не так уж и много относительно количества общих операций.

Заменим в скрипте
exec sp_executesql N'Insert into #T1 values(@P1)', N'@P1 int', @I
на
exec sp_executesql N'if 1=1 declare @m int', N'@P1 int', @i
т.е. просто будем внутри sp_executesql объявлять переменную @m.

Запустив его опять на исполнение мы увидим, что тенденция соблюдается:

673
1203
2390
4686
7640
10626
14246
17330
20970
23796

 

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

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

Решение этой проблемы описано в статье по вышеуказанной ссылке. Все они сводятся к рекомендациям не использовать подобные конструкции или чаще делать reconnect.

Рассмотрим теперь, где в 1С используются подобные конструкции:

ТЗИт=СоздатьОбъект("ТаблицаЗначений");	
Список=СоздатьОбъект("СписокЗначений");
ВыбратьСтроки();
Пока ПолучитьСтроку()=1 Цикл
Список.ДобавитьЗначение(Товар);
КонецЦикла;

РегОстатки = СоздатьОбъект("Регистр.Остатки");
РегОстатки.УстановитьЗначениеФильтра("Товар",Список,2);
РегОстатки.ВыгрузитьИтоги(ТЗИт,1);
Это пример кода из модуля проведения документов в 1С. В данном случае рассчитываются остатки по товарным позициям документа в модуле проведения. В профайлере установка фильтра по списку значения будет выглядеть следующим образом:
if exists (select * from TEMPDB..SYSOBJECTS where ID = OBJECT_ID('TEMPDB..#TMP0PROC') and SYSSTAT & 0xf = 4 )
drop procedure #TMP0PROC;
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE ID=OBJECT_ID('tempdb..#TMP0') AND sysstat & 0xf = 3 )
DROP TABLE #TMP0;
Во временной таблице TMP0 содержатся значения списка товара.
Процедура sp_executesql используется в большинстве вызовов 1С. Вот пример из трэйса по исполнению того же модуля:
exec sp_executesql N'Select * from DH475(NOLOCK) where IDDOC=@P1', N'@P1 varchar(9)', '     2   '

 

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

Как правило, конструкция УстановитьЗначениеФильтра используется один раз в модуле и поэтому данная проблема особо актуальна в информационных базах с большим количеством документов.

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

Тем не менее, решение есть. Для этого достаточно самому из 1С периодически очищать буфер сессии.
Компания СофтПоинт представляет компоненту, с помощью которой, можно производить подобную операцию. Реализована она в качестве dll которая имеет доступ к SQL в рамках рабочей сессии. У данной компоненты реализован всего один метод ОчиститьБуфер. Его можно вызывать как периодически (самостоятельно определив частоту вызова), так и вставив его в модуль обработки проведения.

Процедура ОбработкаПроведения()
Если глСч=100 Тогда
SP.ОчиститьБуфер();
глСч =0;
КонецЕсли;
глСч =глСч+1;

ТЗИт=СоздатьОбъект("ТаблицаЗначений");
Список=СоздатьОбъект("СписокЗначений");
ВыбратьСтроки();
Пока ПолучитьСтроку()=1 Цикл
Список.ДобавитьЗначение(Товар);
КонецЦикла;

РегОстатки = СоздатьОбъект("Регистр.Остатки");
РегОстатки.УстановитьЗначениеФильтра("Товар",Список,2);
РегОстатки.ВыгрузитьИтоги(ТЗИт,1);

 

В данном примере он будет вызываться из модуля проведения через каждые 100 проведенных документов. Проведенные тесты и эксплуатация программного комплекса показали, что использование данной методики полностью снимает вышеописанную проблему медленного проведения в 1С 7.7 и позволяет использовать связку 1С-MSSQL более эффективно.

 


 

Перепечатка, воспроизведение в любой форме, распространение, в том числе в переводе, любых материалов с сайта www.softpoint.ru возможны только с письменного разрешения компании "СофтПоинт". Это правило действует для всех без исключения случаев, кроме тех, когда в материале прямо указано разрешение на копирование (основание: Закон Российской Федерации "Об авторском праве и смежных правах").