Периодические зависания при выполнении привычных рутинных операций — одна из самых коварных проблем при сопровождении высоконагруженных систем 1С. Ситуация, когда поиск по проиндексированному полю регистра сведений отрабатывает мгновенно тысячи раз в день, но раз или два в месяц намертво блокирует работу пользователей, указывает на комплексную проблему на стыке кода конфигурации и механизмов оптимизации СУБД. В такой ситуации стандартный журнал регистрации часто оказывается пустым, а фоновые задания не показывают явных аномалий. Разберем по шагам, почему возникают такие плавающие сбои, как точно локализовать виновника и какие превентивные меры необходимо принять.
Проанализируем ключевые факторы, из-за которых стабильно работающий запрос на чтение данных из регистра сведений может внезапно зависать:
Index Seek) выбирает полное сканирование таблицы (Index Scan или Table Scan). Запрос начинает считывать миллионы строк с диска, вызывая длительный «висяк».Строка(13)), а в поисковом запросе передается строка переменной длины без дополнения пробелами, СУБД может неявно вызвать скалярную функцию усечения или преобразования к каждому значению в таблице. Это полностью исключает использование индекса. Аналогичный эффект вызывает неосторожное использование условия ПОДОБНО '%...'.Выбрать(...) с отбором вместо прямого оптимизированного запроса: платформа может считывать избыточный диапазон записей и нагружать оперативную память.Рассмотрим первоочередной шаг, который решает проблему устаревания планов запросов. Если статистика по индексам не обновляется вовремя, СУБД «слепнет» и строит катастрофически неверные планы. Настроим регулярные задачи обслуживания на сервере баз данных:
sp_updatestats / UPDATE STATISTICS с достаточным процентом сканирования в MS SQL Server или ANALYZE в PostgreSQL). Актуальная статистика гарантирует, что оптимизатор выберет поиск по индексу для точечных запросов.Rebuild Index) для таблиц с фрагментацией выше 30% и реорганизацию (Reorganize Index) при фрагментации от 10% до 30%.Выясним, как программно отследить, что именно «подгружает» регистр в момент подвисания, если обычный журнал регистрации ничего не показывает. Для этого настроим сбор событий Технологического журнала 1С.
Создадим файл конфигурации logcfg.xml с фильтром по длительным событиям (например, свыше 10 секунд, что соответствует значению duration > 10000000 в микросекундах):
TLOCKS и TTIMEOUT — зафиксируют факт ожидания на управляемых блокировках 1С, покажут длительность простоя и конкретное фоновое или пользовательское соединение, удерживающее ресурс.DBMSSQL или DBPOSTGRS — сохранят фактический SQL-текст запроса, поступившего в СУБД, физическое имя таблицы, время выполнения и план запроса.Context — выведет точную строчку кода модуля 1С, на которой система зависла в ожидании данных.Параллельно проанализируем состояние на стороне СУБД в момент зависания. В MS SQL Server для этого используем системные представления sys.dm_os_waiting_tasks и sys.dm_exec_requests. Если в колонке типа ожидания фиксируются значения LCK_* — мы имеем дело с эскалацией блокировок от параллельной записи. Если преобладают ожидания PAGEIOLATCH_* или CXPACKET — СУБД выполняет «тяжелое» сканирование таблицы из-за сбившегося плана запроса.
Разберем правильную реализацию поиска по штрихкоду. Убедимся, что поиск не использует объектную выборку через менеджер регистра, а выполняется легковесным запросом с жестким ограничением выборки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Штрихкоды.Номенклатура КАК Номенклатура,
| Штрихкоды.Характеристика КАК Характеристика
|ИЗ
| РегистрСведений.Штрихкоды КАК Штрихкоды
|ГДЕ
| Штрихкоды.Штрихкод = &Штрихкод";
Запрос.УстановитьПараметр("Штрихкод", СокрЛП(ИскомыйШтрихкод));
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
// Обработка полученных данных
КонецЕсли;
Обратим внимание на следующие критически важные правила в коде:
ВЫБРАТЬ ПЕРВЫЕ 1 позволяет СУБД прервать сканирование индекса сразу после обнаружения первого совпадения, не перебирая оставшиеся записи таблицы.СокрЛП() исключает передачу случайных пробелов и предотвращает неявное приведение типов при сравнении строковых полей.Если анализ Технологического журнала показал, что зависания вызваны блокировками от фоновых пакетных загрузок в этот же регистр, рассмотрим точечную корректировку на уровне базы данных:
ПолучитьСтруктуруХраненияБазыДанных() определяем физическое имя таблицы регистра (например, _InfoRg12345) и выполняем команду:
ALTER TABLE dbo._InfoRg12345 SET (LOCK_ESCALATION = DISABLE);
ЗначенияОтбора = Новый Структура("Штрихкод", СокрЛП(ИскомыйШтрихкод));
ДанныеЗаписи = РегистрыСведений.Штрихкоды.Получить(ЗначенияОтбора);