Кеширование запроса SQLite при старте системы...

Автор MWW_Ruza, 04 марта 2026, 08:41

« назад - далее »

MWW_Ruza

Добрый день!
Вопрос конечно к Djelf, но его с нами нет, и как это ни прискорбно, но судя по: https://forum.dorex.pro/index.php?topic=326.0 уже и не будет  >:(
Уже давно, при старте системы, с его подачи, использую такую процедуру:

Функция СписокГрупп(Сорт = 0) Экспорт
	Перем Запрос;
	Если ПустоеЗначение(БазаДанных) = 1 Тогда
		БазаДанных 	= СоздатьОбъект("SQLiteBase");
	КонецЕсли;	
	Если БазаДанных.Открыта() = 0 Тогда
		БазаДанных.Открыть(":memory:");
	КонецЕсли;
	Запрос 			= БазаДанных.НовыйЗапрос();
	ТекстЗапроса	= "SELECT id [Гр:Справочник.Номенклатура] FROM [Справочник.Номенклатура] WHERE isfolder = 1";
	Если Сорт = 1 Тогда
		ТекстЗапроса = ТекстЗапроса + " ORDER BY DESCR";
	ИначеЕсли Сорт = 2 Тогда
		ТекстЗапроса = ТекстЗапроса + " ORDER BY CODE";	
	КонецЕсли;
	Попытка
		СЗ 		= СоздатьОбъект("СписокЗначений");
		Рез		= Запрос.ВыполнитьЗапрос(ТекстЗапроса, СЗ);
		Запрос	= 0;
		Возврат Рез;
	Исключение
		Сообщить(ОписаниеОшибки());
		Возврат 0;
	КонецПопытки; 
КонецФункции

Процедура КешироватьЗапрос() Экспорт
	Состояние("Построение индекса полнотекстового поиска");
	Если ПустоеЗначение(БазаДанных) = 1 Тогда
		БазаДанных 	= СоздатьОбъект("SQLiteBase");
	КонецЕсли;	
	Если БазаДанных.Открыта() = 0 Тогда
		БазаДанных.Открыть(":memory:");
	КонецЕсли;	
	БазаДанных.НовыйЗапрос().ВыполнитьЗапрос("DROP TABLE IF EXISTS tri;");
	БазаДанных.НовыйЗапрос().ВыполнитьЗапрос("CREATE VIRTUAL TABLE tri USING fts5(ID UNINDEXED, DESCR, ismark, VERSTAMP UNINDEXED, tokenize='trigram');");
	БазаДанных.НовыйЗапрос().ВыполнитьЗапрос("
	|INSERT INTO tri(ROWID,ID,DESCR, ismark, VERSTAMP)
	|SELECT
	|    Номенклатура.ROWID,
	|    Номенклатура.ID,
	|    Номенклатура.DESCR,
	|	 Номенклатура.ismark,
	|    Номенклатура.VERSTAMP
	|FROM Справочник_Номенклатура AS Номенклатура
	|WHERE Номенклатура.isfolder=2 and Номенклатура.ismark <> '*'
	|"); 
	КэшФТС = 1;
	
	СписокГрупп(); // Он нам тут не нужен, но, что-бы тоже закешировался
	
	Состояние("");
КонецПроцедуры

В общем-то, работает отлично, значительно ускоряет "быстрый поиск" товара в справочнике.
Отрабатывает при старте - мгновенно, по крайней мере не заметно.

Магазинчики маленькие, в основном работают по одному пользователю, локально.
Но, подключил в одном магазине второго пользователя по сети - и вылезла проблема...
При запуске программа долго делает этот запрос по сети(файловая база, сетка обычная проводная одноранговая).
При запуске около минуты висит месага: "Построение индекса полнотекстового поиска"... Потом, когда наконец запустится - все работает идеально быстро...
Я конечно понимаю, что решением будет RDP... Но, как-то не хочется...
Может есть какие-то варианты, что еще "подкрутить" в этом запросе, что-бы минимизировать этот эффект? Или как-то запихнуть его в отдельный поток, что-бы он отрабатывал "незаметно" и не тормозил загрузку?
Никто не сталкивался с чем-то подобным?

MWW_Ruza

Цитата: MWW_Ruza от 04 марта 2026, 08:41Или как-то запихнуть его в отдельный поток, что-бы он отрабатывал "незаметно" и не тормозил загрузку?

Через обработку ожидания например? Но, тут, как я понимаю, все равно, когда обработка ожидания сработает, 1Ска "задумается" до завершения запроса... В фон таким образом ее не получится запрятать...

Ветер в поле

Я в таких случаях использую решение от Вирт - V7DBNet 2.5. Вот описание технологии:

Клиентская часть разработки перехватывает обращения к файлам DBF и CDX и
передает запросы на сервер, сервер возвращает результат в виде кэш-страниц.  Далее
клиент работает с такой страницей без обращения к серверу, пока сервер не сообщит о
потере  ее актуальности.
Для обеспечения надежности (начиная с версии 2.0.0.1) каждый сеанс работает с
сервером   через   изолированную   транзакцию,   что   помимо   стабильности   обеспечивает
полную параллельность доступа  по чтению, даже если в данный момент происходит
запись в таблицы.
Запись происходит также в отдельной изолированной транзакции, но поочередно.
Клиент подает запрос серверу на запись, сервер добавляет его в очередь ожидания, если
кто-то уже пишет данные, или сразу дает разрешение. Сервер регулирует очередность
записи, разделяя время между претендентами, сводя к минимуму «Ошибку блокировки
транзакции».   Также,   для   исключения   неудачного   захвата   транзакции,   в   клиенте,
стандартный   вопрос   1С   о   невозможности   захвата   транзакции   заменен   на   вопрос   с
автоповтором попытки через 3 секунды.
Когда  клиенту  разрешена   запись,   он отправляет  на  сервер  модифицированные
данные. Если клиент сообщает, что он завершает запись успешно, и других измененных
данных не будет, сервер отражает измененные  данные в файлах и фиксирует совою
транзакцию. В случае неудачного завершения транзакции, она просто удаляется, ни каких
изменений не происходит.
Сервер поддерживает три типа транзакций: оптимистический, пессимистический и
режим прямого доступа. Начиная с версии 1.0.0.2. клиент может выбрать тип транзакции
необходимый ему в текущий момент.


Я использовал это решение в нескольких местах в течении многих лет и каких-то проблем не фиксировал. Скорость, конечно, меньше чем у RDP, но работать вполне комфортно, примерно на уровне SQL. Я использовал кэш в районе 100 МБ, поэтому большинство часто используемых справочников кэшировались.
Автор в последней версии разрешил бесплатное использование. Могу расшарить инсталляшку.


Ветер в поле

Цитата: item от Вчера в 01:00https://forum.dorex.pro/index.php?topic=287.0

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

MWW_Ruza

Вот тоже интересно:

Цитата: Arbuz от 08 сентября 2025, 15:41
Цитата: Djelf от 29 августа 2025, 15:223. Можно включить монопольный режим доступа в sqlite
Это что имеется в виду?

Как это сделать? Для моей задачи это более чем выход из положения был-бы... Одновременных обращений к SQLite от других пользователей в момент кеширования не будет, в виду того, что пользователей "кот наплакал", всего два в моем случае...

Злоп

Цитата: MWW_Ruza от 04 марта 2026, 08:41СписокГрупп
А для чего СписокГрупп() ...?
Посмотрел - просто возвращает плоский список групп.

Ветер в поле

Цитата: MWW_Ruza от Вчера в 14:53Как это сделать? Для моей задачи это более чем выход из положения был-бы... Одновременных обращений к SQLite от других пользователей в момент кеширования не будет, в виду того, что пользователей "кот наплакал", всего два в моем случае...

Нашел в документации SQLite:
У объекта SQLiteQuery, который создается методом SQLiteBase::НовыйЗапрос есть свойство ВыполнятьВТранзакции / NeedTransaction: 1 - автоматически начинать транзакцию 1С при выполнении запроса в момент первого обращения к таблицам 1С, 0 - не начинать.

MWW_Ruza

Цитата: Злоп от Вчера в 20:30А для чего СписокГрупп() ...?

Да, именно так. Этот список мне нужен для создания нового элемента справочника Номенклатура, из автоматических обработок например, "Сопоставление или создание номенклатуры из ЭДО" или "Создание номенклатуры по ШтрихКоду из интернет-базы ОлегОн". Для выбора - типа, куда поместить создаваемый эжлемент.
Без запроса, тупым перебором справочника, это отрабатывает дольше. Так комфортнее.

MWW_Ruza

Цитата: Ветер в поле от Вчера в 21:44есть свойство ВыполнятьВТранзакции / NeedTransaction: 1 - автоматически начинать транзакцию 1С при выполнении запроса в момент первого обращения к таблицам 1С, 0 - не начинать.

Хм... Интересно, надо попробовать, хотя, как-то прямого смысла не улавливаю - если-бы я менял что-то в запросе, то запись порциями в транзакциях должна была бы ускорить... Но, тут я просто получаю данные... При чем тут транзакция?
Но, все равно попробую - х.з., какие там внутренние заморочки, может как-то повлияет.