2017-02-05 4 views
0

Ich habe eine Haupttabelle mit 500000 + Zeilen.MySQL verwendet keine Indizes in JOIN Abfrage

CREATE TABLE `esc_questions`(
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `esc_id` INT(11) NOT NULL, 
    `question_text` LONGTEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_1` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_2` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_3` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_4` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_5` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `right_answer` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL, 
    `disciplinas_id` INT(11) UNSIGNED NOT NULL, 
    `assunto_id` INT(11) UNSIGNED NOT NULL, 
    `orgao_id` INT(11) UNSIGNED NOT NULL, 
    `cargo_id` INT(11) UNSIGNED NOT NULL, 
    `ano` INT(11) NOT NULL, 
    `banca_id` INT(11) UNSIGNED NOT NULL, 
    `question_type` TINYINT(4) NOT NULL, 
    `url` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `created_at` TIMESTAMP NULL DEFAULT NULL, 
    `updated_at` TIMESTAMP NULL DEFAULT NULL, 
    PRIMARY KEY(`id`), 
    KEY `idx_ano`(`ano`) USING BTREE, 
    KEY `idx_question_type`(`question_type`) USING BTREE, 
    KEY `idx_cargo_id`(`cargo_id`) USING BTREE, 
    KEY `idx_orgao_id`(`orgao_id`) USING BTREE, 
    KEY `idx_banca_id`(`banca_id`) USING BTREE, 
    KEY `idx_question_id`(`id`) USING BTREE, 
    KEY `idx_assunto_id`(`assunto_id`) USING BTREE, 
    KEY `idx_disciplinas_id`(`disciplinas_id`) USING BTREE, 
    CONSTRAINT `fk_assunto_id` FOREIGN KEY(`assunto_id`) REFERENCES `esc_assunto`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_banca_id` FOREIGN KEY(`banca_id`) REFERENCES `esc_bancas`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_cargo_id` FOREIGN KEY(`cargo_id`) REFERENCES `esc_cargo`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_disciplinas_id` FOREIGN KEY(`disciplinas_id`) REFERENCES `esc_disciplinas`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_orgao_id` FOREIGN KEY(`orgao_id`) REFERENCES `esc_orgao`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 
) ENGINE = INNODB AUTO_INCREMENT = 516157 DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci 


ähnliche Daten werden an fünf zusätzliche Tabellen gespeichert ist, sehr ähnlich wie dieser:

CREATE TABLE `esc_assunto`(
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
`name` VARCHAR(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
PRIMARY KEY(`id`), 
KEY `idx_assunto_id`(`id`) USING BTREE, 
KEY `idx_assunto_name`(`name`(30)), 
CONSTRAINT `fk_assunto` FOREIGN KEY(`id`) REFERENCES `esc_questions`(`assunto_id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = INNODB AUTO_INCREMENT = 3618 DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci 


Ich habe Paginierung auf meiner Website. Wenn ich versuche, die neuesten Seiten zu bekommen, steigt die Zeit für die Datenanforderung. Hier ist mein SELECT für diese Aufgabe:

SELECT 
    f.*, 
    d.name disciplinas, 
    o.name orgao, 
    c.name cargo, 
    b.name banca, 
    a.name assunto 
FROM 
    `esc_questions` f 
INNER JOIN 
    `esc_bancas` b 
ON 
    f.banca_id = b.id 
INNER JOIN 
    `esc_disciplinas` d 
ON 
    f.disciplinas_id = d.id 
INNER JOIN 
    `esc_assunto` a 
ON 
    f.assunto_id = a.id 
INNER JOIN 
    `esc_orgao` o 
ON 
    f.orgao_id = o.id 
INNER JOIN 
    `esc_cargo` c 
ON 
    f.cargo_id = c.id 
LIMIT 400020, 20 

Diese Abfrage dauert eine lange Zeit auf Sending Data Bühne in Abfrage Profiler zeigte.
Sending Data 17.6 s 99.99% 1 17.6 s

EXPLAIN zeigt folgendes:
1 SIMPLE d ALL PRIMARY, idx_disciplinas_id 247

1 SIMPLE f ref idx_cargo_id, idx_orgao_id, idx_banca_id, idx_assunto_id, idx_disciplinas_id idx_disciplinas_id 4 concursos.d.id 1116

1 SIMPLE o eq_ref PRIMARY, idx_orgao_id PRIMARY 4 concursos.f.orgao_id 1

1 SIMPLE c eq_ref PRIMARY, idx_cargo_id PRIMARY 4 concursos.f.cargo_id 1

1 SIMPLE ein eq_ref PRIMARY, idx_assunto_id PRIMARY 4 concursos.f.assunto_id 1

1 SIMPLE b eq_ref PRIMARY verbrachte idx_bancas_id PRIMARY 4 concursos.f.banca_id 1


ich den ganzen Tag diese Arbeit schnell zu machen, aber Kein Erfolg.

Kann mir jemand sagen, was mit meiner Select-Abfrage nicht stimmt oder warum MySQL keine Indizes verwendet?

Jede Hilfe wird geschätzt.

Antwort

0

Ich habe selbst eine Lösung gefunden. Ich muss LIMIT mit offset in meiner JOIN-Abfrage vermeiden. Um dies zu tun, muss ich einige Vorbereitungen treffen:
1. Holen Sie nur IDs aus meiner Haupttabelle ohne Joins auf benötigte Offset. Diese Abfrage hat 0 genommen.0856 Sekunden

SELECT id FROM `esc_questions` WHERE 1 LIMIT 489980, 20 

2. Erstellen Sie einen zusammengesetzten Index, um Ihre Anfrage zu stellen. In meinem Fall verwende ich den folgenden Index:

... 
KEY `idx_filter_search` (`id`,`disciplinas_id`,`assunto_id`,`orgao_id`,`cargo_id`,`banca_id`) USING BTREE, 
... 

3. Schließlich machen Sie Ihre Abfrage. Query took 0.0040 seconds:

SELECT SQL_NO_CACHE 
     f.*, 
     d.name disciplinas, 
     o.name orgao, 
     c.name cargo, 
     b.name banca, 
     a.name assunto 
    FROM 
     `esc_questions` f FORCE INDEX(idx_filter_search), 
     `esc_disciplinas` d, 
     `esc_assunto` a, 
     `esc_orgao` o, 
     `esc_cargo` c, 
     `esc_bancas` b 
    WHERE 
     f.id IN(
      497442, 
      497444, 
      497445, 
      497447, 
      497449, 
      497450, 
      497452, 
      497453, 
      497454, 
      497456, 
      497458, 
      497459, 
      497461, 
      497462, 
      497464, 
      497465, 
      497467, 
      497468, 
      497470, 
      497471 
     ) AND f.disciplinas_id = d.id AND f.assunto_id = a.id AND f.orgao_id = o.id AND f.cargo_id = c.id AND f.banca_id = b.id 
    ORDER BY 
     id 

EXPLAIN diese Abfrage wird mir sagen, dass es meine neu erstellte Index ist verwenden.
1 | SIMPLE | f | range | idx_filter_search | idx_filter_search | 4 | NULL | 20 | Using where

Hoffe das hilft jemandem. Danke @GordonLinoff für mich in die richtige Richtung.

0

Sie haben den falschen Ansatz in mehreren sagt. Zuerst hat Ihre Abfrage keine order by Klausel. Bei einer Abfrage wird nicht garantiert, dass die Ergebnisse bei mehreren Ausführungen in derselben Reihenfolge zurückgegeben werden (obwohl in der Praxis Abfragen das Debuggen eines solchen Problems sehr schwierig machen).

Also, sollten Sie eine order by, wahrscheinlich auf den Primärschlüssel von esc_questions und was auch immer sekundäre Schlüssel notwendig sind.

Zweitens ist der Offset von 400020 ziemlich groß. MySQL generiert 400.020 Zeilen und verwirft sie, bevor die 400.021ste Zeile gefunden wird.

Mein Vorschlag ist es, die „id“ verwendet in der Art zu finden und schließen dann eine where Klausel:

where ?? > $last_id 
. . . 
order by ?? 
limit 20 

Dies kann nicht (oder kann) Beschleunigung der Last das erste Mal, aber es sollte beschleunigen nachfolgende Lasten.

+0

1. Wenn ORDER BY für PRIMARY verwendet wird, wird ein Extra hinzugefügt: 'Using temporary; Verwenden von filesort' in EXPLAIN und beschleunigt diese Abfrage nicht. Zuerst war es mit ORDER BY. 2. Ich kann WHERE id nicht größer als% verwenden, da ids nicht sequenziell sind. Aber ich habe getestet und wirklich beschleunigt die Ladezeit. – ganchclub