Description
My SELFOSS database has ~500 sources and ~330k items, the oldest from Jan 2016.
Love Selfoss thanks a lot for it !
Database Version : mariadb 10.3.27
PHP Version : PHP7.0 (yes, I know)
OS : Debian 11 Bullseye
My problem: SELFOSS keeps slowing down when loading items.
I have activated the MySQL Log, and the query executed on each items load is responsible.
Here is the main query :
SELECT
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tagsitems.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
FROM items AS items, sources AS sources
WHERE items.source=sources.id AND TRUE AND unread=1
ORDER BY items.datetime DESC, items.id DESC
LIMIT 50 OFFSET 0;
- - -
50 rows in set (6.440 sec)
Pretty slow.
Running the EXPLAIN plan for that query tells us more:
EXPLAIN SELECT
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tagsitems.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
FROM items AS items, sources AS sources
WHERE items.source=sources.id AND TRUE AND unread=1
ORDER BY items.datetime DESC, items.id DESC
LIMIT 50 OFFSET 0;
+------+-------------+---------+------+---------------+--------+---------+--------------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+--------+---------+--------------------------+------+---------------------------------+
| 1 | SIMPLE | sources | ALL | PRIMARY | NULL | NULL | NULL | 489 | Using temporary; Using filesort |
| 1 | SIMPLE | items | ref | source | source | 4 | alban_selfoss.sources.id | 349 | Using where |
+------+-------------+---------+------+---------------+--------+---------+--------------------------+------+---------------------------------+
The query shows use of temporary table and filesort, which is slow.
Let's see how ORDER BY clauses are responsible for that by removing them:
SELECT
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
FROM items AS items, sources AS sources
WHERE items.source=sources.id AND TRUE AND unread=1
LIMIT 50 OFFSET 0;
- - -
50 rows in set (0.001 sec)
EXPLAIN SELECT
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags
FROM items AS items, sources AS sources
WHERE items.source=sources.id AND TRUE AND unread=1
LIMIT 50 OFFSET 0;
+------+-------------+---------+------+---------------+--------+---------+--------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+--------+---------+--------------------------+------+-------------+
| 1 | SIMPLE | sources | ALL | PRIMARY | NULL | NULL | NULL | 489 | |
| 1 | SIMPLE | items | ref | source | source | 4 | alban_selfoss.sources.id | 349 | Using where |
+------+-------------+---------+------+---------------+--------+---------+--------------------------+------+-------------+
Removing ORDER BY results in in 0.01 second query and no more temp table or filesort.
Solutions
It seems this is due to MySQL's handling of queries using ORDER BY cf. documentation.
A possible fix would be to split the query in 2 parts and let PHP associate items with sources.
SELECT
items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author
FROM items AS items
WHERE unread=1
ORDER BY items.datetime DESC, items.id DESC
LIMIT 50 OFFSET 0;
50 rows in set (0.001 sec)
SELECT sources.title as sourcetitle, sources.tags as tags
FROM sources AS sources;
489 rows in set (0.002 sec)