2016-06-07 7 views
1

In MySQL Log für langsame Abfragen zu finden, ich habe die folgende Abfrage:optimalen Index für diese MySQL-Abfrage

SELECT * FROM `news_items` 
WHERE `ctime` > 1465013901 AND `feed_id` IN (1, 2, 9) AND 
`moderated` = '1' AND `visibility` = '1' 
ORDER BY `views` DESC 
LIMIT 5; 

Hier ist das Ergebnis der EXPLAIN:

+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+ 
| id | select_type | table  | type | possible_keys                   | key | key_len | ref | rows | Extra  | 
+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+ 
| 1 | SIMPLE  | news_items | index | feed_id,ctime,ctime_2,feed_id_2,moderated,visibility,feed_id_3,cday_complex,feed_id_4 | views | 4  | NULL | 5 | Using where | 
+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

Wenn ich diese Abfrage ausführen es manuell, dauert wie 0,00 Sekunden, aber aus irgendeinem Grund erscheint es im langsamen Log von MySQL, der manchmal 1-5 Sekunden dauert. Ich glaube, es passiert, wenn der Server unter hoher Last steht.

Hier ist die Tabellenstruktur:

CREATE TABLE IF NOT EXISTS `news_items` (
    `item_id` int(10) NOT NULL, 
    `category_id` int(10) NOT NULL, 
    `source_id` int(10) NOT NULL, 
    `feed_id` int(10) NOT NULL, 
    `title` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `announce` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `content` text CHARACTER SET utf8 NOT NULL, 
    `hyperlink` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `ctime` varchar(11) CHARACTER SET utf8 NOT NULL, 
    `cday` tinyint(2) NOT NULL, 
    `img` varchar(100) CHARACTER SET utf8 NOT NULL, 
    `video` text CHARACTER SET utf8 NOT NULL, 
    `gallery` text CHARACTER SET utf8 NOT NULL, 
    `comments` int(11) NOT NULL DEFAULT '0', 
    `views` int(11) NOT NULL DEFAULT '0', 
    `visibility` enum('1','0') CHARACTER SET utf8 NOT NULL DEFAULT '0', 
    `pin` tinyint(1) NOT NULL, 
    `pin_dttm` varchar(10) CHARACTER SET utf8 NOT NULL, 
    `moderated` tinyint(1) NOT NULL 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

Der Index wird als "Ansichten" genannt besteht aus 1-Feld nur - Ansichten. Ich habe auch viele andere Indizes, bestehend aus (zum Beispiel):

feed_id + views + visibility + moderated 
moderated + visibility + feed_id + ctime 
moderated + visibility + feed_id + views + ctime 

Ich benutzte Felder in genannten Reihenfolge, weil das der einzige Grund, warum MySQL war begonnen, sie zu benutzen. In EXPLAIN habe ich jedoch nie "Verwenden wo wo; Verwenden von Index".

Irgendwelche Ideen, wie man EXPLAIN dazu bringt, mir "using index" zu zeigen?

+1

Grundregel: Jedes Feld, das in einem Entscheidungskontext verwendet wird (z. B. verwendet in 'where',' join', 'order by', etc ...) sollte indexiert werden. Ihre Indizes scheinen überflüssig zu sein. Wenn ein Feld in mehreren Indizes angezeigt wird, ist es wahrscheinlich besser, einen eigenen separaten Index zu verwenden. z.B. Wenn die Moderation geändert wird, erzwingen Sie die Änderung von drei separaten Indizes anstelle von nur einem. –

+0

Ich werde die meisten dieser Indizes löschen, wenn ich die beste finde. Es ist nur ein Beispiel dafür, was ich tue, um einen guten Index zu finden. Wie auch immer, danke für deinen Kommentar, ich werde es mir merken. – Greg

Antwort

-1

Um Ihre Frage zu beantworten: "using index" bedeutet, dass MySQL nur Index verwendet, um Ihre Abfrage zu erfüllen. Dazu müssen wir einen "überdachten" Index erstellen (Index, der die Abfrage "abdeckt") = index, der sowohl "where" als auch "order by/group by" und alle Felder von "select" abdeckt "Wählen Sie *", so dass dies nicht praktikabel ist.

MySQL wählt den Index für Sichten, da Sie in der Abfrage das Limit 5 haben. Es tut das als 1) Index ist klein 2) es kann in diesem Fall filesort vermeiden.

Ich glaube, das Problem ist nicht mit dem Index, sondern mit der Engine = MyISAM. MyISAM verwendet die Sperre auf Tabellenebene. Wenn Sie also die news_items ändern, wird sie gesperrt. Ich würde vorschlagen, Tabelle zu InnoDB umzuwandeln.

Eine andere Möglichkeit kann sein, dass der Index auf (Ansichten) nicht die beste Option ist, wenn die Tabelle groß ist.

Wenn Sie Percona Server verwenden, können Sie langsam Protokoll Ausführlichkeit Option aktivieren und den Abfrageplan für die langsame Abfrage sehen, wie hier beschrieben: https://www.percona.com/doc/percona-server/5.5/diagnostics/slow_extended_55.html

+0

Hallo Alexander! Danke für deine Antwort. Sie glauben also, dass "Ansichten" der beste Index-ATM für diese MyISAM-Tabelle ist? Es hat 30k Reihen, 80Mbytes. Ich werde versuchen, InnoDB zu testen. BTW, ich mache eine Volltextsuche auf dieser Tabelle mit Sphinx, so dass die Migration kein großes Problem sein wird, nehme ich an. – Greg

+0

Greg, beginnend mit MySQL 5.6 können Sie InnoDB mit Volltextindizes verwenden. Die Verwendung von Sphinx ist ebenfalls ein guter Ansatz. Mit 80Mb Tabelle würde ich sagen, dass der Index für Ansichten der einfachste und der beste hier sein kann. Ich würde auch vorschlagen, andere Indizes zu bereinigen, da einige von ihnen wie dupliziert aussehen. In MySQL 5.6+ können Sie auch das Leistungsschema verwenden, um alle nicht verwendeten Indizes zu finden. –

0

Wenn Sie die Speicher-Engine InnoDB ändern und erstellen Sie den richtigen Composite-Index Sie kann das versuchen. Die erste Abfrage ruft nur die item_id für die ersten 5 Zeilen ab. Die Begrenzung wird durchgeführt, nachdem das vollständige SELECT ausgeführt wurde. So ist es besser, dies zu tun, ohne große Datenmengen und dann nur die Lochreihe erhalten von den 5 Leiden

SELECT idata.* FROM (
    SELECT item_id FROM `news_items` 
    WHERE `ctime` > 1465013901 AND `feed_id` IN (1, 2, 9) AND 
    `moderated` = '1' AND `visibility` = '1' 
    ORDER BY `views` DESC 
    LIMIT 5) as i_ids 
LEFT JOIN news_items AS idata ON idata.item_id = i_ids.item_id 
ORDER BY `views` DESC; 
0

Wenn Ihre Tabelle „auch viele andere Indizes haben“, warum sie zeigen, nicht in der SHOW CREATE TABLE?

Es gibt zwei Möglichkeiten, die

WHERE `ctime` > 1465013901 
    AND `feed_id` IN (1, 2, 9) 
    AND `moderated` = '1' 
    AND `visibility` = '1' 
ORDER BY `views` DESC 

Indizierung verwenden:

  • INDEX(views) und hoffen, dass die gewünschten 5 Reihen (siehe LIMIT) früh auftauchen.
  • INDEX(moderated, visibility, feed_id, ctime)

Dieser letzte 'Composite' Index beginnt mit den beiden Säulen (in beliebiger Reihenfolge), das = constant verglichen werden, bewegt sich dann zu IN auf und schließlich einem "Bereich" (ctime > const). Ältere Versionen kommen nicht über IN hinaus; Neuere Versionen werden die Werte IN überspringen und den Bereich ctime verwenden. More discussion.

Es ist sinnlos, die ORDER BY Spalten in einem zusammengesetzten Index enthalten vor alle der WHERE Spalten. Es ist jedoch nicht sinnvoll, in Ihrem Fall views einzuschließen, da der "Bereich" ctime ist.

Der von Bernd vorgeschlagene Tipp zur "Lazy Evaluation" wird auch helfen.

Ich stimme zu, dass InnoDB wahrscheinlich besser wäre. Conversion tips.

Verwandte Themen