it-swarm.com.de

MySQL-Leistung für Version 5.7 vs. 5.6

Ich habe ein besonderes Leistungsproblem festgestellt, bei dem ich unsicher bin, wie ich damit umgehen soll.

Ich bin gerade dabei, eine Webanwendung mit sehr ähnlichen Spezifikationen von einem Server zu einem anderen zu migrieren. Der neue Server übertrifft in der Regel den alten Server, um übersichtlich zu sein.

Auf dem alten Server wird MySQL 5.6.35 ausgeführt 
Auf dem neuen Server wird MySQL 5.7.17 ausgeführt

Sowohl der neue als auch der alte Server verfügen über praktisch identische MySQL-Konfigurationen. Sowohl auf dem neuen als auch auf dem alten Server wird genau dieselbe Datenbank ausgeführt, die perfekt dupliziert ist.

Die fragliche Webanwendung ist Magento 1.9.3.2.

In Magento soll die folgende FunktionMage_Catalog_Model_Category::getChildrenCategories() alle unmittelbar untergeordneten Kategorien einer bestimmten Kategorie anzeigen.

In meinem Fall sprudelt diese Funktion schließlich zu dieser Abfrage:

SELECT    `main_table`.`entity_id`
        , main_table.`name`
        , main_table.`path`
        , `main_table`.`is_active`
        , `main_table`.`is_anchor`
        , `url_rewrite`.`request_path`

FROM `catalog_category_flat_store_1` AS `main_table`

LEFT JOIN `core_url_rewrite` AS `url_rewrite`
ON url_rewrite.category_id=main_table.entity_id
AND url_rewrite.is_system=1
AND url_rewrite.store_id = 1
AND url_rewrite.id_path LIKE 'category/%'

WHERE (main_table.include_in_menu = '1')
AND (main_table.is_active = '1')
AND (main_table.path LIKE '1/494/%')
AND (`level` <= 2)
ORDER BY `main_table`.`position` ASC;

Während die Struktur für diese Abfrage für jede Magento-Installation gleich ist, gibt es offensichtlich leichte Abweichungen bei den Werten zwischen der Magento-Installation und der Magento-Installation und der Kategorie, auf die sich die Funktion bezieht.

Meine catalog_category_flat_store_1-Tabelle hat 214 Zeilen. 
Meine url_rewrite-Tabelle hat 1.734.316 Zeilen.

Wenn diese Abfrage direkt in MySQL ausgeführt wird, ist die Abfrage zwischen den MySQL-Versionen sehr unterschiedlich.

Ich verwende SQLyog, um diese Abfrage zu profilieren.

In MySQL 5.6 wird die obige Abfrage in 0,04 Sekunden ausgeführt. Das Profil für diese Abfrage sieht folgendermaßen aus: https://codepen.io/Petce/full/JNKEpy/

In MySQL 5.7 wird die obige Abfrage in 1.952 Sekunden ausgeführt. Das Profil für diese Abfrage sieht folgendermaßen aus: https://codepen.io/Petce/full/gWMgKZ/

Wie Sie sehen, ist die gleiche Abfrage in fast derselben Konfiguration um fast zwei Sekunden langsamer, und ich bin mir nicht sicher, warum.

Aus irgendeinem Grund möchte MySQL 5.7 den Tabellenindex nicht zur Erstellung der Ergebnismenge verwenden.

Jeder, der über mehr Erfahrung/Wissen verfügt, kann erklären, was hier vorgeht und wie man es beheben kann.

Ich glaube, das Problem hat etwas mit der Funktionsweise von MYSQL 5.7 Optimizer zu tun. Aus irgendeinem Grund scheint es so zu sein, dass ein vollständiger Tabellenscan der richtige Weg ist. Ich kann die Abfrageleistung drastisch verbessern, indem Sie max_seeks_for_key auf einen sehr niedrigen Wert (z. B. 100) setzen oder den range_optimizer_max_mem_size wirklich niedrig setzen, um eine Warnung auszulösen.

Dadurch wird die Abfragegeschwindigkeit um fast das Zehnfache auf 0,2 Sekunden herabgesetzt. Dies ist jedoch immer langsamer als MYSQL 5.6, das in 0,04 Sekunden ausgeführt wird, und ich denke nicht, dass dies eine gute Idee ist, da ich es nicht bin sicher, ob es andere Auswirkungen geben würde.

Es ist auch sehr schwierig, die Abfrage zu modifizieren, da sie vom Magento-Framework generiert wird und eine Anpassung der Magento-Codebase erforderlich macht, die ich vermeiden möchte. Ich bin mir auch nicht sicher, ob es die einzige Abfrage ist, die ausgeführt wird.

Ich habe die Nebenversionen für meine MySQL-Installationen beigefügt. Ich versuche jetzt, MySQL 5.7.17 auf 5.7.18 (den neuesten Build) zu aktualisieren, um zu sehen, ob die Leistung aktualisiert wird.

Nach dem Upgrade auf MySQL 5.7.18 konnte ich keine Verbesserung feststellen. Um das System wieder in einen stabilen Hochleistungszustand zu bringen, haben wir uns entschlossen, auf MySQL 5.6.30 zurückzustufen. Nach dem Downgrade sahen wir eine sofortige Verbesserung.

Die obige Abfrage, die in MySQL 5.6.30 auf dem NEW-Server ausgeführt wurde, wurde in 0,036 Sekunden ausgeführt.

7
Peter A

Beeindruckend! Dies ist das erste Mal, dass ich etwas Nützliches von Profiling gesehen habe. Das dynamische Erstellen eines Index ist eine neue Optimierungsfunktion von Oracle. Es sieht jedoch so aus, als sei dies nicht der beste Plan für diesen Fall.

Als Erstes empfehle ich Ihnen, einen Fehler unter http://bugs.mysql.com - einzureichen, da sie keine Regressionen mögen, besonders nicht bei diesem ungeheuerlichen. Wenn möglich, geben Sie EXPLAIN FORMAT=JSON SELECT... und "Optimizer-Trace" ein. (Ich akzeptiere nicht, obskure Tunables als akzeptable Antwort zu verwenden, aber ich danke Ihnen, dass Sie sie entdeckt haben.)

Zurück zum Helfen ...

  • Wenn Sie keine LEFT benötigen, verwenden Sie sie nicht. Es gibt NULLs zurück, wenn in der 'rechten' Tabelle keine übereinstimmenden Zeilen vorhanden sind. wird das in deinem Fall passieren?
  • Bitte geben Sie SHOW CREATE TABLE an. Inzwischen werde ich vermuten, dass Sie INDEX(include_in_menu, is_active, path) nicht haben. Die ersten beiden können in beliebiger Reihenfolge sein; path muss zuletzt sein.
  • Und INDEX(category_id, is_system, store_id, id_path) mit id_path zuletzt.
  • Ihre Abfrage scheint ein Muster zu haben, das sich gut in eine Unterabfrage verwandelt:

(Hinweis: Dadurch bleibt sogar die Semantik von LEFT erhalten.)

SELECT  `main_table`.`entity_id` , main_table.`name` , main_table.`path` ,
        `main_table`.`is_active` , `main_table`.`is_anchor` ,
        ( SELECT  `request_path`
            FROM  url_rewrite
            WHERE  url_rewrite.category_id=main_table.entity_id
              AND  url_rewrite.is_system = 1
              AND  url_rewrite.store_id  = 1
              AND  url_rewrite.id_path LIKE 'category/%' 
        ) as request_path
    FROM  `catalog_category_flat_store_1` AS `main_table`
    WHERE  (main_table.include_in_menu = '1')
      AND  (main_table.is_active = '1')
      AND  (main_table.path like '1/494/%')
      AND  (`level` <= 2)
    ORDER BY  `main_table`.`position` ASC
    LIMIT  0, 1000 

(Die vorgeschlagenen Indizes gelten auch hier.)

3
Rick James

Dies ist keine ANTWORT nur für einen Kommentar zu @Nigel Ren

Hier sehen Sie, dass LIKE auch den Index verwendet.

mysql> SELECT *
    -> FROM testdb
    -> WHERE
    -> vals LIKE 'text%';
+----+---------------------------------------+
| id | vals                                  |
+----+---------------------------------------+
|  3 | text for line number 3                |
|  1 | textline 1 we rqwe rq wer qwer q wer  |
|  2 | textline 2 asdf asd fas f asf  wer 3  |
+----+---------------------------------------+
3 rows in set (0,00 sec)

mysql> EXPLAIN
    -> SELECT *
    -> FROM testdb
    -> WHERE
    -> vals LIKE 'text%';
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | testdb | NULL       | range | vals          | vals | 515     | NULL |    3 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0,01 sec)

mysql>

Probe mit LEFT ()

mysql> SELECT *
    -> FROM testdb
    -> WHERE
    -> LEFT(vals,4) = 'text';
+----+---------------------------------------+
| id | vals                                  |
+----+---------------------------------------+
|  3 | text for line number 3                |
|  1 | textline 1 we rqwe rq wer qwer q wer  |
|  2 | textline 2 asdf asd fas f asf  wer 3  |
+----+---------------------------------------+
3 rows in set (0,01 sec)

mysql> EXPLAIN
    -> SELECT *
    -> FROM testdb
    -> WHERE
    -> LEFT(vals,4) = 'text';
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | testdb | NULL       | index | NULL          | vals | 515     | NULL |    5 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0,01 sec)

mysql>
0
Bernd Buffen