2012-04-13 15 views
0

Ich habe drei Tabellen. eine, die Beiträge hält (postadata), eine, die Tags enthält (tag) und eine Verknüpfungstabelle (tag_data) Ich führe eine Abfrage, um alle Beiträge und ihre Tags mit der folgenden Abfrage verkettet zu bekommen.mysql Langsame Abfrage, um Post und Tags

SELECT 
     postdata.data_id as data_id, 
     GROUP_CONCAT(DISTINCT(tag.tag_id)) as tag_ids, 
     GROUP_CONCAT(DISTINCT(tag.tag) ORDER BY tag.tag ASC) as tags 
    FROM postdata 
    LEFT JOIN tag_data as tag_data 
      INNER JOIN tag as tag 
       ON tag_data.tag_id = tag.tag_id 
    ON postdata.data_id = tag_Data.data_id 
    GROUP BY postdata.data_id 
    LIMIT 1000 

Aber es ist sehr, sehr langsam. (ungefähr 20 Sekunden). Im Folgenden wird die erklären, kann ich nicht

1 SIMPLE postdata index NULL source_id 4 NULL   158808 Using index; Using temporary; Using filesort 
1 SIMPLE tag_data index PRIMARY PRIMARY  8 NULL   45279 Using index 
1 SIMPLE tag   eq_ref PRIMARY PRIMARY  4 tag_data.tag_id 1 

unten das Problem zu finden scheinen die Postdata-Indizes sind

postdata 0 PRIMARY  1 data_id  A 120405 NULL NULL  BTREE  
postdata 1 source_id 1 source_id A 168  NULL NULL  BTREE 

Ist es ein Problem mit der Abfrage? Gibt es eine Möglichkeit, es effizienter zu schreiben? Oder ist es ein fehlender Index?

UPDATE: Hinzufügen Index Info per jordeu Kommentare

SHOW TABLE STATUS VON [database_name] WHERE Name = 'postdata'

Name  Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment 
postdata InnoDB 10  Compact  158645 43    6832128  0    8421376   4194304  NULL   2012-03-08 09:22:40 NULL NULL utf8_unicode_ci NULL 

SHOW VARIABLES LIKE 'key_buffer_size'

key_buffer_size 16777216 
+0

Haben Sie versucht, inkrementell Bits davon zu entfernen, um zu sehen, an welchem ​​Punkt es beschleunigt? Ich würde versuchen, ohne die GROUP_CONCAT Zeug und sehen, was für einen Unterschied zum Beispiel macht. –

+0

Meine erste Schätzung ist die 'DISTINCT' im' GROUP_CONCAT'. Was ich denke, ist Per Tag_id und pro Tag wird es die gesamte Tabelle scannen, um sicherzustellen, dass Sie nur die verschiedenen Datensätze haben. Ich schätze, dass da dein Flaschenhals ist. –

+0

Wenn ich die zwei GROUP_CONCAT vollständig auskommentiere, aber die Joins behalten werde, so sieht die Abfrage wie folgt aus: SELECT postdata.data_id als data_id FROM postdata ... 'bleibt so langsam. – applechief

Antwort

0

Wenn wie gesagt, die DISTINCT ist böse, könnten Sie die DISTINCT entfernen und sie so einzigartig machen e this:

$arr_tag_ids = array_unique(implode(',', $row->tag_ids)); 
$arr_tags = array_unique(implode(',', $row->tags)); 
+0

leider die distinct scheint nicht die Ursache zu sein. überprüfe meinen Kommentar zu der Frage. – applechief

0

Von Ihrem EXPLAIN sieht es so aus, als ob es keine INDEXe bei Ihrem ersten Beitritt verwendet. Dies kann daran liegen, dass Sie sowohl JOINs als auch beide ON-Bedingungen haben. Ich habe noch nie einen solchen JOIN gesehen. Versuchen Sie, das so zu ändern

SELECT 
    postdata.data_id as data_id, 
    GROUP_CONCAT(DISTINCT(tag.tag_id)) as tag_ids, 
    GROUP_CONCAT(DISTINCT(tag.tag) ORDER BY tag.tag ASC) as tags 
FROM postdata 
    LEFT JOIN tag_data as tag_data 
     ON postdata.data_id = tag_Data.data_id 
    LEFT JOIN tag as tag 
     ON tag_data.tag_id = tag.tag_id 
GROUP BY postdata.data_id 
LIMIT 1000 

Dann erneut ausführen EXPLAIN und sehen, ob es einen Unterschied macht. Brauchst du definitiv einen LEFT JOIN für den ersten JOIN? Wenn du das zu einem INNER JOIN änderst, hilft das vielleicht.

+0

@schaft Ok, ja, guter Punkt. Ich habe den INNEREN JOIN in einen LINKEN JOIN geändert, der die Abfrage wieder funktionieren lassen sollte. Sind Ihre Tabellen sowohl auf PRMARY- als auch auf FOREIGN-Schlüsseln INDEX? Hat "tag_data" einen INDEX für "data_id" und "tag_id"? – liquorvicar

+0

Ja, der primäre Index von tag_data ist sowohl auf data_id als auch auf tag_id – applechief

+0

@ schaft Können Sie das Schema (einschließlich INDEXe) für alle 3 Tabellen posten? – liquorvicar

1

Es ist nicht notwendig, einen INNER JOIN nach einem LINKEN JOIN zu verwenden.

versuchen, diesen Ansatz:

SELECT 
    postdata.data_id as data_id, 
    GROUP_CONCAT(DISTINCT(tag.tag_id)) as tag_ids, 
    GROUP_CONCAT(DISTINCT(tag.tag) ORDER BY tag.tag ASC) as tags 
FROM postdata 
    LEFT JOIN tag_data as tag_data 
     ON postdata.data_id = tag_Data.data_id 
    LEFT JOIN tag as tag 
     ON tag_data.tag_id = tag.tag_id 
GROUP BY postdata.data_id 
LIMIT 1000 
+0

Es beschleunigte es nur um eine Sekunde. aber zumindest ist die Abfrage jetzt klarer – applechief

+0

Können Sie mir die Erklärung dieser Abfrage zeigen? – jordeu

+0

hier ist es https://gist.github.com/a298a46a32b1c4c948e1 es ist seltsam es zeigt hook_data verwendet keinen Index, aber der primäre Index ist auf data_id und tag_id – applechief

1

das Problem sein kann, ist, dass Ihr Index in den Speicher passen nicht, diese langsam nach unten MySQL.

Ihre ‚key_buffer_size‘ zum Beispiel 160 MB erhöhen:

SET GLOBAL key_buffer_size = 167772160 

Wenn Sie InnoDB verwenden Sie auch erhöhen müssen ‚innodb_buffer_size‘ kann.

+0

Leider kann ich es nicht so einfach tun, meine Datenbank ist auf RDS. Gibt es eine Möglichkeit zu überprüfen, ob dies das Problem ist, ohne es zu versuchen? – applechief

+0

Wenn Sie Shell-Zugriff haben, können Sie überprüfen, ob mysqld mit 'ps' wechselt. Ohne Shell-Zugang weiß ich nicht, wie es geht. – jordeu

+0

Hier ein guter Artikel darüber: http://www.mysqlperformanceblog.com/2012/03/21/troubleshooting-mysql-memory-usage/ – jordeu