При масштабном обновлении конфигураций с большим объемом учетных данных (например, переход с редакции 2.0 на 3.0 для базы размером в сотни гигабайт и десятками миллионов записей субконто) процесс реструктуризации таблиц базы данных в MS SQL Server может затягиваться на несколько суток. Выясним причину такого поведения и разберем по шагам, как локализовать и устранить зависание.
Проанализируем ситуацию, возникающую в процессе реорганизации таблиц. Во время реструктуризации платформа 1С создает временные таблицы с суффиксом *NG (New Generation) и выполняет проверку логической и ссылочной целостности данных. В частности, для регистра бухгалтерии выполняется проверка отсутствия «осиротевших» записей в таблице значений субконто (_AccRgED...NG), для которых отсутствует родительская запись в основной таблице движений (_AccRg...NG).
Посмотрим на пример типичного запроса проверки целостности, формируемого платформой:
SELECT TOP 1
T1._RecorderTRef,
T1._RecorderRRef
FROM dbo._AccRgED479NG T1 WITH(NOLOCK)
LEFT OUTER JOIN dbo._AccRg443NG T2 WITH(NOLOCK)
ON T1._RecorderTRef = T2._RecorderTRef
AND T1._RecorderRRef = T2._RecorderRRef
AND T1._LineNo = T2._LineNo
WHERE T2._RecorderTRef IS NULL
AND T2._RecorderRRef IS NULL
Рассмотрим подробнее, что происходит в оптимизаторе MS SQL Server:
SELECT TOP 1 вводит оптимизатор запросов СУБД в заблуждение: предполагается, что несовпадающая строка будет обнаружена практически сразу на первых итерациях.*NG оптимизатор выбирает физический оператор Nested Loops (вложенные циклы) вместо Hash Join или Merge Join.Index Scan по 46 миллионам строк), и для каждой строки инициируется сканирование таблицы движений (Index Scan по 10 миллионам строк).Проанализируем программные изменения в платформе 1С:Предприятие. В ранних релизах ветки 8.3.10 данный алгоритм генерации запросов валидации содержал архитектурные недоработки. В версиях 8.3.11 и выше разработчики платформы существенно переработали алгоритмы обновления и внедрили механизм оптимизированной серверной реструктуризации.
ALTER TABLE без полного переливания данных во временные таблицы *NG, что экономит часы работы на больших базах.Если обновление платформы в данный момент невозможно, а процесс уже запущен и завис, мы можем принудительно заставить оптимизатор СУБД сменить тип соединения таблиц. Разберем по шагам создание структуры плана запроса (Plan Guide):
sp_create_plan_guide:
EXEC sp_create_plan_guide
@name = N'Fix_AccRg_Restructure_Join',
@stmt = N'SELECT TOP 1 T1._RecorderTRef, T1._RecorderRRef FROM dbo._AccRgED479NG T1 WITH(NOLOCK) LEFT OUTER JOIN dbo._AccRg443NG T2 WITH(NOLOCK) ON T1._RecorderTRef = T2._RecorderTRef AND T1._RecorderRRef = T2._RecorderRRef AND T1._LineNo = T2._LineNo WHERE T2._RecorderTRef IS NULL AND T2._RecorderRRef IS NULL',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (HASH JOIN, MERGE JOIN)';
Если запрос уже выполняется в режиме Nested Loops, узким местом является операция Index Scan во внутреннем цикле. Мы можем ускорить работу СУБД «на лету» без прерывания транзакции:
*NG).
CREATE NONCLUSTERED INDEX IX_Temp_FastJoin
ON dbo._AccRg443NG (_RecorderTRef, _RecorderRRef, _LineNo);
Index Scan) в точечный поиск (Index Seek), что многократно ускоряет выполнение цикла.Рассмотрим комплекс профилактических мер, которые необходимо выполнить до запуска масштабного обновления:
SIMPLE (Простая) на время обновления, чтобы избежать лавинообразного роста журнала транзакций LDF.tempdb. Разместим файлы tempdb на скоростных NVMe-накопителях и создадим несколько файлов данных, соответствующих количеству физических ядер сервера.