Как оптимизировать динамический список в 1С v8.3 при медленном поиске и отборах на больших объемах данных?

Программист 1С v8.3 (Управляемые формы) Сфера услуг, туризм и социальный сектор
← На главную

При работе с большими справочниками (от 300 тысяч элементов и более) в платформе 1С:Предприятие 8.3 разработчики часто сталкиваются с критическим падением производительности динамических списков на управляемых формах. Особенно остро эта проблема проявляется в файловом режиме работы базы данных (расширение .1CD), запущенной на компьютерах с классическими жесткими дисками (HDD). Пользователи начинают жаловаться на то, что форма списка открывается по 20–30 секунд, а наложение отборов по типу «Содержит» намертво вешает систему.

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

Выясняем причины низкой производительности

Для начала проанализируем ситуацию. В нашем распоряжении плоская таблица справочника на 350 тысяч записей, содержащая более 20 реквизитов, среди которых преобладают строковые поля (ФИО, адреса, комментарии). Основные проблемы, с которыми мы сталкиваемся:

  1. Долгое открытие списка (до 30 секунд). Несмотря на включенное свойство ДинамическоеСчитываниеДанных, система тратит огромное количество времени на инициализацию запроса и первичную выборку метаданных.
  2. Зависание при отборе «Содержит». При использовании данного вида сравнения СУБД транслирует условие в SQL-запрос с оператором LIKE "%Текст%". Символ процента в начале строки делает стандартные индексы типа B-Tree абсолютно бесполезными. База данных вынуждена выполнять полное сканирование таблицы (Full Table Scan), считывая терабайты секторов с медленного HDD.
  3. Избыточное индексирование. Наличие 11 проиндексированных реквизитов в одной таблице критически раздувает индексный файл базы данных. Любая операция записи замедляется, а планировщик запросов СУБД начинает ошибаться при выборе оптимального индекса для построения плана запроса.

Ниже рассмотрим по шагам, как исправить эти архитектурные и программные проблемы.

Шаг 1. Оптимизация структуры запроса динамического списка

Главная ошибка при проектировании динамического списка — выборка всех 20–22 реквизитов непосредственно в основном тексте запроса. Даже если эти поля просто выводятся в колонки, платформа вынуждена прокачивать большой объем данных. Мы должны минимизировать тело основного запроса.

Удалим из основного запроса динамического списка все второстепенные текстовые поля (такие как комментарии, подробные адреса, номера свидетельств, данные заказчиков). Оставим в запросе только ключевые поля, необходимые для идентификации и первичного быстрого поиска:


ВЫБРАТЬ
    СправочникКнигаЗахоронений.Ссылка КАК Ссылка,
    СправочникКнигаЗахоронений.ПометкаУдаления КАК ПометкаУдаления,
    СправочникКнигаЗахоронений.Год КАК Год,
    СправочникКнигаЗахоронений.Фамилия КАК Фамилия,
    СправочникКнигаЗахоронений.Имя КАК Имя,
    СправочникКнигаЗахоронений.Отчество КАК Отчество,
    СправочникКнигаЗахоронений.Кладбище КАК Кладбище
ИЗ
    Справочник.КнигаЗахоронений КАК СправочникКнигаЗахоронений

Для вывода остальных «тяжелых» полей на форму мы применим метод отложенного чтения через событие ПриПолученииДанныхНаСервере. Этот обработчик срабатывает только для тех строк таблицы, которые в данный момент реально отображаются на экране пользователя (обычно это не более 30–40 строк).

Разберем пример реализации этого механизма в модуле формы списка:


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Получаем массив ссылок только для отображаемых на экране строк
    МассивСсылок = Новый Массив;
    Для Каждого СтрокаСписка Из Строки Цикл
        МассивСсылок.Добавить(СтрокаСписка.Ключ);
    КонецЦикла;
    
    // Запрашиваем "тяжелые" данные только для этих строк
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   КнигаЗахоронений.Ссылка КАК Ссылка,
        |   КнигаЗахоронений.Комментарий КАК Комментарий,
        |   КнигаЗахоронений.АдресПодачиКатафалка КАК АдресПодачиКатафалка,
        |   КнигаЗахоронений.НомерСвидетельства КАК НомерСвидетельства
        |ИЗ
        |   Справочник.КнигаЗахоронений КАК КнигаЗахоронений
        |ГДЕ
        |   КнигаЗахоронений.Ссылка В (&МассивСсылок)";
        
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Заполняем оформление строк полученными данными
    Пока Выборка.Следующий() Цикл
        ТекущаяСтрока = Строки[Выборка.Ссылка];
        ТекущаяСтрока.Данные["Комментарий"] = Выборка.Комментарий;
        ТекущаяСтрока.Данные["АдресПодачиКатафалка"] = Выборка.АдресПодачиКатафалка;
        ТекущаяСтрока.Данные["НомерСвидетельства"] = Выборка.НомерСвидетельства;
    КонецЦикла;
    
КонецПроцедуры

Благодаря этому подходу база данных мгновенно отдает первые 30 записей, а тяжелые строки с адресами и комментариями подтягиваются порциями на лету при прокрутке списка.

Шаг 2. Настройка ключевых полей и свойств динамического списка

Проанализируем свойства самого динамического списка в редакторе формы. Если используется произвольный запрос, платформе 1С бывает тяжело самостоятельно определить уникальный ключ для каждой строки. Это приводит к тому, что система запрашивает из СУБД избыточный объем информации для позиционирования.

Выполним следующие настройки в свойствах динамического списка:

  1. Убедимся, что установлена галочка Динамическое считывание данных.
  2. В поле Ключевые поля (доступно при редактировании произвольного запроса начиная с платформы 8.3.14) принудительно укажем поле Ссылка.
  3. Проверим настройки колонок в схеме компоновки данных динамического списка. У всех неиспользуемых на форме или скрытых по умолчанию реквизитов необходимо снять флаг Использовать всегда. В противном случае платформа будет принудительно запрашивать эти данные из БД при каждом обновлении списка.

Шаг 3. Внедрение полнотекстового поиска (ППД) вместо LIKE

Попытки найти покойника по обрывкам фамилии или адресу с помощью стандартного поиска в динамическом списке неизбежно приводят к зависанию. Для решения этой проблемы мы должны использовать встроенный механизм Полнотекстового поиска данных.

Выясним, как правильно настроить этот механизм:

  1. В конфигураторе для справочника КнигаЗахоронений в свойстве Полнотекстовый поиск установим значение Использовать.
  2. Для всех индексируемых строковых реквизитов (Фамилия, Имя, Отчество, Адрес) также установим использование полнотекстового поиска.
  3. Настроим регулярное обновление и слияние индекса ППД. Для этого в базе данных должны на постоянной основе выполняться регламентные задания ОбновлениеИндексаППД (раз в 5-10 минут) и СлияниеИндексаППД (один раз в сутки).

Если поиск средствами динамического списка все равно работает неэффективно, мы можем спроектировать собственный программный поиск через ППД. Разберем пример реализации, когда пользователь вводит строку поиска, мы программно ищем подходящие ссылки и передаем их в отбор динамического списка:


&НаСервере
Процедура ВыполнитьПоискНаСервере(СтрокаПоиска)
    
    СписокСсылок = Новый Массив;
    
    // Выполняем полнотекстовый поиск по индексу
    СписокППД = ПолнотекстовыйПоиск.СоздатьСписок(СтрокаПоиска, 100);
    СписокППД.ПерваяЧасть();
    
    Для Каждого ЭлементППД Из СписокППД Цикл
        Если ТипЗнч(ЭлементППД.Значение) = Тип("СправочникСсылка.КнигаЗахоронений") Тогда
            СписокСсылок.Добавить(ЭлементППД.Значение);
        КонецЕсли;
    КонецЦикла;
    
    // Если ничего не нашли, передаем пустую ссылку, чтобы очистить список
    Если СписокСсылок.Количество() = 0 Тогда
        СписокСсылок.Добавить(Справочники.КнигаЗахоронений.ПустаяСсылка());
    КонецЕсли;
    
    // Устанавливаем жесткий отбор по массиву найденных ссылок
    ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
    ЭлементОтбора.ПравоеЗначение = СписокСсылок.Использование = Истина;
    
КонецПроцедуры

Такой поиск по индексу ППД на 350 тысячах записей отработает за сотые доли секунды, полностью исключая Full Table Scan базы данных.

Шаг 4. Управление индексами базы данных

Избыточное индексирование вредит производительности. Если у вас проиндексировано 11 полей, файловая СУБД тратит слишком много ресурсов на поддержку их актуальности. Проанализируем типичные сценарии поиска пользователей и оптимизируем индексы:

  1. Одиночные индексы. Оставьте индексирование только для тех полей, по которым пользователи выполняют поиск в первую очередь и с точным совпадением (например, Фамилия или Кладбище).
  2. Составные индексы. Платформа 1С в файловом режиме не поддерживает создание составных индексов вручную (в отличие от MS SQL Server). Однако мы можем пойти на хитрость. Если поиск практически всегда выполняется в разрезе конкретного кладбища (например, "Кладбище" + "Фамилия"), имеет смысл ограничить поиск: сначала принудительно заставить пользователя выбрать Кладбище из списка, и только потом активировать поле ввода Фамилии.
  3. Удаление лишнего. Снимите флаг "Индексировать" со вспомогательных полей (Ряд, Могила, Номер свидетельства, Возраст). Поиск по ним выполняется редко, а индекс забивает оперативную память.

Шаг 5. Оптимизация интерфейса ввода отборов

По умолчанию динамический список пытается обновить данные сразу же при изменении любого символа в полях отбора. Если пользователь медленно вводит длинную фамилию, система делает 5-10 тяжелых запросов к базе данных подряд, что моментально приводит к зависанию сессии.

Разберем, как оптимизировать интерфейс ввода:

  1. Разместите на форме отдельные элементы управления для ввода параметров поиска (поля ввода для Фамилии, Имени, Отчества, Кладбища).
  2. Отключите у динамического списка стандартную панель автоотчетов и стандартную строку поиска.
  3. Для полей ввода установите свойство РежимВыбораИзСписка или производите поиск только при нажатии на специальную кнопку "Найти". Также можно использовать обработчик ожидания, который запускает поиск через 1.5–2 секунды после того, как пользователь закончил ввод текста.

Посмотрим на пример реализации поиска с использованием обработчика ожидания:


&НаКлиенте
Процедура ПолеПоискаФамилияАвтоПодбор(Элемент, Текст, ДанныеВыбора, Параметры, Ожидание, СтандартнаяОбработка)
    
    // Отменяем стандартный автоподбор, чтобы не перегружать базу
    СтандартнаяОбработка = Ложь;
    
    // Подключаем однократный обработчик ожидания
    ПодключитьОбработчикОжидания("ВыполнитьОтложенныйПоиск", 1.5, Истина);
    
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьОтложенныйПоиск()
    
    // Этот код выполнится только после того, как пользователь сделает паузу в вводе
    ПрименитьОтборыНаСервере();
    
КонецПроцедуры

Шаг 6. Настройка операционной системы и обслуживание файловой базы

Поскольку файловая база данных критически зависит от скорости дисковых операций ввода-вывода (I/O), необходимо выполнить комплекс мер по оптимизации окружения:

  1. Исключения антивируса. В обязательном порядке добавьте файл базы данных 1Cv8.1CD и каталог с базой в список исключений вашего антивируса (Windows Defender, Kaspersky и др.). При каждом запросе динамического списка платформа активно читает файл базы данных, а антивирус пытается "на лету" сканировать эти обращения, замедляя чтение в 3-5 раз.
  2. Тестирование и исправление (Сжатие таблиц). В файловой базе при удалении или изменении записей старые данные физически остаются на диске, образуя "дыры" и фрагментируя индексы. Запустите конфигуратор, перейдите в меню Администрирование — Тестирование и исправление, отметьте пункты Реиндексация таблиц информационной базы и Сжатие таблиц информационной базы. Это значительно сократит размер файла БД и упорядочит физическое расположение данных на HDD.
  3. Электропитание. На ПК, где физически расположена база данных, зайдите в настройки электропитания Windows и переключите режим на "Высокая производительность". По умолчанию Windows может парковать головки жесткого диска или снижать частоту процессора при простое, что вызывает секундные задержки при первом обращении к списку.

Применение описанного комплекса мер позволяет сократить время открытия динамического списка на 350 тысячах записей с 30 секунд до мгновенного вывода (менее 1 секунды), а поиск сделать плавным и комфортным для конечных пользователей.

← На главную