将 sqlite3 blob 列非规范化为主表是否有存储和性能增益,但仅在某些情况下将其视为外键?我有两个实现,而且似乎都运行得更慢。是否有一些 sqlite3 内部结构排除了这种用法?
我有一个~100GB 的 sqlite 文件,里面有两个表。第一个将 z,x,y 坐标映射到一个 ID——一个 32 字符的十六进制字符串,存储为TEXT
. 第二个表将该 ID 映射到一个 BLOB,通常为几千字节。(z,x,y) 和 ID 都有唯一索引。有一个VIEW
连接两个表。
对于约 30% 的坐标,BLOB 在每个坐标组合中都是唯一的。其余的引用相同的约 100 个频繁出现的 BLOB。
我想优化空间和性能:将唯一的 BLOB 移动到第一个表中,并将第二个表仅保留为少数共享 BLOB 的 100 行小查找。可以在运行时检查第一个表的 blob——如果它正好是散列键的大小,则将其视为查找。否则,将其视为价值。
我的想法是,这通常会避免查找大的第二个表,将小的查找表完全保存在缓存中,并避免存储大多数 blob 的键。我的性能测试没有证实这个理论,我不明白为什么。
原始实现:
CREATE TABLE map (z INTEGER, x INTEGER, y INTEGER, id TEXT);
CREATE TABLE blobs (id TEXT, data BLOB);
CREATE VIEW tiles AS
SELECT z, x, y, data FROM map JOIN blobs ON blobs.id = map.id;
CREATE UNIQUE INDEX map_index ON map (z, x, y);
CREATE UNIQUE INDEX blobs_id ON blobs (id);
优化实现将映射表中的 ID 列从 更改id TEXT
为mix BLOB
。
CREATE TABLE map (z INTEGER, x INTEGER, y INTEGER, mix BLOB);
我尝试了两个 VIEW 实现,它们都比上面的 INNER JOIN 方法运行慢约 10%。LEFT JOIN
方法:
CREATE VIEW tiles AS
SELECT z, x, y,
COALESCE(blobs.data, map.mix) AS data
FROM map LEFT JOIN blobs ON LENGTH(map.mix) = 32 AND map.mix = blobs.id;
我尝试了子查询方法:
CREATE VIEW tiles AS
SELECT z, x, y,
CASE
WHEN LENGTH(map.mix) = 32 THEN
(SELECT COALESCE(blobs.data, map.mix) FROM blobs WHERE map.mix = blobs.id)
ELSE map.mix
END AS data
FROM map;
PSCOALESCE()
确保如果我的数据长度恰好是 32,但它不是外键,则查询应该按原样返回数据。
PPS 这是一个带有地图瓦片的mbtiles文件,重复的瓦片代表空旷的水和土地,而独特的瓦片代表具有一些独特特征的地方,如城市街道)