it-swarm.com.ru

Производительность MySQL для версии 5.7 против 5.6

Я заметил конкретную проблему с производительностью, в которой я не уверен, что делать.

Я нахожусь в процессе миграции веб-приложения с одного сервера на другой с очень похожими характеристиками. Новый сервер обычно превосходит старый сервер, чтобы быть чистым.

Старый сервер работает под управлением MySQL 5.6.35 
Новый сервер работает под управлением MySQL 5.7.17

И новый, и старый сервер имеют практически идентичные конфигурации MySQL .... И новый, и старый сервер работают с точно одинаковой дублированной базой данных.

Рассматриваемое веб-приложение - Magento 1.9.3.2.

В Magento следующая функция Mage_Catalog_Model_Category::getChildrenCategories() предназначена для перечисления всех непосредственных дочерних категорий для данной категории.

В моем случае эта функция в конечном итоге переходит к следующему запросу:

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;

Несмотря на то, что структура этого запроса одинакова для любой установки Magento, очевидно, будут небольшие расхождения в значениях между установкой Magento и установкой Magento и какой категорией будет заниматься функция.

Моя таблица catalog_category_flat_store_1 содержит 214 строк. 
Моя таблица url_rewrite содержит 1 734 316 строк.

Этот запрос, когда выполняется сам по себе непосредственно в MySQL, выполняется по-разному в разных версиях MySQL.

Я использую SQLyog для профилирования этого запроса.

В MySQL 5.6 вышеуказанный запрос выполняется за 0,04 секунды. Профиль для этого запроса выглядит следующим образом: https://codepen.io/Petce/full/JNKEpy/

В MySQL 5.7 вышеуказанный запрос выполняется за 1,952 секунды. Профиль для этого запроса выглядит следующим образом: https://codepen.io/Petce/full/gWMgKZ/

Как видите, один и тот же запрос на почти одинаковых настройках практически на 2 секунды медленнее, и я не знаю, почему.

По какой-то причине MySQL 5.7 не хочет использовать индекс таблицы для получения набора результатов.

Кто-нибудь с большим опытом/знаниями может объяснить, что здесь происходит и как это исправить?

Я считаю, что проблема связана с тем, как работает оптимизатор MYSQL 5.7. По некоторым причинам, кажется, что полное сканирование таблицы - путь. Я могу радикально улучшить производительность запроса, установив max_seeks_for_key на очень низкое значение (например, 100) или опустив слишком низко значение range_optimizer_max_mem_size, чтобы оно выдало предупреждение.

Выполнение любого из этих действий увеличивает скорость запроса почти в 10 раз до 0,2 с, однако это все еще на несколько медленнее, чем MYSQL 5.6, который выполняется за 0,04 секунды, и я не думаю, что любой из них является хорошей идеей, поскольку я не конечно, если будут другие последствия.

Также очень сложно изменить запрос, так как он генерируется платформой Magento и потребует настройки базы кода Magento, чего я хотел бы избежать. Я также даже не уверен, что это единственный запрос.

Я включил второстепенные версии для моих установок MySQL. Сейчас я пытаюсь обновить MySQL 5.7.17 до 5.7.18 (последняя сборка), чтобы увидеть, есть ли какие-либо обновления производительности.

После обновления до MySQL 5.7.18 я не увидел улучшений. Чтобы вернуть систему в стабильное высокопроизводительное состояние, мы решили вернуться к MySQL 5.6.30. После выполнения рейтинга мы увидели мгновенное улучшение.

Вышеупомянутый запрос, выполненный в MySQL 5.6.30 на НОВОМ сервере, выполнен за 0.036 секунды.

7
Peter A

Вот Это Да! Это первый раз, когда я увидел что-то полезное из профилирования. Динамическое создание индекса - это новая функция оптимизации от Oracle. Но, похоже, это был не лучший план для этого случая.

Во-первых, я рекомендую вам сообщить об ошибке на http://bugs.mysql.com - им не нравится иметь регрессии, особенно это вопиюще. Если возможно, укажите EXPLAIN FORMAT=JSON SELECT... и «Оптимизатор трассировки». (Я не принимаю тонкие настройки в качестве приемлемого ответа, но спасибо за их открытие.)

Вернуться к помощи вам ...

  • Если вам не нужен LEFT, не используйте его. Он возвращает NULLs, когда в «правой» таблице нет совпадающих строк; это произойдет в вашем случае?
  • Пожалуйста, предоставьте SHOW CREATE TABLE. Между тем, я буду догадываться, что у вас нет INDEX(include_in_menu, is_active, path). Первые два могут быть в любом порядке; path должен быть последним.
  • И INDEX(category_id, is_system, store_id, id_path) с id_path последним.
  • Ваш запрос, кажется, имеет шаблон, который хорошо подходит для превращения в подзапрос:

(Примечание: это даже сохраняет семантику LEFT.)

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 

(Предлагаемые индексы применимы и здесь.)

3
Rick James

ЭТО НЕ ОТВЕТ только за комментарий для @Nigel Ren

Здесь вы можете увидеть, что LIKE также использовать индекс.

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>

образец с 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