Программные перечисления, ч.2: приемы кэширования при разработке

06.10.22

Разработка - Универсальные функции

Все знают, что такое кэш, и зачем он нужен. Но в 1С разработчик обычно использует кэширование только на уровне конфигурации, а в какой-нибудь обработке скорее ломает голову над запросом - как получить все данные за один заход... Хочется рассказать о том, как можно добиться хороших результатов с стратегией "разделяй и властвуй".

В своей первой статье по теме кэширования Использование программных перечислений, ч.1: строковые константы я рассказал о способе, который избавит от неприятностей, связанных с сравнением в коде на строку. Здесь на базе этого-же приема рассмотрена задача по оптимизации загрузки данных из внешнего источника. Расскажу о случае, который неоднократно наблюдал в разных его ипостасях. Стоит задача по загрузке данных в БД из внешнего файла, заказчик предоставил пару примеров, программист успешно все реализовал \ протестировал, сдал работу.. И тут через месяц заказчик грузит файл раз эдак в 50 превышающий пример.. Все не просто тупит, а зависает на ~2 часа. Заказчик недоволен. После работ по оптимизации время загрузки снизилось с 2 часов до 30 минут (из которых только 5 это синхронизация, а все остальное - запись данных в базу).. В чем же была ошибка? При синхронизации данных не использовалось кэширование, и поиск ссылок по одним и тем же ключевым полям выполнялся многократно. После того как был добавлен кэш все залетало.

 
 0. Началась оптимизация с того, что...
 
 1. Синхронизация данных
 
2. Выборка из результата запроса, и метод "НайтиСледующий()"
 
 3. Работа с планом счетов
 
 4. Обертки в общих модулях
 
 5. А вот так делать нельзя...

См. также

SALE! 15%

Инструментарий разработчика Роли и права Запросы СКД Программист Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    159410    872    399    

861

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    14730    YA_418728146    7    

166

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    3580    56    progmaster    8    

4

Запросы Инструментарий разработчика Программист Бесплатно (free)

Список всех популярных обработок.

17.03.2023    60031    kuzyara    89    

190

Механизмы платформы 1С Программист Платформа 1С v8.3 Бесплатно (free)

Давайте разберемся в механизме «История данных» и поэкспериментируем для наглядности. Сравним «Версионирование объектов» и «Историю данных».

06.03.2023    31731    dsdred    74    

214

Механизмы платформы 1С Программист Платформа 1С v8.3 Россия Бесплатно (free)

При знакомстве с новой механикой работы с асинхронностью (обещание, ждать и асинх) делал пометки, которыми и хочу поделиться. Ничего сверхъестественного в них нет, просто небольшие примеры и некоторые всплывшие нюансы использования.

29.07.2022    65984    zeltyr    25    

213

Механизмы платформы 1С Системный администратор Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Копнем глубже в тему "Что же такое динамическое обновление" и почему оно может привести к проблемам. И может ли?

09.05.2022    34139    Infostart    84    

248

Инструментарий разработчика Рефакторинг и качество кода Групповая разработка (Git, хранилище) Механизмы платформы 1С Программист Стажер Бесплатно (free)

Стек технологий, которые могут быть полезны разработчику на 1С и около 1С. По каждой технологии постарался объяснить, зачем она нужна и с чего начать изучение, если заинтересует.

29.11.2021    52780    mrXoxot    63    

482
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. mifka186 9 30.10.17 09:32 Сейчас в теме
Есть еще ситуации, когда надо грузить много однотипных файлов. В таком случае я поиск данных скидываю в кэш. Получается, что все возможные значения для загрузки находятся на первых 2-3 файлах, а самих файлов может быть 70-80. Дальше заполнение данных идёт уже из кэша.
2. guy_septimiy 30.10.17 13:32 Сейчас в теме
(1) Аналогично. Вчера делал таким образом загрузку данных о операциях по мобильной связи. Файл в миллион строк с кешированием грузится где-то за 2 минуты. Если не использовать предварительное кеширование и обновление кеша, то время подскакивает раз в 20.
3. vano-ekt 124 02.11.17 08:00 Сейчас в теме
КлючСчета = "сч" + СтрЗаменить(Выборка.Код, ".", "_")

они же предопределенные, зачем их кэшить?
А если кэшить, чем соответствие не угодило?
Запрос = Новый Запрос("ВЫБРАТЬ
                      |	Бюджетирование.Код,
                      |	Бюджетирование.Ссылка
                      |ИЗ
                      |	ПланСчетов.Бюджетирование КАК Бюджетирование");
Выборка = Запрос.Выполнить().Выбрать();
СчетаПоКодам = Новый Соответствие;
Пока Выборка.Следующий() Цикл
	СчетаПоКодам.Вставить(Выборка.Код,Выборка.Ссылка);
КонецЦикла;

Если Счет = СчетаПоКодам["20321"] Тогда
	//
КонецЕсли;
Показать
4. unichkin 1579 02.11.17 12:59 Сейчас в теме
Счет может и не быть предопределенным, а быть пользовательским. Если использовать соответствие нужно получать значение по ключу, напр.
СчетаУпр.Получить(<КодСчета>) или СчетаУпр[<КодСчета>]- долго, проще написать СчетаУпр.сч58. Кроме того у соответствия нет такого полезного метода как "Свойство".
Кэшить их - затем чтобы применять в люом месте программы, не объявляя новый поиск счета.
13. kasper076 110 06.12.17 14:57 Сейчас в теме
(0)
КэшСсылок = Новый Соответствие;
КэшСсылок.Вставить("Объект1",		Новый Соответствие);
КэшСсылок.Вставить("Объект2",		Новый Соответствие);
КэшСсылок.Вставить("Объект3",		Новый Соответствие);

Пока ПолучитьСледующийЭлемент() Цикл

	Объект1Ссылка = КэшСсылок["Объект1"][Ключ1];
	Если Объект1Ссылка = Неопределено Тогда
		Объект1Ссылка = ПолучитьОбъект1(Ключ1); //Функция выполняет запрос 
		КэшСсылок["Объект1"][Ключ1] = Объект1Ссылка;
	КонецЕсли;
	
	Объект2Ссылка = КэшСсылок["Объект2"][Ключ2];
	Если Объект2Ссылка = Неопределено Тогда
		Объект2Ссылка = ПолучитьОбъект2(Ключ2); //Функция выполняет запрос 
		КэшСсылок["Объект2"][Ключ2] = Объект2Ссылка;
	КонецЕсли;

	Объект3Ссылка = КэшСсылок["Объект3"][Ключ3];
	Если Объект3Ссылка = Неопределено Тогда
		Объект3Ссылка = ПолучитьОбъект3(Ключ3); //Функция выполняет запрос 
		КэшСсылок["Объект3"][Ключ3] = Объект3Ссылка;
	КонецЕсли;

КонецЦикла
Показать


(4) Эффективная обработка данных в оперативной памяти за счет использования коллекции "соответствие"
(5) Загрузка ТаблицыЗначений в TempDB производится построчно. Если таблица будет большая, то это будет не оптимально.
14. unichkin 1579 06.12.17 17:12 Сейчас в теме
(13)
- "доступ к элементу соответствия по ключу происходит почти со скоростью доступа к массиву или элементу структуры!" - и что? Тут больше дело привычки. У соответствия нет метода "Свойство" - нельзя проверить, существует ли параметр.
- в приведенном коде все кэширование выполняется локально, что раздувает метод, и дублирует код. Инициализация кэша, поиск в кэше, помещение в кэш - все в одном месте. Если это модуль объекта, лучше обособить, как мне кажется - будет читабельнее.

Пока ПолучитьСледующийЭлемент() Цикл

    Объект1Ссылка = ОбъектКэша(Ключ1);
    Объект2Ссылка = ОбъектКэша(Ключ2);
    Объект3Ссылка = ОбъектКэша(Ключ3);
    

КонецЦикла

Функция ОбъектКэша(Ключ) 
	                                
	Если НЕ мСтруктураКэшДанных.Свойство("СоответствиеОбъектыКэша") Тогда
		мСтруктураКэшДанных.Вставить("СоответствиеОбъектыКэша");
	КонецЕсли;
	
	ЗначениеКэша = мСтруктураКэшДанных.СоответствиеОбъектыКэша.Получить(Ключ);
	Если ЗначениеКэша = Неопределено Тогда
		ЗначениеКэша = ПолучитьОбъект(Ключ);		
		мСтруктураКэшДанных.СоответствиеОбъектыКэша.Вставить(Ключ, ЗначениеКэша);		
	КонецЕсли; 
	
	Возврат ЗначениеКэша;
		
КонецФункции
Показать
5. NN2P 420 02.11.17 13:33 Сейчас в теме
Роман, можете уточнить, почему при загрузке данных извне Вы не рассматриваете следующий прием: загрузить данные в таблицу значений из внешнего источника.Передать ее в запрос как параметр. В запросе соединиться со всеми нужными таблицами(справочниками, регистрами и пр.). Затем проверить наличие ссылок или записей регистров в результате соединения с таблицами. Затем по несопоставленным пройтись и создать, по сопоставленным обновить данные, если есть различия.
Merkalov; user757381; u_n_k_n_o_w_n; Sam13; creatermc; bulpi; +6 Ответить
8. bulpi 217 07.11.17 23:13 Сейчас в теме
(5)
Истину глаголешь! :)
Идеи, предлагаемые автором весьма и весьма спорны. Как по производительности, так и по читабельности.
Tyler Durden; +1 1 Ответить
6. unichkin 1579 02.11.17 18:54 Сейчас в теме
Я рассматриваю, см. п 2.1
15. Synoecium 785 27.04.18 13:45 Сейчас в теме
(6) можно исходную таблицу нарезать порциями (например по 10к элементов) и в цикле загружать их как описано в 2.1. На мой взгляд наиболее универсальный и быстрый способ.
7. jONES1979 03.11.17 09:03 Сейчас в теме
Cпасибо за качественное оформление блоками! Очень удобно просматривать!
jif; О.Ж; JohnConnor; Serg O.; +4 Ответить
9. Serg O. 297 08.11.17 00:38 Сейчас в теме
Идея клёвая... а как продолжение... Можно ли сохранить кэш в хранилище значений? Если такие обмены раз в сутки делаются после перезагрузки севера. Или в файл придется сохранять...
10. unichkin 1579 08.11.17 11:00 Сейчас в теме
(9) Да, тогда промежуточное хранилище необходимо. Возможно имеет смысл задействовать подсистему присоединенных файлов, чтобы был удобный доступ к кэшу
11. klel 08.11.17 11:52 Сейчас в теме
Спасибо идея классная, сам пользуюсь при обменах между базами. Ускоряет работу в разы.
12. Scorpion4eg 434 23.11.17 08:01 Сейчас в теме
Хорошая статья. Вот только при действительно больших объемах кэширование скорее враг. Недавно пришлось поставить x64 платформу. Потому что 1с выедала 4,5 оперативнки. Так получилось что в кэш попало 500 тыс уникальных значений.
16. pavlovsv 08.05.18 16:17 Сейчас в теме
(12) Согласен с Вами, тем не менее, идея очень здравая. Позволяет существенно снизить количество обращений к БД в случае обработки больших объемов одних и тех же данных, которые необходимо получать "через точку".
Что же касается механизма для "нормализации" кэша, контроля за его размерами, управление жизненным циклом кэшированных данных... Думаю, что можно попробовать самостоятельно развить этот концепт.

Хотел обратить внимание, что в случае использования ТаблицыЗначений для хранения кэшированных значений, может быть полезным организация индекса по ключевым полям.
В конкретном примере из этой статьи, наверное, это избыточно(так как по условию задачи записей будет всего 10).
Однако, в случае, когда количество строк несколько тысяч и ключ для поиска составной - индекс становится полезным.

1. Синхронизация данных -> Как стало
Функция НайтиСотрудникаОрганизации(Организация, ФизЛицо)

// На входе два ссылочных реквизита - соответствие использовать неудобно, в качестве кэша применяется таблица значений:
Если НЕ мСтруктураКэшДанных.Свойство("ТаблицаСотрудников") Тогда
ТаблицаСотрудников = Новый ТаблицаЗначений;
ТаблицаСотрудников.Колонки.Добавить("Организация");
ТаблицаСотрудников.Колонки.Добавить("ФизЛицо");
ТаблицаСотрудников.Колонки.Добавить("Сотрудник");

ТаблицаСотрудников.Индексы.Добавить("Организация, ФизЛицо"); //добавим индекс по ключевым полям

мСтруктураКэшДанных.Вставить("ТаблицаСотрудников", ТаблицаСотрудников);
КонецЕсли;
17. unichkin 1579 08.05.18 18:33 Сейчас в теме
(16)
Однако, в случае, когда количество строк несколько тысяч и ключ для поиска составной - индекс становится полезным.

это вроде "и так понятно".. Об о всем не расскажешь)
Индексировать стоит от 1000 строк Сортировка строк таблиц значений - ИТС в тему)
18. RustIG 1747 22.10.19 17:41 Сейчас в теме
мне понравилось: проблема- задача- идея - реализация...
Оставьте свое сообщение