【问题标题】:How to avoid locks during DELETE in sqlite?如何在sqlite中的DELETE期间避免锁定?
【发布时间】:2014-05-08 14:03:53
【问题描述】:

DELETE 查询很简单:

DELETE FROM pages WHERE status = 0

大约需要 15 分钟才能完成(删除约 20K 行)。这是一个约 500 MB 的数据库,映射本地文件系统,包含大约 300 万条记录。

结构:

  • pages - 几条记录
  • files - 大约 230K 条记录,包含带有 ON DELETE CASCADE 的外键约束,它引用来自 pages 的列
  • meta - 大约 300 万条记录,包含带有 ON DELETE CASCADE 的外键约束,它引用来自 filespages 的列
  • search - FTS4 表,与 meta 几乎完全相同。该表的完整性由触发器维护。
CREATE TABLE pages(
  id       INTEGER PRIMARY KEY AUTOINCREMENT,
  slug     TEXT,                           
  name     TEXT NOT NULL,
  type     INTEGER NOT NULL DEFAULT 1,     
  data     TEXT,
  parent   INTEGER,                        
  status   INTEGER DEFAULT 1,              
  comments INTEGER DEFAULT 1,              
  priority INTEGER DEFAULT 0,              
  UNIQUE(slug),
  FOREIGN KEY(parent) REFERENCES pages(id) ON DELETE CASCADE   
);

CREATE INDEX "pageParent" ON "pages"("parent");


CREATE TABLE files(
  id        INTEGER PRIMARY KEY AUTOINCREMENT,
  gallery   INTEGER NOT NULL,                      
  type      INTEGER NOT NULL DEFAULT 1,            
  sPath     TEXT,                                  
  rPath     TEXT,                                  
  parent    INTEGER,  
  hero      INTEGER,
  hidden    INTEGER DEFAULT 0,                     
  createdAt DATETIME,                              
  mTime     TEXT,                                  
  UNIQUE(sPath),
  FOREIGN KEY(gallery) REFERENCES pages(id) ON DELETE CASCADE,
  FOREIGN KEY(parent)  REFERENCES files(id) ON DELETE CASCADE,
  FOREIGN KEY(hero)    REFERENCES files(id) ON DELETE SET NULL
);

CREATE INDEX "fileGallery" ON "files"("gallery");
CREATE INDEX "fileType"    ON "files"("type");
CREATE INDEX "fileParent"  ON "files"("parent");
CREATE INDEX "fileRPathNS" ON "files"("rPath" COLLATE NATSORT);


CREATE TABLE thumbs(          
  hash    TEXT,  
  image   INTEGER,
  width   INTEGER,
  height  INTEGER,            
  FOREIGN KEY(image) REFERENCES files(id) ON DELETE CASCADE,
  PRIMARY KEY(hash, image) ON CONFLICT REPLACE
);

CREATE INDEX "thumbImage" ON "thumbs"("image");


CREATE TABLE meta(
  id      INTEGER PRIMARY KEY AUTOINCREMENT,
  file    INTEGER NOT NULL,
  key     TEXT NOT NULL,
  value   TEXT,         
  extra   TEXT,         
  gallery INTEGER,
  FOREIGN KEY(gallery) REFERENCES pages(id) ON DELETE CASCADE,  
  FOREIGN KEY(file) REFERENCES files(id) ON DELETE CASCADE
);

CREATE INDEX "metaFileId" ON "meta"("file"); 
CREATE INDEX "metaKey"    ON "meta"("key"); 
CREATE INDEX "metaExtra"  ON "meta"("extra"); 


CREATE VIRTUAL TABLE search USING fts4(file, key, value, gallery);

CREATE TRIGGER metaBeforeUpd BEFORE UPDATE ON meta BEGIN
  DELETE FROM search WHERE docid = OLD.rowid;
END;

CREATE TRIGGER metaBeforeDel BEFORE DELETE ON meta BEGIN
  DELETE FROM search WHERE docid = OLD.rowid;
END;

CREATE TRIGGER metaAfterUpd AFTER UPDATE ON meta BEGIN
  INSERT INTO search(docid, file, key, value, gallery) VALUES(NEW.rowid, NEW.file, NEW.key, NEW.value, NEW.gallery);
END;

CREATE TRIGGER metaAfterIns AFTER INSERT ON meta BEGIN
  INSERT INTO search(docid, file, key, value, gallery) VALUES(NEW.rowid, NEW.file, NEW.key, NEW.value, NEW.gallery);
END;

问题在于它不仅速度慢,而且还锁定了这些表,所以在此期间我无法对它们进行任何更改。

我尝试了来自this question and answers 的所有建议,但没有显着改进。将日记模式设置为 MEMORY 并关闭同步可以让它运行得更快一些,但风险太大。

为了避免长时间的写锁,我尝试逐步删除记录,一次删除 40 条记录,中间有 0.5 秒的延迟。但这会使整个过程减慢 10 倍

还有其他方法可以提高速度和/或避免锁定吗?


PS:让我感到困惑的是 INSERT 的速度要快得多。插入我要删除的记录数量需要 2 分钟,并且该时间包括一些繁重的文件处理(Exif 从大量图像中读取)。为什么删除记录比插入慢?

【问题讨论】:

  • 建议,使用软删除而不是硬删除。缺点是代码更改,可能会增加存储量。
  • 数据库架构(包括索引)?
  • @CL:the schema,但我不得不注释掉索引和 fts 表,因为它似乎不适用于 sqlfiddle。通过软删除,您的意思是使用表明不应在 SELECT 查询中考虑记录的值更新记录?这就是我正在做的事情,但我仍然想在后台进程中删除死记录。问题是该进程锁定了一些表,并且它所做的事情花费了太长时间

标签: performance sqlite cascading-deletes sql-delete


【解决方案1】:

pages 删除很慢,因为meta 表中的gallery 列上没有索引。 每当pages 记录被实际删除时,数据库必须搜索任何匹配 ON DELETE CASCADE 约束的meta 记录;这会导致对每条已删除记录进行全表扫描。

(插入更快,因为不必进行此类检查。)

SQLite 不是为并发设计的;不可能同时拥有多个作家。 但是,要允许多个读者同时作为一个作者,请考虑启用write-ahead logging

【讨论】:

  • 我认为它会使用metaFileId 索引。无论如何,在为图库列添加索引之前,我做了一个“解释查询计划”,它确实在对 meta 进行扫描。现在 EXPLAIN QUERY PLAN 告诉我正在使用新索引,但是查询和以前一样慢:(
  • 好的,问题是hero 列缺少索引,它也有一个约束:) 向meta.gallery 列添加索引并没有太大区别,因为它只做一个扫描每个已删除的页面记录,这对我的数据库来说就像 +2 秒,我可以忍受。但你是对的,谢谢你的帮助!我希望 EXPLAIN QUERY PLAIN 能提供更多信息,例如对级联删除的搜索/扫描操作...
猜你喜欢
  • 2015-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
相关资源
最近更新 更多