2014-03-28 106 views
1

刪除查詢很簡單:如何避免在sqlite 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秒的延遲。但是這會減慢整個過程甚至10x

有沒有其他方法可以提高速度和/或避免鎖定? PS:讓我感到困惑的是INSERTs要快得多。插入我正在刪除的記錄數量需要2分鐘,並且該時間包括一些繁重的文件處理(Exif從大量圖像中讀取)。爲什麼刪除記錄比插入慢?

+0

建議,使用軟刪除而不是硬刪除。下行是代碼變化,可能會增加存儲量。 – Rippo

+0

數據庫模式(包括索引)? –

+0

@CL:[schema](http://sqlfiddle.com/#!5/39c70),但我不得不註釋一個索引和fts表,因爲它似乎不能在sqlfiddle上工作。通過軟刪除,你的意思是更新記錄的值,這表明記錄不應該在SELECT查詢中考慮?這就是我正在做的,但我仍然想要在後臺進程中刪除死亡記錄。問題是這個進程鎖定了一些表,並且它花費的時間太長了 –

回答

2

pages刪除很慢,因爲meta表中的gallery列沒有索引。 每當pages記錄被實際刪除時,數據庫必須搜索匹配ON DELETE CASCADE約束條件的任何meta記錄;這會導致每個已刪除記錄的全表掃描。

(插入件更快,因爲沒有這樣的必須進行檢查。)

SQLite是不是設計用於併發;永遠不可能同時擁有多個作家。 但是,要允許多個閱讀器同時作爲作者,請考慮啓用write-ahead logging

+0

我認爲它會使用'metaFileId'索引。無論如何,我在爲圖庫列添加索引之前做了一個「解釋查詢計劃」,它確實正在對meta進行掃描。現在解釋查詢計劃告訴我,這是使用新的索引,但查詢速度與之前一樣緩慢:( –

+0

確定問題是英雄列缺少索引,它也有一個約束:)將索引添加到'meta.gallery'列沒有太大區別,因爲它只對每個刪除的頁面記錄執行一次掃描,就像我的db的+2秒一樣,我可以忍受這一點。但你是對的,感謝你的幫助!我希望解釋QUERY PLAIN會提供更多信息,例如級聯刪除的搜索/掃描操作... –