Что делает платформа 1С, когда код обращается к ссылке через точку
Содержание
Строка Контрагент = Документ.Контрагент.ИНН выглядит безобидно, но платформа 1С по ссылке читает из базы весь объект целиком, вместе с табличными частями, и кладёт его в кэш объектов сеанса. Ради одного реквизита. Быстрее работает запрос, который выбирает только нужные поля, или функция БСП ОбщегоНазначения.ЗначениеРеквизитаОбъекта().
Типичная картина: обработка перебирает 5 000 документов, у каждого берёт Ссылка.Контрагент.Наименование, и выполняется не 3 секунды, а 4 минуты. Причина почти всегда одна: работа со ссылочными данными идёт через точку, а не через выборку. Механизм кэширования в 1С:Предприятии 8.3 здесь не спасает: кэш объектов ускоряет повторное чтение того же объекта, а не тысячи разных.
Как платформа превращает ссылку в объект
Ссылка (СправочникСсылка.Контрагенты, ДокументСсылка.РеализацияТоваровУслуг) — это не данные, а указатель: тип плюс уникальный идентификатор. Сама по себе она лёгкая. Её можно хранить в переменной, передавать между клиентом и сервером, сравнивать — всё это без обращения к базе.
База подключается в тот момент, когда от ссылки требуют содержимое.
Какие операции читают базу, а какие нет
| Операция | Чтение из базы |
|---|---|
Ссылка = Другая (сравнение) | нет |
Ссылка.Пустая(), ЗначениеЗаполнено(Ссылка) | нет |
Ссылка.УникальныйИдентификатор() | нет |
Ссылка.Наименование, Ссылка.Контрагент.ИНН | да, объект целиком |
Ссылка.ПолучитьОбъект() | да, объект целиком, создаётся новый экземпляр |
| вывод ссылки в поле формы или в строку | да, для получения представления |
| запрос с выбором полей | да, только выбранные поля |
Цепочка через точку
Документ.Контрагент.ГоловнойКонтрагент.ИНН — это три чтения подряд: документ, контрагент, головной контрагент. Каждое тянет объект целиком. Если у контрагента большая табличная часть контактной информации, вы её тоже прочитали, хотя она не нужна.
Длинные цепочки через точку внутри цикла — главный источник медленных обработок и проведения. На 10 строках это незаметно, на 10 000 — минуты.
Чем «битая» ссылка отличается от пустой
Пустая ссылка — нулевой идентификатор, в базу за ней платформа не ходит. Битая ссылка указывает на объект, которого в базе нет (удалён напрямую, не перенесён при обмене). В форме она видна как <Объект не найден>, а ПолучитьОбъект() вернёт Неопределено. Проверяйте результат перед использованием, иначе получите ошибку при обращении к полю.
Уровни кэширования в 1С: что, где и сколько живёт
Слово «кэш» в 1С означает несколько разных механизмов. Их часто путают, и отсюда советы вроде «почистите кэш, и проведение ускорится».
| Механизм | Что хранит | Где живёт | Как сбросить |
|---|---|---|---|
| Кэш объектов | данные объектов, прочитанных по ссылке | в памяти сеанса (на сервере — в рабочем процессе) | сам, по времени и при изменении версии объекта |
| Повторно используемые значения | результаты функций общих модулей с флагом повторного использования | на время вызова или сеанса, на клиенте или сервере | ОбновитьПовторноИспользуемыеЗначения() |
| Кэш метаданных клиента | файлы конфигурации, формы, картинки | каталоги пользователя в профиле Windows | ключ запуска /ClearCache или удаление каталогов кэша |
| Кэш СУБД | страницы данных и планы запросов | сервер СУБД | средствами СУБД, из 1С не управляется |
Кэш объектов
Когда код прочитал объект по ссылке, платформа сохраняет его данные в памяти сеанса. Повторное обращение к тому же объекту в течение короткого времени берёт данные оттуда, без запроса к СУБД.
Кэш короткоживущий и ограничен по размеру. Через некоторое время платформа сверяет версию данных объекта в базе и, если объект изменили в другом сеансе, перечитывает его. Поэтому на кэш объектов нельзя опираться как на источник актуальных данных в критичных местах, например при контроле остатков. Там нужен запрос с блокировкой.
Повторно используемые значения
Это кэш, которым управляет разработчик. У общего модуля в свойствах есть параметр «Повторное использование возвращаемых значений»:
- 🔹 Не использовать — функция выполняется при каждом вызове.
- 🔹 На время вызова — результат живёт, пока выполняется текущий серверный вызов.
- 🔹 На время сеанса — результат живёт дольше, но платформа может удалить его по времени или при нехватке памяти.
В такие модули выносят то, что почти не меняется: настройки учёта, значения констант, параметры организации. В типовых конфигурациях это модули с окончанием ПовтИсп и ПовтИспКлиент.
Возвращайте из таких функций неизменяемые значения: ФиксированнаяСтруктура, ФиксированныйМассив, ФиксированноеСоответствие. Если вернуть обычную структуру и кто-то изменит её в вызывающем коде, «испорченное» значение получат все следующие вызовы в сеансе.
Повторное использование не работает для функций с параметрами изменяемых типов: передать в такую функцию обычную структуру или массив нельзя. Ключ кэша строится по значениям параметров, поэтому параметры — примитивные типы, ссылки или фиксированные коллекции.
Кэш метаданных клиента
Это то, что чистят ключом /ClearCache в строке запуска 1cv8.exe. Он хранит описание конфигурации на компьютере пользователя и влияет на старт программы, открытие форм и странные ошибки после обновления. На скорость чтения данных из базы он не влияет.
Как читать ссылочные данные без лишней нагрузки
Правило простое: нужно одно-два поля — не читайте объект. Нужен объект для изменения и записи — читайте объект.
Способы получить значения реквизитов
| Способ | Что читается | Когда применять |
|---|---|---|
Ссылка.Реквизит | весь объект | разово, вне циклов, в простом коде |
ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ИНН") | одно поле через запрос | нужен один реквизит одного объекта |
ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "ИНН, КПП") | несколько полей одного объекта | нужны 2-5 полей |
| один запрос по списку ссылок | только нужные поля всех объектов | обработка массива, проведение, отчёты |
Ссылка.ПолучитьОбъект() | весь объект | будете менять и записывать |
Функции ОбщегоНазначения есть в конфигурациях на Библиотеке стандартных подсистем: БП 3.0, ЗУП 3.1, УТ 11 и других типовых. В самописной конфигурации без БСП запрос пишется вручную.
Пример: убрать обращение через точку из цикла
Медленно:
Для Каждого Документ Из МассивДокументов Цикл
ИНН = Документ.Контрагент.ИНН;
// ...
КонецЦикла;
Быстро: один запрос на весь массив.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Документ,
| Реализация.Контрагент.ИНН КАК ИНН
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Ссылка В (&МассивДокументов)";
Запрос.УстановитьПараметр("МассивДокументов", МассивДокументов);
ИННПоДокументам = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ИННПоДокументам.Вставить(Выборка.Документ, Выборка.ИНН);
КонецЦикла;
Дальше цикл берёт ИНН из соответствия. Вместо тысяч чтений объектов — один запрос.
Не выполняйте запрос внутри цикла по строкам. «Запрос в цикле» так же медленен, как точка в цикле: каждое выполнение — отдельное обращение к СУБД.
Ссылочные поля в запросах: где прячутся соединения
В языке запросов точка тоже работает, но иначе. Реализация.Контрагент.ИНН в запросе платформа превращает в левое соединение с таблицей справочника. Одно такое разыменование — нормально. Проблемы начинаются с составных типов.
Составной тип: соединение с каждой таблицей
Поле Регистратор у регистра может ссылаться на десятки видов документов. Запрос Остатки.Регистратор.Дата заставит платформу соединиться с таблицей каждого документа из состава типа. Запрос формально короткий, а план выполнения — огромный.
Ограничьте тип явно:
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Дата
Для дат регистраторов вообще не нужно разыменование: у таблиц регистров есть поле Период.
Представления ссылок
Если в результате запроса нужна строка для вывода, а не ссылка, используйте ПРЕДСТАВЛЕНИЕ(Поле) или ПРЕДСТАВЛЕНИЕССЫЛКИ(Поле). Иначе представление будет получено уже после запроса, при выводе, отдельными чтениями.
Чек-лист запроса со ссылками
Блокировки и одновременный доступ к объектам
Пока один пользователь читает документ, другой может его править. Платформа решает это блокировками, и здесь кэш объектов отходит на второй план: решения о списании остатков нельзя принимать по данным, прочитанным без блокировки.
Три вида защиты
| Механизм | Как работает | Где применяется |
|---|---|---|
| Оптимистическая блокировка | при записи сравнивается версия данных объекта; если её изменили в другом сеансе, запись отменяется с ошибкой | автоматически при записи объекта |
| Пессимистическая (объектная) блокировка | Объект.Заблокировать() или открытие формы на редактирование не дают другим сеансам изменить объект | интерактивное редактирование |
| Управляемая блокировка данных | БлокировкаДанных внутри транзакции, режимы «Разделяемый» и «Исключительный» | проведение, контроль остатков |
Пример: контроль остатков при проведении
Перед чтением остатков внутри транзакции проведения ставится исключительная блокировка на нужные записи регистра:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();
Здесь Товары — таблица значений с колонкой Номенклатура. После блокировки остатки читаются запросом, и два документа одновременно не спишут одну и ту же единицу товара.
Ожидание и взаимоблокировки
- 🔒 Если блокировку держит другой сеанс, ваша транзакция ждёт. Не дождавшись в пределах таймаута, она завершается ошибкой блокировки.
- 🔁 Взаимоблокировка — когда два сеанса ждут друг друга. Частая причина: разный порядок блокировки ресурсов, сначала разделяемая, потом исключительная блокировка того же ресурса.
- ⏳ Чем дольше транзакция, тем дольше держатся блокировки. Не вызывайте внутри неё внешние сервисы и не показывайте вопросы пользователю.
Ссылки в управляемом приложении: клиент, сервер, вызовы
В управляемом приложении клиент не работает с базой. В тонком и веб-клиенте ссылку можно хранить и передавать, но обращение через точку к её реквизитам на клиенте недоступно: код вида Объект.Контрагент.ИНН в процедуре &НаКлиенте не пройдёт проверку.
Каждый переход на сервер — это сетевой вызов. Через интернет он стоит десятки или сотни миллисекунд, и это заметно пользователю.
Как сократить серверные вызовы
- Собирайте всё нужное за один вызов: функция на сервере возвращает структуру с полями, а не вызывается три раза за три значения.
- Используйте
&НаСервереБезКонтекста, если серверу не нужна форма. Тогда данные формы не передаются туда и обратно. - Для редко меняющихся значений на клиенте заведите клиентский общий модуль с повторным использованием на время сеанса. Первый вызов сходит на сервер, следующие возьмут значение из клиентского кэша.
- В обработчиках
ПриИзмененииреквизитов формы считайте, сколько раз сработает сервер при вводе строки табличной части. Один вызов на строку при вставке 200 строк из буфера — 200 вызовов.
В динамическом списке поля ссылок платформа выбирает сама, одним запросом на страницу. Добавлять в форму списка код, который дочитывает реквизиты по каждой строке, не нужно: добавьте поле в запрос динамического списка.
Данные формы и объект
Форма документа хранит не объект, а его копию в виде данных формы. РеквизитФормыВЗначение("Объект") на сервере превращает их в объект, ЗначениеВРеквизитФормы() возвращает обратно. Это не чтение из базы, а преобразование, но оно не бесплатное для документа с тысячами строк. Не делайте его в каждом серверном вызове без нужды.
Как найти узкое место: замеры и технологический журнал
Медленная операция не всегда связана с кэшем. Прежде чем переписывать код, найдите, где уходит время.
Инструменты по порядку
- Замер производительности в конфигураторе. Запустите отладку, включите замер через меню отладки, выполните медленную операцию. Отчёт покажет строки кода с процентом времени и числом выполнений. Строка
Ссылка.Реквизитс 5 000 выполнений и 70% времени — ваш кандидат. - Технологический журнал. Настраивается файлом
logcfg.xmlна сервере 1С. СобытияSDBLиDBMSSQL(илиDBPOSTGRS) показывают запросы и их длительность,TLOCK,TTIMEOUT,TDEADLOCK— управляемые блокировки, ожидания и взаимоблокировки. - Оценка производительности из БСП. Ключевые операции и показатель APDEX показывают, какие действия пользователей стали медленнее со временем.
- Средства СУБД. Планы запросов и ожидания на сервере базы. Помогают, когда медленный один конкретный запрос.
Технологический журнал с полным набором событий на рабочем сервере быстро растёт до гигабайтов и нагружает диск. Включайте его с отбором по базе и длительности, на ограниченное время, и следите за местом.