スロークエリログにこのエントリがあります。
# User@Host: user[Host] @ [ip]
# Thread_id: 1514428 Schema: db Last_errno: 0 Killed: 0
# Query_time: 2.795454 Lock_time: 0.000116 Rows_sent: 15 Rows_examined: 65207 Rows_affected: 0 Rows_read: 65207
# Bytsent: 26618
SET timestamp=1407511874;
select off.*,translated_title,translated_description
from ephpb2b_products off USE INDEX(id_viewed)
INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
Left Join ephpb2b_product_language_new pol
ON off.id = pol.offer_id
and pol.language='en'
where off.approved=1
order by off.viewed
LIMIT 15;
このクエリについて説明すると、まったく問題ありません。
mysql> explain select off.*,translated_title,translated_description from ephpb2b_products off USE INDEX(id_viewed) INNER JOIN ephpb2b_members mem ON off.uid = mem.id Left Join ephpb2b_product_language_new pol ON off.id = pol.offer_id and pol.language='en' where off.approved=1 order by off.viewed LIMIT 15;
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
| 1 | SIMPLE | off | index | NULL | id_viewed | 4 | NULL | 3 | Using where |
| 1 | SIMPLE | mem | eq_ref | PRIMARY | PRIMARY | 4 | db.off.uid | 1 | Using index |
| 1 | SIMPLE | pol | ref | offer_id,id_language | offer_id | 5 | db.off.id | 4 | |
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
3 rows in set (0.17 sec)
このクエリを最適化するにはどうすればよいですか?なぜ3行が表示され、クエリログが遅いので65207行を調べたと表示されるのですか。
皆さんの洞察に感謝します。この問題は、このクエリを2つの異なるクエリに分割することで解決しました。最初にIDを照会し、次にそれらのIDを他のテーブルに渡して情報を得ました。
select id from ephpb2b_products off INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
where off.approved=1
order by off.viewed
LIMIT 15;
次に:
select * from ephpb2b_product_language_new where offer_id IN ({ids from lasts query})
これははるかに優れたパフォーマンスを発揮し、奇妙な動作をしません。
この質問に答えるには、説明の行列の意味、および統計に基づく計算と実行後の統計の違いを理解する必要があります。
Explainを実行すると、行の列から、目的のフィルターを使用して検査される行数が表アクセスごとにわかります。これを計算する方法は2つあります。インデックスダイブ(通常は正確な結果が得られるはずです)と、各エンジンが個別に保存するおおよその統計を使用する方法-各テーブルで最大5.6-。最初の方法を使用できる場合は1つの方法が推奨されますが(単一のインデックス付き列に対する単純なフィルター)、多くの場合、近似しか使用できません。そうでない場合、クエリオプティマイザーはクエリの実行自体と同じだけの時間を要します。
いずれの場合も、行はテーブルアクセスごとに読み取られる(返されない)計算された行です。正確であっても(多くの場合、数桁の差がありますが、オプティマイザには十分です)、結合全体でアクセスされる実際の行数は予測されません。たとえば、テーブルA
(正確にX
行を読み取る)とテーブルB
(正確にY
行を読み取る)を_A -> B
_、読み取られる実際の行数は次のようになります:X + # of rows returned by A (<=X) multiplied by Y
、標準のmysqlはネストされたループ結合のみをサポートするため。
これらの統計は実行後に収集されるため、正確なのでであるため、ハンドラーの統計やその他のプロファイリングメカニズムのような遅いログは、処理および送信された実際の行数を示します。
あなたの特定のケースに関して、EXPLAIN
は、実際にはフルインデックススキャンを実行している可能性がある場合に、最初のアクセスでスキャンされるのは3行のみであることを示しているためです(ソートにのみキーを使用しているため) )。これは、実行される結合ごとに後で乗算されます。 説明を信用しないでください。以下を使用できます。
_FLUSH STATUS;
-- Execute your query here
SHOW STATUS like 'Hand%';
_
行操作の実際の数と種類(PKアクセス、参照、インデックススキャン、テーブルスキャン)を確認します。各テーブルアクセスを個別にテストするために使用します。
より具体的なヘルプを得るには、各テーブルのテーブル構造と各フィルター条件のおおよその選択性が必要になります。
EXPLAIN
、低速のクエリログ、および検査対象の行に関する説明については、+ 1から@junusまで。
"クエリを最適化する方法"について:
products
テーブルとmembers
テーブルの間に明示的な外部キー関係があると仮定すると、それらの間の結合は次のようになります。
_ephpb2b_products off
INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
_
_LEFT JOIN
_に変換できます。 FKは、2つのクエリが100%等価であることを保証します。それを念頭に置き、where
句とorder
句は次のとおりです。
_WHERE off.approved=1
ORDER BY off.viewed
LIMIT 15
_
ベーステーブル(products
)の列のみを使用します。つまり、FROM
句の「左側」の部分のテーブルを使用します。サブクエリを使用して、最初に行を制限し、もう一方を結合できます。 2つのテーブル、私が呼ぶテクニック
"最初にlimit
、次にjoin
":
_SELECT
off.*,
pol.translated_title,
pol.translated_description
FROM
( SELECT p.* -- first limit
FROM ephpb2b_products AS p
WHERE p.approved=1
ORDER BY p.viewed
LIMIT 15
) AS off
LEFT JOIN -- then join
ephpb2b_members AS mem
ON off.uid = mem.id
LEFT JOIN
ephpb2b_product_language_new AS pol
ON pol.language = 'en'
AND pol.offer_id = off.id
ORDER BY
off.viewed ; -- no LIMIT required here
_
ephpb2b_products (approved, viewed)
のインデックスを使用すると、サブクエリは非常に効率的になります。 15行のみが含まれるため、残りの実行プランは問題になりません(結合する列にインデックスがあると思います)。
phpb2b_product_language_new (language, offer_id)
にインデックスを追加すると、効率がさらに向上する可能性があります(ただし、盲目的にこれを追加しないで、最初にテストしてください。上記のインデックスと書き換えで、速度を大幅に向上させることができます)。