Почему тормозит первый запрос к файловой базе 1С по сети после простоя и как это исправить?

Программист 1С v8.3 (Обычные формы) 1С:Розница Управленческий учет Торговля и дистрибуция
← На главную

Разберем распространенную ситуацию: в файловой базе 1С, к которой пользователи подключаются через локальную сеть по общему сетевому ресурсу, при первом нажатии на кнопку обработки после некоторого периода неактивности система зависает на 5–10 секунд. При повторных нажатиях тот же самый код выполняется мгновенно. Однако стоит сделать паузу в работе — и задержка повторяется снова. Проанализируем, почему возникает такое поведение, как устроена работа файлового движка по сети и как оптимизировать структуру данных и программный код для устранения этих пауз.

Анализ механизма возникновения проблемы

Для понимания корня проблемы проанализируем, как платформа 1С работает с файловой базой данных (файлом .1CD) через сетевую папку по протоколу SMB:

  1. Отсутствие серверного движка СУБД: При подключении толстого или тонкого клиента напрямую к файлу базы по сети вся вычислительная нагрузка и обработка базы данных происходят на стороне клиентского компьютера. Клиент самостоятельно считывает страницы данных из общего файла .1CD.
  2. Сброс сетевого кэша при простое: Во время первого обращения клиент считывает необходимые страницы таблицы через сеть, после чего операционная система сохраняет их в оперативной памяти (кэш страниц и SMB-кэш). При повторных обращениях запрос выполняется мгновенно, так как данные берутся прямо из локального ОЗУ. При длительном простое операционная система освобождает память и сбрасывает кэш, вынуждая 1С при следующем запросе заново выкачивать блоки данных по сети.
  3. Полное сканирование таблицы (Full Scan): Если запрос не может использовать индекс, движок 1С вынужден последовательно передать по сети всю таблицу регистра сведений с компьютера-хранилища на рабочий компьютер пользователя.

Анализ исходного кода и структуры метаданных

Посмотрим на исходный запрос, вызывающий задержку:


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

В данном примере измерение НомерПродажи стоит на первом месте в регистре сведений ХранилищеСуточныхПродаж. Поскольку это поле хранит уникальные ключи, основной индекс формируется по правилу: НомерПродажи + ДатаПродажи + Номенклатура + .... Платформа 1С не может применить индекс, если в условии ГДЕ не используется самое первое поле составного индекса. В результате при каждом первом запуске после сброса кэша вся таблица регистра полностью пересылается по локальной сети.

Способ 1. Исправление структуры и порядка измерений регистра

Главный индекс независимого регистра сведений всегда строится строго по порядку измерений, заданному в дереве конфигурации сверху вниз. Чтобы запрос с отбором по полям СменаЗакрыта, ЧекФРРаспечатан и Номенклатура отрабатывал по индексу без полного сканирования таблицы, выполним следующие шаги:

  1. Откроем конфигуратор и перейдем в ветку Регистры сведений к объекту ХранилищеСуточныхПродаж.
  2. В ветке Измерения переместим поля с помощью стрелок так, чтобы первыми шли поля, по которым выполняется регулярный отбор:
    1. СменаЗакрыта
    2. ЧекФРРаспечатан
    3. Номенклатура
    4. НомерПродажи (и остальные поля)
  3. Для критически важных полей поиска при необходимости установим свойство Индексировать. Однако помним, что в файловом режиме платформа наиболее эффективно использует именно составной индекс основных измерений.

Способ 2. Оптимизация текста запроса и алгоритма обработки

Упростим и ускорим выполнение кода. Обработку значения NULL при агрегации лучше доверить языку запросов через функцию ЕСТЬNULL, а также исключить лишний цикл выборки для скалярного результата:


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

Способ 3. Переход на публикацию базы через веб-сервер (HTTP-сервис / Тонкий клиент)

Если специфика сети или объем таблицы не позволяют добиться мгновенного отклика через сетевой ресурс Windows, наилучшим архитектурным решением для файловой базы является веб-публикация:

  1. На компьютере, где физически расположен файл .1CD, установим и настроим веб-сервер (Apache или IIS).
  2. Через меню Администрирование > Публикация на веб-сервере опубликуем базу данных.
  3. На клиентских рабочих местах настроим подключение тонкого клиента по протоколу http://имя_компьютера/путь_к_базе вместо прямого пути к файлу \\server\base.

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

Способ 4. Корректировка системных настроек электропитания и антивируса

Если задержки после простоя сохраняются, необходимо исключить аппаратное засыпание оборудования:

  1. Отключение сна жестких дисков: В панели управления Windows компьютера с базой перейдем в Электропитание > Настройка схемы электропитания > Изменить дополнительные параметры питания. В разделе Жесткий диск установим параметр «Отключать жесткий диск через» в значение 0 (Никогда). Это предотвратит потерю 3–5 секунд на раскрутку диска при обращении.
  2. Исключения антивируса: Добавим файл базы данных 1Cv8.1CD, а также системные каталоги временных файлов %LOCALAPPDATA%\1C и %APPDATA%\1C в список доверенных исключений сканирования на клиенте и сервере.
← На главную