対処方法がわからない特定のパフォーマンスの問題に気づきました。
私は、非常によく似た仕様のサーバー間でWebアプリケーションを移行している最中です。通常、新しいサーバーは古いサーバーよりもパフォーマンスが優れています。
古いサーバーはMySQL 5.6.35を実行しています
新しいサーバーはMySQL 5.7.17を実行しています
新しいサーバーと古いサーバーの両方で、実質的に同じMySQL構成が使用されています。新しいサーバーと古いサーバーの両方が完全に複製されたまったく同じデータベースを実行しています。
問題のWebアプリケーションは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秒になりますが、これは0.04秒で実行されるMYSQL 5.6よりもはるかに遅く、私はそうではないので、これらのどちらも良い考えではないと思います他の影響があるかどうか確認してください。
また、Magentoフレームワークによって生成され、回避したいMagentoコードベースのカスタマイズが必要になるため、クエリを変更することも非常に困難です。また、影響を受けるクエリがそれだけかどうかもわかりません。
MySQLインストールのマイナーバージョンを含めました。現在、MySQL 5.7.17を5.7.18(最新ビルド)に更新して、パフォーマンスの更新がないか確認しています。
MySQL 5.7.18にアップグレードした後、改善は見られませんでした。システムを安定した高パフォーマンス状態に戻すために、MySQL 5.6.30にダウングレードすることを決定しました。ダウングレードを行った後、すぐに改善が見られました。
上記のクエリは、新しいサーバーのMySQL 5.6.30で0.036秒で実行されました。
うわー!プロファイリングから何か便利なものを見たのはこれが初めてです。インデックスの動的作成は、Oracleの新しい最適化機能です。しかし、それはこの場合に最適な計画ではなかったようです。
最初に、バグを http://bugs.mysql.com に報告することをお勧めします-彼らは回帰、特にこの悪質なものを好まないのです。可能であれば、_EXPLAIN FORMAT=JSON SELECT...
_および「オプティマイザトレース」を提供してください。 (私は、あいまいな調整パラメータの微調整を許容できる回答として受け入れませんが、それらを発見してくれてありがとう。)
支援に戻ります...
LEFT
が必要ない場合は、使用しないでください。 「右」テーブルに一致する行がない場合、NULLs
を返します。あなたの場合それは起こりますか?SHOW CREATE TABLE
_を入力してください。その間、私はあなたがINDEX(include_in_menu, is_active, path)
を持っていないと推測します。最初の2つはどちらの順序でもかまいません。 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
_
(推奨インデックスはここにも適用されます。)
これは@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>