如何在sqlite中的DELETE期间避免锁定?

时间:2014-03-28 22:24:06

标签: performance sqlite cascading-deletes sql-delete

DELETE查询很简单:

DELETE FROM pages WHERE status = 0

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

结构:

  • pages - 只是几条记录
  • files - 大约230K记录,包含ON DELETE CASCADE引用pages
  • 列的外键约束
  • meta - 大约300万条记录,包含引用ON DELETE CASCADEfiles
  • 列的pages的外键约束
  • 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从大量图像中读取)。为什么删除记录比插入慢?

1 个答案:

答案 0 :(得分:2)

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

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

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