SQLite:删除大量行的有效方法

时间:2013-10-23 00:00:31

标签: android sql sqlite

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);

在应用程序生命周期中,我填充缓存,在某些时候我想清除它并删除N条记录。通常这个表将包含~25000个blob~100-500Kb,DB中的总blob大小为600-800Mb,但是现在我测试〜2000,大约是60Mb(以下数字适用于这种情况)。 Clear删除了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);

速度更快,但速度非常慢:约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;

这要好得多,但需要约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...

对于blob,复制需要约300毫秒的6Mb。但是DROP TABLE约为8秒。

请注意,在所有情况下,我VACUUM需要另外约1秒。我怎样才能快速完成?为什么DROP TABLE和删除速度如此之慢?我认为这可能是因为索引:当我在DELETE之前删除密钥索引时,它工作得更快。如何快速删除SQLite?

3 个答案:

答案 0 :(得分:3)

您正在使用“大”数据处理数据库 - 即每个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 ...最差DELETEVACUUM。真空不应该经常在数据库中使用。我知道这个算法应该“清理”你的数据库,但是清理不应该是一个频繁的操作 - 数据库针对select / insert / delete / update进行了优化 - 而不是将所有数据保持在最小的大小。

根据预定义的标准,我的选择是使用DELETE ... IN (SELECT ...)单个操作。不会使用VACUUM,至少不会这么常用。一个不错的选择是监视器数据库大小 - 当此大小超过限制时,运行假定的昂贵清理来修剪数据库。

最后,当使用多个命令时,永远不要忘记使用事务!

答案 1 :(得分:3)

显然,缓慢的是找不到要删除的记录,而是实际的删除本身。

检查Android的SQLite中是否默认设置了PRAGMA secure_delete。 你应该禁用它,只是为了确定。

您无需运行VACUUM; SQLite自动重用已释放的页面。 只有当实际知道数据库将来不会再次增长时,您才需要VACUUM

答案 2 :(得分:-1)

您有两种方法可以改善效果,尤其是第一种:

1)使用这样的交易:

DbTransaction trans = conn.BeginTransaction(); // <-------------------
try 
{
   Any code to delete the items
}
catch
{
    trans.Rollback(); // <-------------------
    throw; // <-------------------
}

2)否则,假设项目是连续的,那么

  • a)获取第一个项目的ID;

  • b)获取要删除的项目总数

  • c)使用如下命令:

    DELETE FROM blobs WHERE ID > fistId LIMIT count;

祝你好运。

相关问题