В процессе разработки высоконагруженных систем на платформе 1С:Предприятие мы часто сталкиваемся с необходимостью многократного чтения одних и тех же данных: настроек учета, констант, валют или справочников-классификаторов. Если обращаться к базе данных каждый раз, когда нам нужна ставка НДС или основной склад, производительность системы существенно снизится, особенно в циклах проведения документов. Рассмотрим, как организовать надежное кэширование, которое позволит моментально получать данные и при этом гарантировать их актуальность.
Стандартный механизм повторного использования возвращаемых значений в общих модулях 1С отлично работает «из коробки», но у него есть существенный недостаток: мы не можем точечно управлять очисткой этого кэша. Он очищается либо по истечении времени (20 минут), либо принудительно для всего сеанса сразу. Проанализируем ситуацию: если администратор изменил важную константу, другие пользователи могут продолжать работать со старым значением еще долгое время, что приведет к ошибкам в расчетах.
Для решения этой задачи разберем алгоритм синхронизации через контрольные ключи. Суть метода заключается в сравнении «версии» кэша в текущем сеансе с «версией», записанной в базе данных.
Этот метод позволяет заставить каждый сеанс обновить свои данные только тогда, когда это действительно необходимо. Выполним следующие шаги по реализации:
ОсновнойКлючКэшаКонстант с типом Строка (в ней мы будем хранить уникальный идентификатор версии данных).СеансовыйКлючКэшаКонстант с аналогичным типом.ПриЗаписи добавим код обновления ключа:
Процедура ПриЗаписи(Отказ)
// Генерируем новый ключ при любом изменении данных
Константы.ОсновнойКлючКэшаКонстант.Установить(Строка(Новый УникальныйИдентификатор));
КонецПроцедуры
Теперь рассмотрим, как правильно получать данные. Мы создадим единую точку входа в серверном общем модуле, которая будет проверять актуальность данных перед обращением к кэшированному методу:
Функция ПолучитьЗначениеНастройки(ИмяНастройки) Экспорт
ТекущийКлючБД = Константы.ОсновнойКлючКэшаКонстант.Получить();
// Если ключ в сеансе не совпадает с ключом в базе — сбрасываем кэш
Если ПараметрыСеанса.СеансовыйКлючКэшаКонстант <> ТекущийКлючБД Тогда
ОбновитьПовторноИспользуемыеЗначения();
ПараметрыСеанса.СеансовыйКлючКэшаКонстант = ТекущийКлючБД;
КонецЕсли;
Возврат ПолучитьДанныеИзКэша(ИмяНастройки);
КонецФункции
// Метод в модуле с установленным флагом "Повторное использование: на время сеанса"
Функция ПолучитьДанныеИзКэша(ИмяНастройки) Экспорт
// Здесь происходит реальное чтение из БД
Возврат Справочники.НастройкиУчета.НайтиПоНаименованию(ИмяНастройки);
КонецФункции
Проанализируем преимущества: мы минимизируем нагрузку на базу данных. Проверка двух строк (параметра сеанса и константы) происходит мгновенно, а тяжелый запрос к справочнику выполняется лишь один раз за все время жизни ключа.
Если настроек не слишком много, мы можем использовать ПараметрыСеанса. Это один из самых быстрых способов доступа к данным в 1С. Чтобы не создавать десятки параметров, упакуем их в одну структуру. Посмотрим на пример реализации в модуле сеанса:
Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
Буфер = Новый Структура;
Буфер.Вставить("ИспользоватьСклады", Константы.ИспользоватьСклады.Получить());
Буфер.Вставить("ВалютаУчета", Константы.ВалютаРегламентированногоУчета.Получить());
// Используем ФиксированнуюСтруктуру, так как параметры сеанса должны быть неизменяемыми
ПараметрыСеанса.ОбщиеНастройки = Новый ФиксированнаяСтруктура(Буфер);
КонецПроцедуры
Важный момент: Обращение к ПараметрыСеанса.ОбщиеНастройки.ВалютаУчета работает на порядок быстрее, чем вызов Константы.ВалютаРегламентированногоУчета.Получить(), так как данные уже находятся в памяти рабочего процесса для этого сеанса.
Рассмотрим ситуацию, когда нам нужно кэшировать данные только на время выполнения конкретной процедуры (например, при печати множества документов). Чтобы не передавать структуру настроек через все функции, можно использовать локальную переменную в модуле:
Перем ЛокальныйКэшНастроек;
Функция ПолучитьТочность(ИмяПараметра)
Если ЛокальныйКэшНастроек = Неопределено Тогда
ЛокальныйКэшНастроек = Новый Соответствие;
КонецЕсли;
Значение = ЛокальныйКэшНастроек.Получить(ИмяПараметра);
Если Значение = Неопределено Тогда
Значение = Константы[ИмяПараметра].Получить();
ЛокальныйКэшНастроек.Вставить(ИмяПараметра, Значение);
КонецЕсли;
Возврат Значение;
КонецФункции
Этот прием называется "ленивой инициализацией". Программа прочитает настройку из базы только при первом обращении, а все последующие вызовы в рамках работы этого экземпляра объекта будут брать значение из переменной ЛокальныйКэшНастроек.
Выясним, почему Функциональные опции (ФО) являются наиболее профессиональным способом решения задачи. Платформа 1С содержит встроенный оптимизированный механизм кэширования ФО. Если вы привязываете функциональную опцию к константе, платформа сама:
Поэтому для простых значений типа Булево или ссылок на основные настройки рекомендуется в первую очередь использовать именно ФО, а не изобретать собственные велосипеды с GUID.
Разберем несколько тонкостей, которые помогут избежать типичных ошибок при работе с кэшем:
ПоместитьВоВременноеХранилище(). Это гораздо эффективнее, чем передавать таблицу значений с сервера на клиент и обратно.Выяснив все способы управления условно-постоянной информацией, мы можем сделать вывод: для простых флагов лучше всего подходят Функциональные опции, для структурированных настроек — Параметры сеанса, а для сложных выборок из БД — Общие модули с повторным использованием и механизмом контрольных GUID-ключей.