При развитии крупных информационных систем на платформе 1С:Предприятие 8 часто возникает необходимость изменения архитектуры базы данных. Типичный сценарий — отказ от использования общего реквизита (например, выполнявшего роль разделителя доступа) в конкретном регистре накопления с сохранением накопленных данных и их переносом в обычное измерение регистра.
Главная сложность этой задачи на высоконагруженных базах с терабайтными объемами данных заключается в жестком ограничении на время простоя (технологического окна). Стандартная реструктуризация силами платформы 1С может занять десятки часов или даже дней, полностью заблокировав работу пользователей. В этой статье мы подробно разберем, как устроен этот процесс изнутри, проанализируем риски «быстрого» изменения через СУБД и рассмотрим действительно безопасные альтернативные подходы.
Для принятия правильного технического решения нам необходимо четко понимать, как платформа 1С проецирует метаданные на физические таблицы СУБД. Существует распространенное заблуждение, что общие реквизиты хранятся в некой отдельной связанной таблице. На самом деле это не так:
_AccumRg123) общий реквизит создается как самая обычная колонка (с физическим именем вида _Fld123_RRRef).На форумах часто предлагают «хакерский» способ быстрого решения проблемы в обход штатных механизмов 1С. Давайте проанализируем этот алгоритм, предложенный в сообщениях (6) и (8):
_AccumRg123) на уровне SQL-сервера.UPDATE или INSERT INTO ... SELECT скопировать данные из старой колонки общего реквизита в колонку нового измерения.ВНИМАНИЕ! Этот способ несет в себе критические риски для работоспособности базы данных:
Во-первых, вы рискуете полностью нарушить целостность внутренней таблицы DBSchema, в которой платформа 1С хранит точное описание структуры базы данных (включая состав индексов, типы полей и соответствие имен). Несовпадение реальной физической структуры в SQL и описания в DBSchema гарантированно приведет к ошибкам при последующих обновлениях конфигурации или выполнении операции «Тестирование и исправление».
Во-вторых, регистр накопления остатков состоит из двух таблиц: таблицы движений (_AccumRg123) и таблицы итогов (_AccumRgT123). Измерение регистра — это разрез учета, по которому СУБД рассчитывает и хранит остатки. Если вы просто скопируете данные в таблицу движений на уровне SQL, таблица итогов останется неверной. Все виртуальные таблицы остатков начнут выдавать некорректные и искаженные данные. Для исправления ситуации вам все равно придется запускать полный пересчет итогов средствами 1С, что на больших объемах нивелирует всю экономию времени от использования SQL-скриптов.
Чтобы избежать многочасовой остановки системы и не подвергать базу данных риску разрушения структуры, мы рекомендуем использовать методику постепенного фонового переноса. Этот подход разбивает одну тяжелую операцию на серию небольших изолированных транзакций.
Разберем по шагам реализацию этого метода:
В конфигураторе добавьте в регистр накопления новое измерение. Очень важно на этом этапе временно снять флаг «Запрет незаполненных значений» для этого измерения. Это позволит платформе успешно записывать новые движения с незаполненным измерением. Обновите конфигурацию базы данных. Данное обновление пройдет быстро, так как добавление новой колонки без пересчета существующих данных не требует полной перестройки таблицы СУБД.
Напишите внешнюю обработку (или регламентное задание), которая будет порциями считывать документы-регистраторы, считывать значения общего реквизита и записывать их в новое измерение. Ниже представлен пример кода, реализующий этот алгоритм:
// Процедура запускает порционный перенос данных в фоновом режиме
Процедура ВыполнитьПереносДанных() Экспорт
ИмяРегистра = "РегистрНакопления.ТоварыНаСкладах";
РазмерПорции = 1000; // Оптимальный размер порции для минимизации блокировок
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ " + Формат(РазмерПорции, "ЧГ=0") + "
| Регистр.Регистратор КАК Регистратор
|ИЗ
| РегистрНакопления.ТоварыНаСкладах КАК Регистр
|ГДЕ
| Регистр.НовоеИзмерение = ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)
| И Регистр.ОбщийРеквизитОрганизация <> ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)";
Выборка = Запрос.Выполнить().Выбрать();
Если Не Выборка.Следующий() Тогда
ЗаписатьВЛог("Перенос данных успешно завершен или нет данных для обработки.");
Возврат;
КонецЕсли;
СписокРегистраторов = Новый Массив;
Выборка.Сбросить();
Пока Выборка.Следующий() Цикл
СписокРегистраторов.Добавить(Выборка.Регистратор);
КонецЦикла;
// Обрабатываем порцию регистраторов в транзакции
НачатьТранзакцию();
Попытка
Для Каждого ДокументСсылка Из СписокРегистраторов Цикл
Набор = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
// Копируем значение из общего реквизита в новое измерение
Запись.НовоеИзмерение = Запись.ОбщийРеквизитОрганизация;
КонецЦикла;
// Записываем набор, отключая лишние проверки при необходимости
Набор.Записать(Истина);
КонецДля;
ЗафиксироватьТранзакцию();
ЗаписатьВЛог("Успешно обработана порция из " + СписокРегистраторов.Количество() + " документов.");
Исключение
ОтменитьТранзакцию();
ЗаписатьВЛог("Ошибка при обработке порции: " + ОписаниеОшибки());
ВызватьИсключение; // Прерываем выполнение фонового задания для анализа
КонецПопытки;
КонецПроцедуры
Запуск этой обработки можно настроить в виде фонового задания с интервалом в несколько секунд. Пользователи смогут продолжать беспрепятственно работать в системе, так как блокировка таблиц на период записи небольшого пакета документов (до 1000 штук) будет практически незаметна.
Когда фоновое задание обработает основную историческую массу данных, вам потребуется организовать короткое технологическое окно (обычно от 15 до 30 минут):
После завершения реструктуризации на этапе финального переключения крайне важно актуализировать таблицу итогов. Для этого воспользуйтесь стандартным механизмом пересчета итогов. Вы можете выполнить его интерактивно через обработку «Управление итогами» или запустить программно на коротком монопольном сеансе:
// Программный запуск пересчета итогов для конкретного регистра
Процедура ПересчитатьИтогиРегистра()
ИмяРегистра = "ТоварыНаСкладах";
ЗаписатьВЛог("Начало пересчета итогов для регистра: " + ИмяРегистра);
РегистрыНакопления[ИмяРегистра].ПересчитатьИтоги();
ЗаписатьВЛог("Пересчет итогов успешно завершен.");
КонецПроцедуры
Начиная с современных версий платформы 1С:Предприятие 8.3, разработчикам доступна встроенная технология «Фоновое обновление конфигурации базы данных». Этот механизм разработан специально для минимизации времени простоя крупных баз данных.
При использовании этого метода процесс выглядит следующим образом:
Если изменяемый общий реквизит использовался в вашей конфигурации как разделитель доступа (в рамках механизма RLS — Row Level Security), помните о критически важных последствиях его отключения:
Как только общий реквизит исключается из состава регистра, платформа 1С перестает автоматически накладывать неявные фильтры во всех запросах к этому регистру на уровне СУБД. Это означает, что если вы не перепишете правила RLS для этого регистра с использованием нового измерения, пользователи мгновенно увидят данные других областей (например, чужих организаций или филиалов), доступ к которым для них должен быть закрыт.
Перед запуском обновления в промышленную эксплуатацию обязательно проверьте и адаптируйте шаблоны ограничений доступа в ролях конфигурации, добавив явную фильтрацию по новому измерению регистра.