Как очистить огромный регистр сведений в файловой базе 1С и избежать ошибки «Недостаточно памяти»?

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

При администрировании и разработке в системе «1С:Предприятие» одной из частых проблем становится неконтролируемый рост системных регистров сведений. Ярким примером является регистр ЛогДействийКассира в конфигурации «1С:Розница». Со временем в нем накапливаются миллионы записей, что приводит к значительному увеличению размера файловой базы данных (файла 1CD), замедлению работы интерфейса и трудностям при резервном копировании.

Попытка очистить такой регистр «в лоб» с помощью стандартного метода Записать(Истина) для пустого набора записей в файловой базе данных, которая к тому же работает в режиме распределенной информационной базы (РИБ), практически всегда заканчивается аварийным завершением работы программы с ошибкой «Недостаточно памяти». В этой статье мы подробно разберем причины этой проблемы и рассмотрим несколько эффективных способов её решения.

Выясняем причины: почему возникает ошибка «Недостаточно памяти»?

Прежде чем перейти к практическим методам решения, давайте проанализируем, почему платформа «1С:Предприятие» завершает работу аварийно при попытке очистить большой объем данных:

  1. Ограничения транзакционной модели файловой базы (1CD). Когда мы вызываем метод очистки регистра без предварительной установки отборов, платформа пытается выполнить удаление всех записей в рамках одной неделимой транзакции. Для СУБД файловой базы это означает необходимость построить гигантский массив изменений в оперативной памяти компьютера перед его физической записью на диск. Если объем данных велик (сотни тысяч или миллионы строк), лимит выделения адресного пространства для процесса будет быстро исчерпан.
  2. Ограничения 32-битного клиента 1С. Даже если вы разрешили 32-битным приложениям использовать более 2 Гб оперативной памяти, физический лимит адресного пространства для 32-битных процессов в ОС Windows все равно жестко ограничен (около 3–3.5 Гб). Платформа просто физически не может адресовать больше памяти для выполнения тяжелой транзакции.
  3. Механизм регистрации изменений в РИБ. Это самый важный и критичный фактор. Если очищаемый регистр сведений входит в состав планов обмена распределенной информационной базы, платформа при удалении каждой строки регистрирует это событие в специальной таблице изменений. Попытка зарегистрировать миллионы удалений за один раз мгновенно перегружает оперативную память. Даже если операция завершится успешно, сформируется огромный файл обмена (сотни мегабайт или гигабайты), который периферийные узлы РИБ просто не смогут загрузить, вызвав аналогичную ошибку нехватки памяти уже на кассовых компьютерах.

Способ 1. Использование 64-битного клиента «1С:Предприятие»

Самым простым системным решением для обхода ограничений памяти является запуск процедуры очистки под управлением 64-битного клиента 1С (x64). Разберем этот вариант подробнее:

В отличие от стандартного 32-битного клиента, 64-битная версия платформы не имеет жесткого ограничения на использование оперативной памяти одним процессом в 2-3 Гб. Она способна задействовать весь объем физически установленной памяти на сервере или компьютере разработчика (например, 16 Гб или 32 Гб).

Если база данных файловая, достаточно временно скопировать ее на мощный компьютер с установленной 64-битной платформой «1С:Предприятие», запустить базу в режиме «1С:Предприятие» под x64-клиентом и выполнить очистку. Транзакция удаления, требующая больших объемов памяти, успешно завершится без падения процесса. После очистки и сжатия базы ее можно вернуть на исходный рабочий компьютер.

Способ 2. Очистка набора записей порциями (пакетами) с отключением регистрации изменений

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

Для предотвращения регистрации изменений на узлах РИБ мы должны использовать свойство ОбменДанными.Загрузка = Истина. Это критически важно, иначе процесс удаления заблокирует обмен.

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


// Процедура порционной очистки независимого регистра сведений
ВыборкаЗаписей = Новый Запрос;
ВыборкаЗаписей.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 10000"
    "    ЛогДействийКассира.Период КАК Период,"
    "    ЛогДействийКассира.Кассир КАК Кассир,"
    "    ЛогДействийКассира.НомерОперации КАК НомерОперации"
    "ИЗ"
    "    РегистрСведений.ЛогДействийКассира КАК ЛогДействийКассира";

Пока Истина Цикл
    РезультатЗапроса = ВыборкаЗаписей.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        Прервать; // Все записи успешно удалены
    КонецЕсли;
    
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Создаем набор записей для точечного удаления
        НЗ = РегистрыСведений.ЛогДействийКассира.СоздатьНаборЗаписей();
        
        // Устанавливаем отборы по всем измерениям регистра
        НЗ.Отбор.Период.Установить(Выборка.Период);
        НЗ.Отбор.Кассир.Установить(Выборка.Кассир);
        НЗ.Отбор.НомерОперации.Установить(Выборка.НомерОперации);
        
        // Важно: отключаем регистрацию в РИБ и выполнение подписок на события
        НЗ.ОбменДанными.Загрузка = Истина;
        
        // Записываем пустой набор для удаления конкретной записи
        НЗ.Записать(Истина);
    КонецЦикла;
    
    // Фиксируем транзакцию и даем СУБД освободить память
    ОбработкаПрерыванияПользователя();
КонецЦикла;

Обратите внимание: При использовании свойства ОбменДанными.Загрузка = Истина записи будут удалены исключительно локально (в той базе, где запущен этот код). На остальных узлах РИБ эти записи останутся нетронутыми. Чтобы обеспечить полную синхронность данных во всей распределенной сети, у вас есть два пути:

  1. Запустить данный скрипт очистки локально на абсолютно каждом узле РИБ (в центральном офисе и на всех торговых точках).
  2. Временно исключить регистр сведений ЛогДействийКассира из состава планов обмена в конфигураторе, выполнить полную очистку на главном узле, а затем вернуть его обратно в состав плана обмена (после чего выполнить повторную инициализацию данных на узлах).

Сравнение подходов: почему стоит использовать НаборЗаписей вместо МенеджераЗаписи?

В процессе разработки часто возникает соблазн использовать объект МенеджерЗаписи для удаления отдельных строк, так как его синтаксис кажется более простым и лаконичным. Проанализируем, почему для массовых операций этот подход категорически противопоказан:

Универсальный программный инструмент для очистки любого регистра

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


// Универсальный алгоритм для удаления записей из любого независимого регистра сведений порциями
ИмяРегистра = "ЛогДействийКассира";
РазмерПорции = 5000;

// Сформируем текст запроса динамически на основании измерений регистра
МетаданныеРегистра = Метаданные.РегистрыСведений[ИмяРегистра];
СтрокаИзмерений = "";
СтрокаВыбора = "";

Для Каждого Измерение Из МетаданныеРегистра.Измерения Цикл
    ИмяИзмерения = Измерение.Имя;
    СтрокаИзмерений = СтрокаИзмерений + ИмяИзмерения + ", ";
    СтрокаВыбора = СтрокаВыбора + "РегистрИсточник." + ИмяИзмерения + " КАК " + ИмяИзмерения + ", ";
КонецЦикла;

// Удаляем лишнюю запятую и пробел в конце строк
СтрокаИзмерений = Лев(СтрокаИзмерений, СтрДлина(СтрокаИзмерений) - 2);
СтрокаВыбора = Лев(СтрокаВыбора, СтрДлина(СтрокаВыбора) - 2);

ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерПорции, "ЧГ=0") + " " + СтрокаВыбора + " ИЗ РегистрСведений." + ИмяРегистра + " КАК РегистрИсточник";

Запрос = Новый Запрос(ТекстЗапроса);
ВсегоУдалено = 0;

Пока Истина Цикл
    Результат = Запрос.Выполнить();
    Если Результат.Пустой() Тогда
        Прервать;
    КонецЕсли;
    
    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Для каждой строки создаем свой набор записей
        Набор = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
        
        // Устанавливаем отборы по всем измерениям динамически
        Для Каждого Измерение Из МетаданныеРегистра.Измерения Цикл
            ИмяИзмерения = Измерение.Имя;
            Набор.Отбор[ИмяИзмерения].Установить(Выборка[ИмяИзмерения]);
        КонецЦикла;
        
        // Отключаем обработчики и обмен данными для экономии памяти
        Набор.ОбменДанными.Загрузка = Истина;
        
        Попытка
            Набор.Записать(Истина);
            ВсегоУдалено = ВсегоУдалено + 1;
        Исключение
            Сообщить("Ошибка при удалении записи: " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;
    
    // Каждые N удалений выводим информацию в консоль для мониторинга
    Сообщить("Успешно удалено записей: " + ВсегоУдалено);
    ОбработкаПрерыванияПользователя();
КонецЦикла;

Способ 3. Использование штатных механизмов «1С:Розницы»

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

Разберем по шагам, как задействовать этот механизм:

  1. Перейдите в раздел меню Администрирование -> Продажи -> Логирование действий кассиров.
  2. Установите флаг использования логирования и укажите разумный срок хранения записей в параметре «Хранить лог действий кассиров» (например, 30 или 45 дней). Хранить более старую историю действий на кассовых узлах не имеет практического смысла.
  3. Включите и настройте расписание для регламентного задания «Очистка лога действий кассиров».

Штатное регламентное задание разработано профессионально: оно производит удаление устаревших данных небольшими порциями в фоновом режиме. Это позволяет избежать блокировок базы данных, не вызывает пиковых нагрузок на оперативную память и не приводит к падению платформы.

Способ 4. Экстремальная очистка таблицы напрямую в файле базы данных (.1CD)

Бывают ситуации, когда база данных повреждена или раздулась до критического объема (например, файл 1CD превысил лимит в 4 Гб на одну таблицу), из-за чего запуск предприятия или конфигуратора становится невозможным. В этом случае программисты используют инструмент для прямого редактирования структуры СУБД 1С.

Рассмотрим последовательность действий при данном экстремальном подходе:

  1. Обязательно создайте резервную копию файла базы данных (1Cv8.1CD)! Любые прямые манипуляции с файлом базы данных могут привести к ее полной неработоспособности.
  2. Убедитесь, что все пользователи вышли из базы данных, а также остановлены все фоновые процессы и службы (например, веб-серверы, если база опубликована).
  3. Запустите специализированную утилиту для работы с файловыми базами — Tool_1CD.
  4. Откройте файл базы данных в утилите и найдите системную таблицу, соответствующую вашему регистру сведений. Обычно физическое имя таблицы выглядит как _InfoRgXXXX (где XXXX — порядковый номер таблицы в конкретной конфигурации). Выяснить точное имя таблицы вашего регистра можно в конфигураторе 1С с помощью функции «Получить структуру хранения базы данных» или с помощью встроенного поиска в Tool_1CD по метаданным.
  5. Выполните команду полной очистки (Truncate) выбранной таблицы в утилите Tool_1CD. Это мгновенно удалит все строки из таблицы физически, не нагружая оперативную память.
  6. После сохранения изменений закройте утилиту Tool_1CD.
  7. Запустите штатную утилиту тестирования файлов баз данных chdbfl.exe (она находится в папке bin установленной платформы 1С) для исправления возможных физических ошибок индексов.
  8. Запустите базу данных в режиме «Конфигуратор» и выполните процедуру «Тестирование и исправление» с установленными флагами проверки логической целостности и реиндексации таблиц базы данных.

Используйте этот метод только в самых крайних случаях, когда другие стандартные средства 1С оказываются бессильны.

Резюме и рекомендации

Для решения проблемы переполнения регистра ЛогДействийКассира в РИБ мы рекомендуем придерживаться следующего плана действий. Сначала попробуйте настроить штатное регламентное задание очистки логов в «1С:Рознице». Если это невозможно из-за критического объема накопленных данных, выполните программное удаление порциями с обязательным отключением регистрации изменений (ОбменДанными.Загрузка = Истина) на каждом узле РИБ отдельно. И лишь в крайнем случае, когда платформа не дает даже запустить сеанс, прибегайте к прямому редактированию базы данных с помощью сторонних инструментов.

← На главную