Каков наиболее эффективный способ считывания данных плитки (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 для сохранения некоторого времени ввода-вывода для случаев, когда пользователь перемещает представление карты, пока вы читаете плитки из набора результатов, а запрошенная область находится вне видимого представления (вы прекрати читать и никогда не читай остальных).

Другие вопросы по тегам