Режим работы TpFIBDataSet "Ограниченный локальный буфер"
Режим работы впервые был предложен в компонентах gb_Datasets от Сергея Спирина. Теперь аналогичная функциональность реализована непосредственно в коде FIBPlus. Особенность данного режима состоит в том, что при навигации по TpFIBDataSet не будет происходить полное скачивание всех записей, которые возвращаются запросом. Фактически, реализуется имитация непосредственного доступа к произвольным записям за счет выполнения дополнительных запросов. Технология накладывает ряд требований на запрос. В частности, необходимым требованием является использование ORDER BY в SelectSQL. Причем, комбинация значений полей, входящих в выражение ORDER BY, должна быть уникальной. Во-вторых, для реального быстродействия желательно иметь два индекса - по возрастанию и по убыванию - для данной комбинации полей.
Технологию можно пояснить следующим простым примером. Имея запрос в SelectSQL:
· SELECT * FROM TABLE · ORDER BY FIELD |
мы можем получить несколько первых записей, делая последовательный fetch. Если мы хотим сразу посмотреть последние записи, то вместо полного выкачивания на клиента всех записей данного запроса, можно выполнить дополнительный запрос с обратной сортировкой:
· SELECT * FROM TABLE · ORDER BY FIELD DESC |
Очевидно, что последовательный fetch нескольких записей вернет нам последние записи относительно первоначального запроса. Аналогичные запросы выполняются для точного позиционирования на произвольную запись, а также на записи выше и ниже текущей:
· SELECT * FROM TABLE · WHERE (FIELD = x) |
· SELECT * FROM TABLE · WHERE (FIELD < x) · ORDER BY FIELD DESC
|
· SELECT * FROM TABLE · WHERE (FIELD > x) · ORDER BY FIELD
|
Для реализации этой технологии в TpFIBDataSet добавлено новое свойство:
· property CacheModelOptions:TCacheModelOptions, где · · TCacheModelOptions = class(TPersistent) · property BufferChunks: Integer ; · property CacheModelKind: TCacheModelKind ; · property PlanForDescSQLs: string ;
· end;
|
BufferChunks в данном случае заменяет существующее свойство BufferChunks у TpFIBDataSet. Тип TCacheModelKind может принимать значения cmkStandard, для использования стандартной работы локального буфера, и cmkLimitedBufferSize, для использования новой технологии ограниченного локального буфера. Размер буфера - это количество записей, указанных в свойстве BufferChunks.
Свойство PlanForDescSQLs позволяет указать отдельный план для запросов с обратной сортировкой.
Необходимо отметить, что при использовании технологии ограниченного локального буфера:
· нельзя включать режим CachedUpdate;
· свойство RecNo будет возвращать неправильные значения;
· локальная фильтрация не поддерживается;
· работа с BLOB-полями в текущей версии не гарантируется;
· в PrepareOptions необходимо включать опцию psGetOrderInfo.
Кэширование BLOB-полей на клиенте
Поддержка BLOB-полей на уровне InterBase и Firebird API во многом определила возникновение и реализацию данной идеи. При выполнении любого запроса SELECT с участием BLOB-полей, клиентское приложение на самом деле получает на реальные значения этих полей, а лишь BLOB_ID. В дальнейшем, если приложение явным образом пытается прочитать значение конкретного BLOB-поля, сервер возвращает значение по BLOB_ID. То есть, фактически, обработка BLOB-полей уже отделена от работы с полями других типов. Делается это, поскольку в большинстве случаев, размеры BLOB-полей достаточно велики, и без особой нужды пересылать такие объемы по сети не имеет смысла.
Дальнейшее развитие этого механизма позволяет вообще избавиться от пересылки BLOB-полей, которые уже были получены приложением в предыдущих сеансах работы с сервером и с тех пор не изменялись. Основной реализации служит тот факт, что при изменении значения BLOB-поля меняется также и его BLOB_ID. Следовательно, приложению достаточно просто сравнить «старый» и «новый» BLOB_ID, чтобы определить необходимость получения BLOB-поля из базы данных. Полученные BLOB-поля сохраняются на локальном диске в указанном каталоге и при необходимости берутся оттуда, а не закачиваются с сервера.
В TpFIBDatabase добавлено новое свойство BlobSwapSupport: TBlobSwapSupport; где
TBlobSwapSupport = class(TPersistent) property Active: boolean default False;
property SwapDir: string; property MinBlobSizeToSwap: integer default 0; end; |
При включении BlobSwapSupport.Active := True, FIBPlus автоматически будет сохранять полученные BLOB-поля в указанном каталоге (свойство SwapDir). По умолчанию свойство SwapDir принимает значение равное {APP_PATH}, то есть, равное каталогу, в котором находится исполняемое приложение. При необходимости, можно указать также подкаталог, в котором будут сохраняться BLOB-поля. Например, SwapDir := '{APP_PATH}' + '\BLOB_FILES\'
Помимо нового свойства в TpFIBDatabase было добавлено 4 новых события:
property BeforeSaveBlobToSwap: TBeforeSaveBlobToSwap; property AfterSaveBlobToSwap: TAfterSaveLoadBlobSwap; property AfterLoadBlobFromSwap: TAfterSaveLoadBlobSwap; property BeforeLoadBlobFromSwap: TBeforeLoadBlobFromSwap; |
где
- TBeforeSaveBlobToSwap = procedure(const TableName, FieldName: string; RecordKeyValues: array of variant; Stream: TStream; var FileName: string; var CanSave: boolean) of object;
- TAfterSaveLoadBlobSwap = procedure(const TableName, FieldName: string; RecordKeyValues: array of variant; const FileName: string) of object;
- TBeforeLoadBlobFromSwap = procedure(const TableName, FieldName: string; RecordKeyValues: array of variant; var FileName: string; var CanLoad: boolean) of object;
Обработчики служат для более гибкого управления процессом сохранения и чтения BLOB-полей с диска. В частности, в обработчиках перед сохранением BLOB-поля можно запретить сохранение конкретного BLOB-поля в зависимости от имени таблицы и поля, значений других полей записи, свободного места на диске и т.д. Регулировать процесс сохранения можно и при помощи свойства MinBlobSizeToSwap, в котором можно задать минимальный размер BLOB-полей, которые будут сохраняться на диске.
Технология имеет ряд ограничений:
Таблица должна иметь первичный ключ.
Чтение BLOB-полей должна производиться компонентом TpFIBDataSet.
Приложение само должно следить за свободным местом на диске. В частности, такую функцию можно реализовать в обработчике события BeforeSaveBlobToSwap.
К сожалению, после backup/restore базы данных внутри базы данных происходит замена всех BLOB_ID, а потому локальный кэш теряет актуальность и автоматически вычищается. При очередном подключении к базе данных запускается специальный трэд, который в отдельном подключении проверяет весь кэш на диске на наличие соответствующих BLOB-полей в базе данных. Для отсутствующих BLOB-полей файлы сразу же удаляются.