背景:
我正在开发一个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)
对于上述查询,我会考虑将fiBlobs从FetchOptions中排除,以节省一些I/O时间,用于当用户在从结果集中读取瓦片时移动地图视图,并且请求的区域在可见视图之外时(您停止读取,永远不会读取其余区域(。