dem*_*emi 5 sql sqlite android
SQlite,Android,真实故事。我有一张表,用作缓存:
CREATE TABLE cache(key TEXT, ts TIMESTAMP, size INTEGER, data BLOB);
CREATE UNIQUE INDEX by_key ON cache(key);
CREATE INDEX by_ts ON cache(ts);
Run Code Online (Sandbox Code Playgroud)
在应用程序生命周期中,我填充了缓存,在某些时候我想清除它并删除N记录。通常,该表将包含 ~25000 个 blob,每个 blob 约为 100-500Kb,数据库中的 blob 总大小为 600-800Mb,但现在我测试了 ~2000,大约为 60Mb(以下数字适用于这种情况)。清除会删除 90% 的缓存条目。
我尝试了不同的方法来做到这一点,这里简要说明:
[1]最差也最简单。先选择,再一一移除,行走光标。慢得可怕。
[2] 使用SQLite 来做查询(删除其中包含完全N字节的blob ):
DELETE FROM blobs WHERE
ROWID IN (SELECT ROWID FROM blobs WHERE
(SELECT SUM(size) FROM blobs AS _ WHERE ts <= blobs.ts) <= N);
Run Code Online (Sandbox Code Playgroud)
这更快,但仍然非常慢:~15 秒。似乎它也具有二次复杂性。
[3]选择删除位置周围的行(使用平均 blob 大小进行计算)并使用简单WHERE子句删除:
-- Find row after which to delete, let it's time stamp is T0:
SELECT ts FROM cache ORDER BY ts LIMIT 1 OFFSET count;
-- Delete
DELETE FROM cache WHERE ts < T0;
Run Code Online (Sandbox Code Playgroud)
这要好得多,但需要约 7 秒。
[4]创建新表,复制我需要保存并删除旧表。请注意,我在复制所有这些内容后在新表中创建索引:
-- Insert only rows I want leave
INSERT INTO temp(key, ts, size, data) SELECT key, ts, size, data
FROM cache ORDER BY ts LIMIT count;
-- Drop table and indices.
DROP INDEX by_key;
DROP INDEX by_ts;
DROP TABLE cache;
-- Rename temp table and create indices...
Run Code Online (Sandbox Code Playgroud)
对于 6Mb 的 blob,复制需要大约 300 毫秒。但DROP TABLE大约是 8 秒。
请注意,在所有情况下,我都VACUUM需要再花费约 1 秒。我怎样才能让它快?为什么DROP TABLE和删除这么慢?我认为这可能是因为索引:当我在关键索引DELETE工作更快之前删除它。如何让 SQLite 快速删除?
您正在处理一个包含“大”数据的数据库 - 即每个 blob 使用多个页面。
在接近最佳性能的某个时刻,您将达到无法改进的极限。
检查您的所有选择,我看到不同的行为,而不仅仅是不同的算法。
[1] 只要您使用事务,这个速度就不会太慢。您需要同时执行两个操作:查询(以获取 blob 大小)和删除。
[2] 这是一个好方法。由于两个查询和一个删除都在一个命令中,因此 SQLite 引擎将进行优化。
[3] 这是与之前所有行为不同的行为。与...一样DELETE FROM cache WHERE ts < (SELECT ts FROM cache ORDER BY ts LIMIT 1 OFFSET count)。查询比以前便宜,但我敢打赌删除的行数比以前少得多!查询/删除的昂贵部分将被删除!查询优化很重要,但删除总是会变慢。
[4] 这是一个非常糟糕的做法!将所有数据复制到新表(可能是另一个数据库)将非常昂贵。我只能从中得到一个好处:您可以将数据复制到新数据库并避免VACUUM,因为新数据库是从基础构建的并且它是干净的。
关于VACUUM...最糟糕的DELETE是VACUUM。真空不应该在数据库中经常使用。我知道这个算法应该“清理”你的数据库,但清理不应该是一个频繁的操作 - 数据库针对选择/插入/删除/更新进行了优化 - 不要将所有数据保持在最小大小。
我的选择是DELETE ... IN (SELECT ...)根据预定义的标准使用单个操作。VACUUM不会被使用,至少不会经常使用。一个不错的选择是监视数据库大小 - 当该大小超过限制时,运行假定的昂贵清理来修剪数据库。
最后,当使用多个命令时,永远不要忘记使用事务!
| 归档时间: |
|
| 查看次数: |
7483 次 |
| 最近记录: |