Почему медленно записывается периодический регистр сведений со срезом последних и как это исправить?

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

При внедрении и активной эксплуатации крупных информационных систем на базе 1С:Предприятие часто возникает неприятная ситуация: документы, выполняющие запись в подчиненные периодические регистры сведений, начинают проводить транзакции крайне медленно. Анализ через технологический журнал или MS SQL Profiler показывает, что основное время тратится на операцию вставки или обновления временных таблиц и физических таблиц итогов, а СУБД выполняет операцию Table Scan (полное сканирование таблицы) там, где ожидался быстрый поиск по индексу. Проанализируем ситуацию, выясним причину такого поведения и рассмотрим подробнее все возможные варианты решения этой проблемы.

В чем суть проблемы и как устроены таблицы срезов под капотом

Когда мы в конфигураторе 1С взводим у периодического регистра сведений свойство «Разрешить итоги: Срез последних» (или «Срез первых»), платформа 1С автоматически создает в базе данных SQL дополнительную физическую таблицу. Для таблицы среза последних она носит имя _InfoRgSL<n>, а для среза первых — _InfoRgSF<n> (где <n> — порядковый номер таблицы в СУБД).

В этой таблице хранятся актуальные значения ресурсов в разрезе уникального набора измерений на текущий момент времени. При записи набора записей в основной регистр платформа в рамках той же транзакции выполняет обновление данных в этой сопутствующей таблице. Однако на версиях платформы 1С:Предприятие 8.3.12 и ниже существовала серьезная архитектурная недоработка: СУБД хранила таблицу _InfoRgSL как «кучу» (Heap) — структуру данных без кластерного индекса.

Это приводило к следующим последствиям:

  1. Даже если программист устанавливал свойство «Индексировать» у измерений в конфигураторе, этот индекс создавался только для основной таблицы регистра _InfoRg<n>, но полностью игнорировался для таблицы срезов.
  2. При записи новых данных СУБД была вынуждена делать полное сканирование таблицы (Table Scan), так как некластерный индекс не содержал всех необходимых системных полей (таких как _Period, _RecorderTRef, _RecorderRRef).
  3. С ростом объема данных в регистре время выполнения простейшей записи росло лавинообразно, генерируя сотни тысяч чтений (logical reads) на ровном месте.

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

Решение 1. Переход на платформу 8.3.13 и выше (штатный путь)

Начиная с релиза платформы 8.3.13.1644, разработчики фирмы «1С» устранили эту ошибку. Теперь для физических таблиц срезов первых и последних автоматически выстраивается полноценный кластерный индекс по измерениям.

Однако простого обновления платформы недостаточно, чтобы исправить структуру базы данных на SQL-сервере. Нам необходимо принудительно запустить реструктуризацию таблиц. Выполним это одним из следующих способов:

  1. Запустите конфигуратор, откройте меню «Администрирование» — «Тестирование и исправление». Установите флаг «Реиндексация таблиц информационной базы», а также выберите пункт «Ресчет итогов». Запустите выполнение процедуры.
  2. Если база данных слишком большая и полное тестирование займет много времени, можно запустить реструктуризацию только для конкретного проблемного регистра. Для этого достаточно внести минимальное изменение в структуру регистра в конфигураторе (например, временно добавить новый реквизит с типом Булево), обновить конфигурацию базы данных (нажав клавишу F7), а затем удалить этот реквизит и снова обновить конфигурацию. Платформа принудительно пересоздаст таблицы регистра в СУБД, добавив долгожданные кластерные индексы.

Решение 2. Отключение физических таблиц среза последних

Если проект по каким-то причинам жестко привязан к старым версиям платформы (например, 8.3.12) и обновиться в ближайшее время невозможно, самым эффективным решением будет отказаться от хранения физических таблиц среза.

Для этого выполните следующие действия:

  1. В конфигураторе найдите ваш регистр сведений.
  2. Перейдите на вкладку «Данные» (или свойства регистра) и снимите галочку «Разрешить итоги: Срез последних».
  3. Обновите конфигурацию базы данных.

Что изменится? Физическая таблица _InfoRgSL будет полностью удалена из базы данных SQL. Запись в регистр станет выполняться мгновенно, так как платформе больше не нужно параллельно обновлять "кучу" и тратить ресурсы на её сканирование.

При этом виртуальная таблица СрезПоследних по-прежнему останется доступной в запросах 1С! Однако теперь платформа будет получать эти данные динамически — путем построения подзапроса к основной таблице регистра _InfoRg<n> с использованием конструкции MAX(Период) и группировки по измерениям. На больших объемах данных такое динамическое получение среза через кластерный индекс основной таблицы работает в десятки раз быстрее, чем обращение к неиндексированной таблице срезов.

Решение 3. Обход использования таблицы срезов в запросах через смещение даты

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

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


// Вместо этого (запрос обратится к медленной таблице итогов):
ГраницаПериода = Новый Граница(ТекущаяДата(), ГраницаТипа.Включая);

// Используем смещение на 1 секунду (запрос обратится к индексам основной таблицы):
ГраницаПериода = Новый Граница(ТекущаяДата() - 1, ГраницаТипа.Включая);

Решение 4. Ручное создание кластерного индекса на уровне СУБД (Нештатный путь)

Посмотрим на пример решения проблемы напрямую через MS SQL Server Management Studio. Этот метод часто используется системными администраторами и экспертами по технологическим вопросам для быстрого устранения "затыка" в работе СУБД без изменения конфигурации 1С.

Важное предупреждение: Данное действие формально нарушает лицензионное соглашение фирмы «1С». Кроме того, при любой последующей реструктуризации базы данных (при обновлении конфигурации или тестировании) платформа удалит созданный вами индекс, и его придется создавать заново.

Если вы осознаете риски и решили применить этот способ, выполните следующий T-SQL скрипт в вашей базе данных (заменив имя таблицы _InfoRgSL8427 на имя таблицы вашего регистра):


-- Создаем кластерный индекс по измерениям регистра для ускорения поиска и обновления данных
CREATE CLUSTERED INDEX CX_InfoRgSL8427_Dimensions ON dbo._InfoRgSL8427 
(
    _Fld281 ASC,
    _Fld8417RRef ASC,
    _Fld8418RRef ASC,
    _Fld8419RRef ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY];

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

Решение 5. Отказ от виртуальной таблицы в пользу ручного запроса

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

Рассмотрим пример такого запроса:


ВЫБРАТЬ
    РегистрМаксимумы.Измерение1 КАК Измерение1,
    МАКСИМУМ(РегистрМаксимумы.Период) КАК Период
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    РегистрСведений.ИмяРегистра КАК РегистрМаксимумы
ГДЕ
    РегистрМаксимумы.Измерение1 В (&СписокИзмерений)
СГРУППИРОВАТЬ ПО
    РегистрМаксимумы.Измерение1
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    РегистрДанные.Период КАК Период,
    РегистрДанные.Измерение1 КАК Измерение1,
    РегистрДанные.Ресурс1 КАК Ресурс1
ИЗ
    ВТ_МаксимальныеДаты КАК МаксДаты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИмяРегистра КАК РегистрДанные
        ПО МаксДаты.Измерение1 = РегистрДанные.Измерение1
        И МаксДаты.Период = РегистрДанные.Период

Этот код гарантированно задействует штатные кластерные индексы основной таблицы регистра сведений _InfoRg<n>, обеспечивая стабильную и высокую скорость выполнения операции как на чтение, так и на запись.

← На главную