При работе в клиент-серверном варианте платформы 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда одна и та же обработка на разных рабочих местах показывает колоссальную разницу в скорости выполнения. В нашей практике мы разберем реальный случай из отраслевого решения «АК-Кредит» (используется микрофинансовыми организациями для автоматизации требований 115-ФЗ). Задача обработки — сопоставить список из 52 000 клиентов компании с базой террористов Росфинмониторинга.
Ситуация выглядит следующим образом: на одном компьютере документ заполняется за 20–30 минут, а на другом — этот же процесс занимает более 4 часов. При этом оба пользователя работают в тонком клиенте под полными правами, а сервер приложений 1С и сервер СУБД (SQL) разнесены на разные физические машины. Проанализируем ситуацию, выясним причины столь сильного расхождения в производительности и разберем по шагам, как оптимизировать алгоритм, чтобы ускорить его работу до нескольких секунд.
Разница в скорости выполнения одной и той же серверной процедуры в 12 раз (30 минут против 4 часов) при идентичных правах пользователей не может быть вызвана только мощностью процессора клиентского компьютера. Так как код выполняется на сервере, основные причины кроются в инфраструктурных особенностях и механизме взаимодействия «Клиент - Сервер 1С - СУБД»:
ping -l 65500 IP_Адрес_Сервера_1С. Если наблюдаются потери пакетов или скачки времени отклика, проблему нужно решать на уровне сетевого оборудования.Рассмотрим оригинальный код фоновой проверки, предоставленный разработчиком. В нем допущено сразу несколько грубейших архитектурных ошибок сопоставления данных, которые порождают лавинообразное падение скорости:
Для Каждого ТекЛицо Из Проверяемые Цикл
Заметки = "";
// 1. Поиск в неиндексированной таблице значений в памяти
Поиск = Террористы.НайтиСтроки(Новый Структура("ФИО", ВРЕГ(СокрЛП(ТекЛицо.Наименование))));
// 2. Запрос в цикле по паспортам
Если СтрДлина(Серия) > 0 И СтрДлина(Номер) > 0 Тогда
СтрокаПоиска = "%" + Лев(Серия, 2) + Прав(Серия, 2) + "," + СокрЛП(Номер) + "%";
ЗапросПаспорта.Параметры.Вставить("Паспорт", СтрокаПоиска);
Поиск = ЗапросПаспорта.Выполнить().Выгрузить(); // Убийца производительности!
КонецЕсли;
// 3. Запрос в цикле по адресам
СтрокаПоиска = ПолучитьСтрокуПоискаПоАдресу(ЗначенияАдреса);
ЗапросАдреса.УстановитьПараметр("Адрес", СтрокаПоиска);
Поиск = ЗапросАдреса.Выполнить().Выгрузить(); // Еще один запрос в цикле!
КонецЦикла;
Проанализируем этот алгоритм подробнее:
НайтиСтроки() выполняется перебором. На 52 000 итераций цикла процессор сервера 1С совершает миллиарды операций сравнения строк, так как таблица значений не проиндексирована.Для того чтобы сократить время работы обработки с нескольких часов до 10–15 секунд, необходимо кардинально изменить логику сопоставления. Мы должны заставить SQL-сервер сделать всю работу за один пакетный запрос, используя механизмы временных таблиц и соединений.
Вместо обхода коллекции в цикле, мы подготовим пакетный запрос. Сначала соберем все данные о проверяемых лицах, их паспортах и адресах во временную таблицу. Затем свяжем ее левым соединением с базой террористов. Посмотрим на пример реализации такого подхода:
// Создаем менеджер временных таблиц для работы с пакетом
МВТ = Новый МенеджерВременныхТаблиц;
ЗапросОптимизированный = Новый Запрос;
ЗапросОптимизированный.МенеджерВременныхТаблиц = МВТ;
// Шаг 1.1. Выгружаем нормализованные данные проверяемых во временную таблицу
ЗапросОптимизированный.Текст =
"ВЫБРАТЬ
| ФизЛица.Ссылка КАК ЮрФизЛицо,
| ВРЕГ(ПОДСТРОКА(ФизЛица.Наименование, 1, 150)) КАК ФИО_Норм,
| ФизЛица.ДатаРождения КАК ДатаРождения,
| ЕСТЬNULL(Паспорта.Серия, """") КАК Серия,
| ЕСТЬNULL(Паспорта.Номер, """") КАК Номер
|ПОМЕСТИТЬ ВТ_Проверяемые
|ИЗ
| Справочник.ФизическиеЛица КАК ФизЛица
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДокументыФизическихЛиц.СрезПоследних КАК Паспорта
| ПО Паспорта.Физлицо = ФизЛица.Ссылка И Паспорта.ЯвляетсяДокументомУдостоверяющимЛичность
|ГДЕ
| НЕ ФизЛица.ЭтоГруппа И НЕ ФизЛица.ПометкаУдаления
|;
|
|////////////////////////////////////////////////////////////////////////////////
|// Шаг 1.2. Выполняем пакетное сопоставление одним запросом к СУБД
|ВЫБРАТЬ
| Проверяемые.ЮрФизЛицо,
| Проверяемые.ФИО_Норм,
| Террористы.ФИО КАК Террорист_ФИО,
| ВЫБОР
| КОГДА Проверяемые.ФИО_Норм = ВРЕГ(Террористы.ФИО) И Проверяемые.ДатаРождения = Террористы.ДатаРождения
| ТОГДА ""Совпадение по ФИО и Дате Рождения""
| КОГДА Проверяемые.Серия = Террористы.ПаспортСерия И Проверяемые.Номер = Террористы.ПаспортНомер
| ТОГДА ""Совпадение по серии и номеру паспорта""
| ИНАЧЕ """"
| КОНЕЦ КАК РезультатПроверки
|ИЗ
| ВТ_Проверяемые КАК Проверяемые
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.БазаТеррористов КАК Террористы
| ПО Проверяемые.ФИО_Норм = ВРЕГ(Террористы.ФИО)
| ИЛИ (Проверяемые.Номер = Террористы.ПаспортНомер И Проверяемые.Номер <> """")";
РезультатСвязывания = ЗапросОптимизированный.Выполнить().Выгрузить();
Важное примечание: Чтобы сопоставление по паспортам работало мгновенно, предварительно нормализуйте данные в регистре БазаТеррористов при их загрузке из внешнего файла. Разделите поле «Паспорт» на отдельные колонки ПаспортСерия и ПаспортНомер без пробелов и спецсимволов. Это позволит СУБД использовать стандартные индексы B-Tree и выполнять поиск за миллисекунды.
Если по условиям бизнес-логики часть проверок необходимо выполнять непосредственно в коде на сервере 1С, обязательно используйте индексацию коллекций в памяти. Начиная с версии платформы 8.3.13, Таблицы Значений поддерживают быстрое индексирование:
// Перед началом сопоставления добавляем индекс по ключевому полю поиска
Террористы.Индексы.Добавить("ФИО");
Для Каждого ТекЛицо Из Проверяемые Цикл
// Теперь НайтиСтроки() работает по хэш-таблице мгновенно
Поиск = Террористы.НайтиСтроки(Новый Структура("ФИО", ТекЛицо.ФИО_Норм));
// ... дальнейшая постобработка
КонецЦикла;
Передача таблицы на 52 000 строк из памяти сервера в интерфейс управляемой формы тонкого клиента — тяжелая операция для рендеринга. Чтобы разгрузить клиентский компьютер:
ФоновыеЗадания.Выполнить().РезультатыПроверкиПОДФТ).Применяя данные подходы, мы устраняем влияние сетевых задержек между серверами СУБД и 1С, полностью нивелируем разницу в производительности компьютеров пользователей и переводим тяжелые алгоритмы обработки данных на рельсы высокопроизводительной работы с СУБД.