The DELETE query is simple:
DELETE FROM pages WHERE status = 0
It takes around 15 minutes to complete (for removing ~20K rows). It's a ~500 MB database that maps a local files system, and contains around 3 million records.
The structure:
pages
- just a few recordsfiles
- around 230K records, contains a foreign key constraint withON DELETE CASCADE
that references a column frompages
meta
- around 3 million records, contains foreign key constraints withON DELETE CASCADE
that reference columns fromfiles
andpages
search
- a FTS4 table, almost exact duplicate ofmeta
. The integrity of this table is maintained with triggers.
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;
The problem is that not only it's slow, but it also locks these tables so I cannot do any changes to them during this time.
I tried every suggestion from this question and answers but without significant improvements. Setting journaling mode to MEMORY and turning off sync made it run a little faster, but it's too risky.
To avoid long write locks, I tried deleting records step by step, 40 at time with a 0.5s delay between. But this slows down the entire process even by 10x
Is there any other way I can improve the speed and/or avoid locks?
PS: What baffles me is that INSERTs are much faster. It takes 2 minutes to insert the amount of records I was deleting, and that time includes some heavy file processing (Exif reading from lots of images). Why is removing records slower than inserting?