使用AnyDAC(FireDAC)从SQLite表读取磁贴数据(MBTiles)的最有效方法是什么



背景:

我正在开发一个SQLite tile缓存数据库(类似于MBTiles规范(,目前仅由具有以下列的单个表Tiles组成:

X [INTEGER]-水平平铺索引(非地图坐标(
Y [INTEGER]-垂直平铺索引(非地图坐标(
Z [INTEGER]-磁贴的缩放级别
Data [BLOB]-具有平铺图像数据的流(当前为PNG图像(

所有的coords-to-tile计算都是在应用程序中完成的,所以SQLite R*Tree Module和相应的TADSQLiteRTree类对我来说没有意义。我只需要尽快加载由给定X, Y, Z值找到的记录的Data字段blob流。

除了这个数据库之外,应用程序还将有一个内存缓存,由类似TTileCache类型的哈希表实现:

type
  TTileIdent = record
    X: Integer;
    Y: Integer;
    Z: Integer;
  end;    
  TTileData = TMemoryStream;    
  TTileCache = TDictionary<TTileIdent, TTileData>;

当在计算X, Y, Z值的同时请求某个瓦片时,工作流程将很简单。我会要求一个磁贴-内存缓存(在app.startup上从上表中部分填充(,如果在那里找不到磁贴,请询问数据库(即使找不到该磁贴,也可以从磁贴服务器下载(。

问题:

您会使用哪个AnyDAC(FireDAC(组件频繁查询SQLite表中的3个整列值(假设有100k条记录(,并可选加载找到的blob流?

你会用吗

  • 查询类型组件(我认为执行同一个准备好的查询可能会很高效,不是吗?(
  • 内存表(我担心它的大小,因为tiles表中可能存储了几个GB,或者它是以某种方式流式传输的?(
  • 有什么不同吗

一定要使用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)

对于上述查询,我会考虑将fiBlobsFetchOptions中排除,以节省一些I/O时间,用于当用户在从结果集中读取瓦片时移动地图视图,并且请求的区域在可见视图之外时(您停止读取,永远不会读取其余区域(。

最新更新