Как 1С записывает табличные части документов в СУБД: перезаписываются ли все строки или только новые?

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

При проектировании высоконагруженных систем на платформе 1С:Предприятие разработчики часто сталкиваются с вопросами производительности СУБД (будь то Microsoft SQL Server, PostgreSQL или IBM DB2). Один из наиболее частых вопросов: как физически происходит запись документа, содержащего табличную часть?

Допустим, в документе уже есть 10 строк, и пользователь (или программный код) добавляет 11-ю строку. Что происходит в СУБД при вызове метода Записать()? Добавляется ли только одна новая строка, или СУБД полностью перезаписывает все 11 строк? И как в этот момент ведет себя таблица шапки документа? Разберем эти механизмы во всех подробностях, изучим внутреннее устройство платформы и проанализируем влияние этих процессов на производительность базы данных.

Архитектурное устройство таблиц документов в СУБД

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

  1. Основная таблица документа (шапка) — содержит уникальный идентификатор (ссылку _IDRRef), номер, дату, признак проведения, а также все реквизиты шапки. Физическое имя таблицы в СУБД выглядит примерно так: _Document111.
  2. Табличные части — для каждой табличной части документа в СУБД создается отдельная физическая таблица. Ее имя обычно состоит из имени таблицы шапки и суффикса табличной части, например: _Document111_VT222. В этой таблице содержатся поля связи (ссылка на родительский документ _Document111_IDRRef), номер строки _LineNo и реквизиты табличной части.

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

Как записывается табличная часть документа: DELETE + INSERT

Разберем ключевой аспект работы платформы. Платформа 1С не вычисляет разницу (diff) между старым состоянием табличной части в СУБД и новым состоянием в оперативной памяти. Она не пытается определить, какая именно строка была добавлена, изменена или удалена пользователем на форме.

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

Шаг 1. Полное удаление старых строк (DELETE)

При записи документа платформа отправляет в СУБД запрос на удаление всех существующих строк данной табличной части, которые привязаны к уникальному идентификатору (GUID) записываемого документа. На уровне SQL это выглядит следующим образом:


DELETE FROM _Document111_VT222 
WHERE _Document111_IDRRef = 0x80C200155D01010111E1A1A1A1A1A1A1;

Даже если в документе изначально было 10 000 строк, а мы просто изменили один символ в одной строке или добавили одну новую, СУБД сначала физически удалит все 10 000 строк.

Шаг 2. Запись всех строк заново (INSERT)

После очистки таблицы от старых записей платформа считывает актуальный массив строк табличной части из оперативной памяти сервера приложений 1С и записывает их в СУБД "с чистого листа". В нашем примере запишутся все 11 строк.

На уровне СУБД выполняется серия инструкций INSERT:


INSERT INTO _Document111_VT222 (_Document111_IDRRef, _LineNo, _Fld333, _Fld334)
VALUES 
(0x80C200155D01010111E1A1A1A1A1A1A1, 1, Значение1, Значение2),
(0x80C200155D01010111E1A1A1A1A1A1A1, 2, Значение1, Значение2),
...
(0x80C200155D01010111E1A1A1A1A1A1A1, 11, Значение1, Значение2);

Для оптимизации этого процесса современные версии платформы (8.3) используют параметризованные пакетные запросы (Bulk Insert), объединяя несколько инструкций вставки в один сетевой пакет. Это минимизирует накладные расходы на сетевое взаимодействие между сервером приложений 1С и сервером СУБД.

Как записывается шапка документа: UPDATE

В отличие от табличной части, строка в основной таблице документа (шапке) никогда не удаляется при перезаписи. Для обновления реквизитов шапки платформа использует стандартную инструкцию UPDATE:


UPDATE _Document111 
SET _Fld112 = 'НовоеЗначениеРеквизита', _Version = @НоваяВерсияДанных
WHERE _IDRRef = 0x80C200155D01010111E1A1A1A1A1A1A1;

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

Влияние на сопутствующие таблицы (регистры и справочники)

Выясним еще один важный момент, затронутый в обсуждении на форуме. Запись документа — это не только работа с таблицами самого документа. В реальных конфигурациях при вызове метода Записать() с режимом проведения запускается целый комплекс транзакционных изменений:

Технические последствия механизма DELETE + INSERT

Такой "радикальный" способ перезаписи табличных частей имеет ряд важных технических последствий, о которых должен знать каждый разработчик.

1. Быстрая фрагментация индексов

Поскольку при каждой перезаписи документа происходит массовое удаление старых строк и вставка новых, в физических файлах СУБД образуются "дыры". Индексы, построенные по таблицам табличных частей (особенно по ключевому полю _Document111_IDRRef), подвергаются сильной фрагментации.

Если документы в системе перезаписываются часто, это приводит к резкому падению производительности операций чтения. Для борьбы с этим требуется регулярное обслуживание СУБД — дефрагментация и реиндексация таблиц (Rebuild/Reorganize Indexes) во время технологических окон.

2. Накопление "фантомных" записей (Ghost Records / Vacuum)

При выполнении операции DELETE современные СУБД не удаляют данные с диска мгновенно — это слишком дорогая операция. Вместо этого записи помечаются как удаленные.

Если темп перезаписи документов слишком высок, СУБД может не успевать вычищать удаленные строки, что приводит к раздуванию базы данных ("bloat") и замедлению сканирования таблиц.

3. Блокировки и дедлоки (Deadlocks)

При выполнении цикла DELETE + INSERT СУБД накладывает монопольные блокировки на строки (или даже на страницы данных/всю таблицу при эскалации блокировок). Если несколько сеансов одновременно пытаются записать или провести документы одного типа, это может приводить к длительным ожиданиям на блокировках и взаимным блокировкам (дедлокам).

4. Нумерация строк (_LineNo)

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

Практические рекомендации по оптимизации работы с табличными частями

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

  1. Контролируйте размер табличных частей. Если бизнес-логика предполагает, что в документе может быть более 1000–5000 строк (например, огромные накладные или чеки закрытия смены), использование стандартной табличной части становится узким местом. При каждом изменении будет переписываться весь массив данных. В таких случаях эффективнее хранить данные в подчиненном регистре сведений с привязкой по регистратору или в подчиненном справочнике.
  2. Избегайте лишних перезаписей. Перед вызовом метода Записать() программно проверяйте, изменились ли данные документа. Не вызывайте перезапись "на всякий случай".
  3. Разделяйте транзакции. По возможности переносите тяжелые расчеты и модификацию связанных данных из транзакции записи документа в фоновые задания.
  4. Используйте оптимизированные методы записи. Если вам нужно обновить только движения, записывайте непосредственно наборы записей регистров, не переписывая сам документ-объект.
← На главную