Реорганизация индексов базы данных MS SQL "Невозможно реорганизовать индекс"
Приветствую вас заклинатели железа, направляю вам преинтереснейшую ситуёвину для разбора и помощи вашему слуге.
Проблема: После обновления платформы 8.3.20.1674->8.3.22.1603 сломалось автоматическое резервное копирование баз данных, созданное менеджером в SSMS 2012. При попытке реиндексации баз данных коих у нас не одна, ssms ругается на "Невозможно реорганизовать индекс "_Acc48_1" в таблице "_Acc48", поскольку отключена блокировка на уровне страницы." И так на 30к+ индексов. Во всех базах где произошло какое-то обновление/регламентное задание/бог его знает что ещё. В кратце, для реиндексации нужно чтобы все таблицы баз были с включенным параметром "allow_page_locks" или как описан в клиенте он "Блокировка страниц в памяти".
Что пробовал?:
· Гуглил. Не помогло.
· Доставать dt сувать обратно, индексы логично достаются и суются вместе с базой. Не помогло.
· Поднимал бэкап до обновления платформы в тест базу, смотрел что в индексах с нашим параметром. Там везде "ON". У нас после обновы в 30к "OFF".
· В небольших базах было по 1-2 индекса с флагом офф, проставлял там руками параметр в истину, запускал план бэкапа, реиндексация и сам бэкап проходили на ура. Сделал вывод, что выставив как-то параметр в 30к индексах я таки получу автобэкапирование, но не пойму причины возникновения отключения параметра. Можно утолкать кусок скрипта перед реиндексацией который будет ставить параметр в "ON". Костыль....
· Пробовал делать тестирование и исправление ошибок через конфигуратор. Не помогло.
Люди добрые:
Кто сталкивался с подобным? Подскажите как решали.
Кто сможет написать запрос который в автомате проставит флаг во всех индексах всех таблиц базы, буду очень признателен.
Конструкция не помогает, так как во втором параметре нужна таблица, как туда указать все таблицы, не знаю, не могу, не умею.
Проблема: После обновления платформы 8.3.20.1674->8.3.22.1603 сломалось автоматическое резервное копирование баз данных, созданное менеджером в SSMS 2012. При попытке реиндексации баз данных коих у нас не одна, ssms ругается на "Невозможно реорганизовать индекс "_Acc48_1" в таблице "_Acc48", поскольку отключена блокировка на уровне страницы." И так на 30к+ индексов. Во всех базах где произошло какое-то обновление/регламентное задание/бог его знает что ещё. В кратце, для реиндексации нужно чтобы все таблицы баз были с включенным параметром "allow_page_locks" или как описан в клиенте он "Блокировка страниц в памяти".
Что пробовал?:
· Гуглил. Не помогло.
· Доставать dt сувать обратно, индексы логично достаются и суются вместе с базой. Не помогло.
· Поднимал бэкап до обновления платформы в тест базу, смотрел что в индексах с нашим параметром. Там везде "ON". У нас после обновы в 30к "OFF".
· В небольших базах было по 1-2 индекса с флагом офф, проставлял там руками параметр в истину, запускал план бэкапа, реиндексация и сам бэкап проходили на ура. Сделал вывод, что выставив как-то параметр в 30к индексах я таки получу автобэкапирование, но не пойму причины возникновения отключения параметра. Можно утолкать кусок скрипта перед реиндексацией который будет ставить параметр в "ON". Костыль....
· Пробовал делать тестирование и исправление ошибок через конфигуратор. Не помогло.
Люди добрые:
Кто сталкивался с подобным? Подскажите как решали.
Кто сможет написать запрос который в автомате проставит флаг во всех индексах всех таблиц базы, буду очень признателен.
Конструкция
ALT ER INDEX ALL ON <Table name>
SET (ALLOW_PAGE_LOCKS = ON)
GO
Найденные решения
(1)
Проверьте такой вариант, имя таблицы автоматом подставляется вместо знака вопроса.
как туда указать все таблицы, не знаю
USE имя базы
EXEC sp_MSforeachtable 'ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = ON)'
GO
Проверьте такой вариант, имя таблицы автоматом подставляется вместо знака вопроса.
Остальные ответы
В избранное
Подписаться на ответы
Сортировка:
Древо развёрнутое
Свернуть все
Подскажите, пожалуйста.
Поставили платформу 8.3.22.ххх
По некоторым причинам пришлось сразу же откатиться на платформу 8.3.18
И там столкнолись с ошибкой при реиндексации. Судя по всему, реструктуризация БД не учла изменения в БД внесенные платформой 22.
Описаный здесь скрипт помог.
Но у меня вопрос.
Надо ли мне использовать после реиндексации второй скрипт для выключения блокировок?
Ведь сейчас у меня платформа 18, а не 22. А 1с внесла изменения в блокировки только на 22й платформе.
Поставили платформу 8.3.22.ххх
По некоторым причинам пришлось сразу же откатиться на платформу 8.3.18
И там столкнолись с ошибкой при реиндексации. Судя по всему, реструктуризация БД не учла изменения в БД внесенные платформой 22.
Описаный здесь скрипт помог.
Но у меня вопрос.
Надо ли мне использовать после реиндексации второй скрипт для выключения блокировок?
Ведь сейчас у меня платформа 18, а не 22. А 1с внесла изменения в блокировки только на 22й платформе.
Вот правильная конструкция, которая работает у меня:
До реорганизации индекса:
И после:
Всё отрабатывает отлично.
До реорганизации индекса:
DECLARE @dbname nvarchar(128)
DECLARE @sql nvarchar(max)
DECLARE db_cursor CURSOR FOR
SEL ECT name FR OM sys.databases
WHERE name NOT IN ('master','tempdb','model','msdb')
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @dbname
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @dbname
SET @sql = 'USE [' + @dbname + '];' +
'EXEC sp_MSforeachtable ''ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = ON)'';' +
'EXEC sp_MSforeachtable ''ALT ER INDEX ALL ON ? SET (ALLOW_ROW_LOCKS = ON)'''
EXEC (@sql)
PRINT 'Using database: ' + @dbname
FETCH NEXT FROM db_cursor INTO @dbname
END
CLOSE db_cursor
DEALLOCATE db_cursor
ПоказатьИ после:
DECLARE @dbname nvarchar(128)
DECLARE @sql nvarchar(max)
DECLARE db_cursor CURSOR FOR
SEL ECT name FROM sys.databases
WH ERE name NOT IN ('master','tempdb','model','msdb')
OPEN db_cursor
FETCH NEXT FR OM db_cursor INTO @dbname
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @dbname
SET @sql = 'USE [' + @dbname + '];' +
'EXEC sp_MSforeachtable ''ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = OFF)'';' +
'EXEC sp_MSforeachtable ''ALT ER INDEX ALL ON ? SET (ALLOW_ROW_LOCKS = OFF)'''
EXEC (@sql)
PRINT 'Using database: ' + @dbname
FETCH NEXT FR OM db_cursor INTO @dbname
END
CLOSE db_cursor
DEALLOCATE db_cursor
ПоказатьВсё отрабатывает отлично.
(103)
А Вам не кажется, что в скрипте После должно быть так: "ALLOW_PAGE_LOCKS = OFF" и "ALLOW_ROW_LOCKS = ON"?
На ИТС такая информация по этому вопросу:
Важно! Начиная с версии платформы 8.3.22 необходимо выполнять дефрагментацию индексов по следующему алгоритму:
До дефрагментации индекса необходимо включить страничные блокировки. Пример команды: ALT ER INDEX index_name ON table_name SET (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON);
Выполнить дефрагментацию.
Обратно выключить страничные блокировки. Пример команды: ALT ER INDEX index_name ON table_name SET (ALLOW_PAGE_LOCKS = OFF, ALLOW_ROW_LOCKS = ON);
А Вам не кажется, что в скрипте После должно быть так: "ALLOW_PAGE_LOCKS = OFF" и "ALLOW_ROW_LOCKS = ON"?
На ИТС такая информация по этому вопросу:
Важно! Начиная с версии платформы 8.3.22 необходимо выполнять дефрагментацию индексов по следующему алгоритму:
До дефрагментации индекса необходимо включить страничные блокировки. Пример команды: ALT ER INDEX index_name ON table_name SET (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON);
Выполнить дефрагментацию.
Обратно выключить страничные блокировки. Пример команды: ALT ER INDEX index_name ON table_name SET (ALLOW_PAGE_LOCKS = OFF, ALLOW_ROW_LOCKS = ON);
Апдейт - чтобы пропускало базы со статусом "Вне сети" добавьте AND state = 0 в конструкцию WHERE name NOT IN ('master','tempdb','model','msdb'), вот так вот:
WHERE name NOT IN ('master','tempdb','model','msdb') AND state = 0
Кто-нибудь тестил отключение-включение страничных блокировок сколько времени занимает? Если допустим терабайтная база на сервере крутится или 30-40 баз поменьше... Какую нагрузку включение-отключение создает на сервер?
З.Ы. Сам не проверял, возможно там доли секунды и никаких подводных камней, но вдруг..
З.Ы. Сам не проверял, возможно там доли секунды и никаких подводных камней, но вдруг..
На сколько вообще корректно использовать:
ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = ON)
ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = OFF)
Если посмотреть индексы до этих манипуляций, то есть индексы у которых изначально ALLOW_PAGE_LOCKS = ON и этими действиями по итогу изменяем их на ALLOW_PAGE_LOCKS = OFF. например:
ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = ON)
ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = OFF)
Если посмотреть индексы до этих манипуляций, то есть индексы у которых изначально ALLOW_PAGE_LOCKS = ON и этими действиями по итогу изменяем их на ALLOW_PAGE_LOCKS = OFF. например:
Прикрепленные файлы:
Дай Вам Бог всем здоровья, и долгих лет жизни!
Тоже столкнулся, платформа v8.3.22.1750 + MS SQL 2012
Всё было настроено и всё работало ... но потом ... но потом перестало ))
В результате добавив в план обслуживания шаги на нужных этапах, всё заработало снова!
Единственной, там выше уточняли, что запросе изначально была ошибка ALTER было написано через пробел, поэтому запрос не отрабатывал
Тоже столкнулся, платформа v8.3.22.1750 + MS SQL 2012
Всё было настроено и всё работало ... но потом ... но потом перестало ))
В результате добавив в план обслуживания шаги на нужных этапах, всё заработало снова!
Единственной, там выше уточняли, что запросе изначально была ошибка ALTER было написано через пробел, поэтому запрос не отрабатывал
USE ERP
EXEC sp_MSforeachtable 'ALT ER INDEX ALL ON ? SET (ALLOW_PAGE_LOCKS = OFF)'
EXEC sp_MSforeachtable 'ALT ER INDEX ALL ON ? SET (ALLOW_ROW_LOCKS= ON)'
GO
Прикрепленные файлы:
Использую такой код для обслуживания индексов в MS SQL 2016:
DECLARE @Base_Name nvarchar(100);
DECLARE @Base_id int;
-- Обрабатываются все пользовательские базы данных в оперативном режиме
DECLARE Bases CURSOR FOR SEL ECT database_id FR OM sys.databases where database_id > 4 and state_desc = 'ONLINE';
-- Обрабатываются только нужные пользовательские базы данных в оперативном режиме
-- DECLARE Bases CURSOR FOR SEL ECT database_id FR OM sys.databases where database_id in (5,6,7,8) and state_desc = 'ONLINE';
OPEN Bases;
FETCH NEXT FR OM Bases INTO @Base_id;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Отбор таблиц и индексов с помощью системного представления sys.dm_db_index_physical_stats
-- Отбор только тех объектов которые являются индексами (index_id > 0),
-- фрагментация которых более 10% и количество страниц в индексе более 128
Sel ect @Base_Name = DB_NAME(@Base_id)
PRINT @base_Name;
Exec ('Use ' + @base_Name + '
SET NOCOUNT ON; -- отключаем вывод количества возвращаемых строк, это несколько ускорит обработку
DECLARE @objectid int; -- ID объекта
DECLARE @indexid int; -- ID индекса
DECLARE @partitioncount bigint; -- количество секций если индекс секционирован
DECLARE @schemaname nvarchar(130); -- имя схемы в которой находится таблица
DECLARE @objectname nvarchar(130); -- имя таблицы
DECLARE @indexname nvarchar(130); -- имя индекса
DECLARE @partitionnum bigint; -- номер секции
DECLARE @frag float; -- процент фрагментации индекса
DECLARE @allow_page_locks int; -- признак включенности блокировки на уровне страниц
DECLARE @command nvarchar(4000); -- инструкция T-SQL для дефрагментации либо ренидексации
SEL ECT
IndexStats.object_id AS objectid,
IndexStats.index_id AS indexid,
IndexStats.partition_number AS partitionnum,
IndexStats.avg_fragmentation_in_percent AS frag,
AllIndexes.allow_page_locks as allow_page_locks
INTO #work_to_do
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, ''LIMITED'') AS IndexStats
INNER JOIN sys.indexes AS AllIndexes
ON AllIndexes.index_id = IndexStats.index_id
AND AllIndexes.object_id = IndexStats.object_id
AND IndexStats.avg_fragmentation_in_percent > 10.0
AND IndexStats.index_id > 0
AND IndexStats.page_count > 128;
-- Объявление курсора для чтения секций
DECLARE partitions CURSOR FOR SEL ECT * FR OM #work_to_do;
-- Открытие курсора
OPEN partitions;
-- Цикл по секциям
WHILE (1=1)
BEGIN;
FETCH NEXT
FROM partitions
INTO @objectid, @indexid, @partitionnum, @frag, @allow_page_locks;
IF @@FETCH_STATUS < 0 BREAK;
-- Собираем имена объектов по ID
SELECT @objectname = QUOTENAME(o.name), @schemaname = QUOTENAME(s.name)
FR OM sys.objects AS o
JOIN sys.schemas as s ON s.schema_id = o.schema_id
WH ERE o.object_id = @objectid;
SELECT @indexname = QUOTENAME(name)
FR OM sys.indexes
WH ERE object_id = @objectid AND index_id = @indexid;
SELECT @partitioncount = count (*)
FR OM sys.partitions
WH ERE object_id = @objectid AND index_id = @indexid;
-- Если фрагментация менее или равна 30% тогда дефрагментация, иначе реиндексация
IF @frag <= 30.0
SET @command = N''ALT ER INDEX '' + @indexname + N'' ON '' + @schemaname + N''.'' + @objectname + N'' REORGANIZE'';
IF @frag > 30.0
SET @command = N''ALT ER INDEX '' + @indexname + N'' ON '' + @schemaname + N''.'' + @objectname + N'' REBUILD'';
IF @partitioncount > 1
SET @command = @command + N'' PARTITION='' + CAST(@partitionnum AS nvarchar(10));
-- Если реиндексация, то для ускорения добавляем параметры использования TEMPDB(имеет смысл только если TempDB на отдельном физ. диске) и многопроцессорной обработки
IF @frag > 30.0
SET @command = @command + N'' WITH (SORT_IN_TEMPDB = ON, MAXDOP = 0)'';
-- Принудительное включение блокировки индекса на уровне страницы и её отключение, если блокировка была изначально выключена (для платформы 1С 8.3.22.х)
IF @allow_page_locks = 0
BEGIN
SET @command = N''ALT ER INDEX '' + @indexname + N'' ON '' + @schemaname + N''.'' + @objectname + N'' SET (ALLOW_PAGE_LOCKS = ON)'' + char(10) + @command;
SET @command = @command + char(10) + N''ALT ER INDEX '' + @indexname + N'' ON '' + @schemaname + N''.'' + @objectname + N'' SET (ALLOW_PAGE_LOCKS = OFF)'';
END
EXEC (@command);
PRINT N''Executed: '' + @command;
END;
-- Закрытие курсора
CLOSE partitions;
DEALLOCATE partitions;
-- Удаление временной таблицы
DR OP TABLE #work_to_do;')
FETCH NEXT FR OM Bases INTO @Base_id;
END;
CLOSE Bases;
DEALLOCATE Bases;
GO
Показать