Skip to content

Mein Blog ist (nicht mehr) langsam

Langsame MySQL-Abfragen aus dem letzten Monat; das vergangene Jahr sieht exakt genauso aus.

Seit Jahren - und nicht nur im übertragenen Sinne, sondern tatsächlich seit ungefähr 2005 - verwundert und stört es mich, daß insbesondere die Startseite dieses meines Blogs hier sehr langsam lädt; "langsam" im Sinne von "der Aufruf dauert oft mehrere Sekunden". Ich habe abwechselnd das teilweise tabellenbasierte Layout, teilweise die Datenbankabfragen verantwortlich gemacht, bin aber nie so recht zu einer Lösung gelangt, obwohl ich schonmal die Vermutung hatte, daß möglicherweise die Tabellenstruktur von Serendipity - dem besten Blog der Welt - etwas durcheinandergeraten ist oder irgendwo noch ein Index fehlt. Solche und ähnliche Probleme tauchten vor Jahren schon einmal auf; entweder fehlte da etwas in einer uralten Version, oder ich habe mal einen Fehler bei der Installation gemacht; und da ich das Blog seitdem immer durch Neuinstallation gefolgt von einem Ersetzen der Datenbank durch die Sicherungskopie der alten Datenbank umziehe, bleiben solche Fehler einfach erhalten.

Wie ich dieser Tage beim Durchgehen alter Newsgroupbeiträge feststellte, habe ich das Thema Ende 2006 (!) schon einmal in szaf.* angesprochen, den Tip erhalten, doch einfach mal im MySQL-Daemon das Logging von "slow queries" zu aktivieren. Schon im Februar vergangenen Jahres habe ich diesen Rat dann auch umgesetzt und erfahren, daß ich mir dann diese Queries einfach mal angucken und dann mittels EXPLAIN näher untersuchen soll. Aber dann kamen andere Aufgaben dazwischen, ich hatte - wie üblich - nicht ausreichend Zeit, und das Thema geriet schon wieder in Vergessenheit.

Dieser Tage stieß ich allerdings mal wieder auf das noch getaggte Posting und dachte mir, vielleicht ist es ja gar nicht so aufwendig, wie es klingt. Also warf ich einen kurzen Blick in das Log und stellte fest, daß der Übeltäter mit Aufrufzeiten von 3-6 Sekunden der folgende SELECT-Aufruf war:

SELECT e.isdraft AS orderkey, ep_cache_extended.value AS ep_cache_extended, ep_cache_body.value AS ep_cache_body,
       e.id,
       e.title,
       e.timestamp,
       e.comments,
       e.exflag,
       e.authorid,
       e.trackbacks,
       e.isdraft,
       e.allow_comments,
       e.last_modified,
       a.realname AS author,
       a.username AS loginname,
       a.email
       FROM
       s9y_entries AS e
       LEFT JOIN s9y_authors a
           ON e.authorid = a.authorid
       LEFT JOIN s9y_entrycat ec
           ON e.id = ec.entryid
       LEFT JOIN s9y_category c
           ON ec.categoryid = c.categoryid
        LEFT OUTER JOIN s9y_entryproperties ep_no_frontpage
               ON (e.id = ep_no_frontpage.entryid AND ep_no_frontpage.property = 'ep_no_frontpage')
                  LEFT OUTER JOIN s9y_entryproperties ep_cache_extended
               ON (e.id = ep_cache_extended.entryid AND ep_cache_extended.property = 'ep_cache_extended')
                  LEFT OUTER JOIN s9y_entryproperties ep_cache_body
               ON (e.id = ep_cache_body.entryid AND ep_cache_body.property = 'ep_cache_body')
                  LEFT OUTER JOIN s9y_entryproperties ep_access
               ON (e.id = ep_access.entryid AND ep_access.property = 'ep_access')
       WHERE isdraft = 'false' AND e.timestamp <= 1269063000
             AND (ep_access.property IS NULL OR ep_access.value = 'public')
             AND (ep_no_frontpage.property IS NULL OR ep_no_frontpage.value != 'true')
       GROUP BY e.id
       ORDER BY orderkey DESC, last_modified DESC
       LIMIT 1;

Na gut, ich würde vermutlich länger als 5-6 Sekunden brauchen, bloß um das Ding zu lesen, aber ich bin ja nun auch kein MySQL-Server … Ein Test ergab dann leider, daß diese Abfrage tatsächlich mehrere Sekunden dauert. :-(

EXPLAIN erklärte dazu folgendes:

+----+-------------+-------------------+--------+--------------------------+------------+---------+-----------------------+------+
| id | select_type | table             | type   | possible_keys            | key        | key_len | ref                   | rows |
+----+-------------+-------------------+--------+--------------------------+------------+---------+-----------------------+------+
|  1 | SIMPLE      | e                 | ref    | date_idx,edraft_idx      | edraft_idx | 1       | const                 | 1439 |
|  1 | SIMPLE      | a                 | eq_ref | PRIMARY                  | PRIMARY    | 4       | XXXXXXX.e.authorid    |    1 |
|  1 | SIMPLE      | ec                | ALL    | NULL                     | NULL       | NULL    | NULL                  | 1392 |
|  1 | SIMPLE      | c                 | eq_ref | PRIMARY                  | PRIMARY    | 4       | XXXXXXX.ec.categoryid |    1 |
|  1 | SIMPLE      | ep_no_frontpage   | eq_ref | prop_idx,entrypropid_idx | prop_idx   | 261     | XXXXXXX.e.id,const    |    1 |
|  1 | SIMPLE      | ep_cache_extended | eq_ref | prop_idx,entrypropid_idx | prop_idx   | 261     | XXXXXXX.e.id,const    |    1 |
|  1 | SIMPLE      | ep_cache_body     | eq_ref | prop_idx,entrypropid_idx | prop_idx   | 261     | XXXXXXX.e.id,const    |    1 |
|  1 | SIMPLE      | ep_access         | eq_ref | prop_idx,entrypropid_idx | prop_idx   | 261     | XXXXXXX.e.id,const    |    1 |
+----+-------------+-------------------+--------+--------------------------+------------+---------+-----------------------+------+
8 rows in set (0.00 sec)

Ein kurzer Blick in die MySQL-Dokumentation verriet mir dann schon, daß das Problem in der dritten Zeile von oben liegen dürfte; dort muss die komplette Datenbank gescannt werden. Ein Blick auf die betroffene Tabelle s9y_entrycat zeigt dann, dass diese ohnehin nur aus zwei Spalten besteht. Vielleicht mal versuchsweise einen Index auf diese Spalte legen?

alter table s9y_entrycat add index (entryid);

Et voilà - Problem gelöst. Mit einer Zeile. Nach fünf Jahren. *seufz*

Die Systemüberwachung zeigt: Problem schlagartig gelöst.

Trackbacks

Keine Trackbacks

Kommentare

Ansicht der Kommentare: Linear | Verschachtelt

Wiszszaf am :

 Wiszszaf

Hast Du das mal im s9y-Forum gemeldet? Klingt mir ja nach allgemeinerem Interesse.

Thomas Hochstein am :

Thomas Hochstein

Bisher nicht … You know, I don’t do forums. ;-)

Aber wenn Du das machen möchtest … jederzeit gerne.

Garvin am :

Garvin

War auf deiner entrycat ein index auf (entryid, categoryid) gesetzt? Ich ging bisher davon aus, dass der auch greift wenn categoryid=NULL ist bei ner Abfrage.

Thomas Hochstein am :

Thomas Hochstein

Nein, wohl nicht - ich habe IIRC vorher nicht nachgesehen, aber der Index müßte ja dann auch jetzt noch vorhanden sein, was er definitiv nicht ist. Ich gehe auch eher von einem lokalen Problem meiner Installation aus …

Wiszszaf am :

Wiszszaf

Also bei mir zB steht laut phpmyadmin:

Name entryid_idx Typ UNIQUE
Kardinalität 460 Feld entryid / categoryid

Wenn ich dann den Befehl da oben ausführe, dann habe ich zusätzlich:

Name entryid
Typ INDEX
Kardinalität 230
Feld entryid

nebst der Warnung:

Die Index-Typen INDEX und UNIQUE sollten nicht gleichzeitig für die Spalte entryid gesetzt sein

Thomas Hochstein am :

Thomas Hochstein

Jo. Ich sag ja, der fehlende Index war dann wohl ein lokales Problem (oder eine gaaaanz alte Sache).

Wiszszaf am :

Wiszszaf

Dann lösch doch mal Deinen neuen Index und probiere den offenbar von s9y vorgesehenen mit den zwei Feldern:

Sind Deine Lastprobleme dann immer noch weg?

Kommentar schreiben

HTML-Tags werden in ihre Entities umgewandelt.
Markdown-Formatierung erlaubt
Standard-Text Smilies wie :-) und ;-) werden zu Bildern konvertiert.
BBCode-Formatierung erlaubt
Gravatar, Identicon/Ycon Autoren-Bilder werden unterstützt.
Formular-Optionen