Каков наиболее эффективный способ считывания данных плитки (MBTiles) из таблицы SQLite с использованием AnyDAC (FireDAC)?
Фон:
Я работаю над базой данных кэша плиток SQLite (аналогично MBTiles
спецификация), состоящий пока только из одной таблицы Tiles
со следующими столбцами:
X [INTEGER]
- индекс горизонтальной плитки (не координата карты) Y [INTEGER]
- вертикальный индекс плитки (не координата карты) Z [INTEGER]
- уровень масштабирования плитки Data [BLOB]
- поток с данными изображения плитки (в настоящее время изображения PNG)
Все координаты для расчета листов выполняются в приложении, поэтому SQLite R*Tree Module
с соответствующим TADSQLiteRTree
класс не имеет для меня значения. Все, что мне нужно, это загрузить Data
поток полевых объектов записи, найденной данным X, Y, Z
значения как можно быстрее.
Приложение, кроме этой базы данных, также будет иметь кэш памяти, реализованный с помощью такой хеш-таблицы TTileCache
тип:
type
TTileIdent = record
X: Integer;
Y: Integer;
Z: Integer;
end;
TTileData = TMemoryStream;
TTileCache = TDictionary<TTileIdent, TTileData>;
Рабочий процесс при запросе определенной плитки при наличии X, Y, Z
рассчитанные значения будут простыми. Я буду запрашивать плитку в кеше памяти (частично заполненную из приведенной выше таблицы при запуске приложения), и если плитка там не будет найдена, спросите базу данных (и даже если плитки не будет найдено, загрузите это с сервера плиток).
Вопрос:
Какой компонент (ы) AnyDAC (FireDAC) вы бы использовали для частого запроса 3-х целочисленных значений столбца в таблице SQLite (с, скажем, 100k записями) с необязательной загрузкой найденного потока больших двоичных объектов?
Вы бы использовали:
- компонент типа запроса (я бы сказал, что выполнение одного и того же подготовленного запроса может быть эффективным, не так ли?)
- таблица памяти (я боюсь ее размера, поскольку в таблице плиток может храниться несколько ГБ, или, например, она как-то потоковая?)
- что-то другое?
2 ответа
Обязательно используйте TADQuery
, Если вы не установите запрос в Unidirectional
, он буферизует все записи, возвращенные из базы данных в памяти (по умолчанию 50). Поскольку вы имеете дело с BLOB-объектами, ваш запрос должен быть написан так, чтобы получить минимальное количество записей, которое вам нужно.
Используйте параметризованный запрос, например, следующий запрос
SELECT * FROM ATable
WHERE X = :X AND Y = :Y AND Z = :Z
После того, как вы первоначально открыли запрос, вы можете изменить параметры, а затем использовать Refresh
метод для получения следующей записи.
Таблица памяти не может быть использована для извлечения данных из базы данных, она должна быть заполнена с помощью запроса. Это может быть использовано для замены вашего TTileCache
записей, но я бы не советовал, потому что это будет иметь больше накладных расходов, чем ваша реализация кэша памяти.
Я хотел бы использовать TFDQuery с запросом, как показано ниже. Предполагая, что вы собираетесь отображать извлеченные плитки на карте, вы можете рассмотреть выборку всех плиток для отсутствующей (не кэшированной) области плитки сразу, не выбирая плитки всегда одну за другой для вашей сетки плиток:
SELECT
X,
Y,
Data
FROM
Tiles
WHERE
(X BETWEEN :HorzMin AND :HorzMax) AND
(Y BETWEEN :VertMin AND :VertMax) AND
(Z = :Zoom)
Для вышеупомянутого запроса я хотел бы рассмотреть исключение fiBlobs из FetchOptions для сохранения некоторого времени ввода-вывода для случаев, когда пользователь перемещает представление карты, пока вы читаете плитки из набора результатов, а запрошенная область находится вне видимого представления (вы прекрати читать и никогда не читай остальных).