私はDjango MySQL InnoDBデータベースにデータを保存するWebアプリケーションを持っています。Django adminで多くアクセスされる特定のページがあり、クエリに時間がかかる(〜20秒)Django internalsであるため、クエリを変更できません。
A
、B
、およびC
の3つのテーブルがあります。クエリは次のようになります。
SELECT *
FROM A
INNER JOIN B ON (A.b_id = B.foo)
INNER JOIN C ON (B.foo = C.id)
ORDER BY A.id DESC
LIMIT 100
単純なjoin-3-tablesを一緒に。
id
フィールドは主キーであり、インデックスがあります。 A.b_id
、B.foo
両方に独自のインデックスがあります。
ただし、クエリプランは間違って見え、Bでキーを使用していないと表示されます(ただし、他の結合のキーを使用しています)。 MySQLのパフォーマンスに関する多くのことを読むと、「フォールスルー」する可能性があるさまざまなconst結合であるため、理論的にはインデックスを使用する必要があります。 Bの〜1200行すべてをスキャンする必要があると述べています。
奇妙なことに、私はローカルマシンで1つずつOPTIMIZE
edし、クエリを再実行しました(SQL_NO_CACHE
)、それは元の20秒に対して0.02秒とはるかに高速でした。同じクエリに対してEXPLAIN
を実行すると、異なる、はるかに実用的な結果が得られ、それぞれにインデックスを使用できること、および全体をスキャンする必要がないことを示します。同僚は、ほぼ同じデータ(最近、ロードされたダンプファイルから再作成された)を使用して、テストマシン上でそれぞれに対してOPTIMIZE
を実行しましたが、速度の向上と理にかなった説明も示しました。
したがって、ライブシステムで実行しましたが、何も変わりませんでした(速度も説明もありません)。 MySQLデータベースを再作成し(DROP
ed the database and reload from a dump)、今度はOPTIMIZE
は何も変更しません(つまり、実行時間〜20秒、クエリプランが正しくありません)。
なぜこれが起こるのですか? MySQLに正しいインデックスを使用させ、0.02秒のクエリ時間を取り戻すにはどうすればよいですか?このブログ投稿( http://www.xaprb.com/blog/2010/02/07/how-often-should-you-use-optimize-table/ )は、OPTIMIZE
は主キーのみを最適化します(それだけではありませんb_id
、foo
はプライマリインデックスではありません)。 「セカンダリインデックスを再構築」するにはどうすればよいですか?やってみましたALTER TABLE A ENGINE=InnoDB
(BとCも同じ)で、変更はありませんでした。
実際、これを見れば見るほど、MySQLクエリプランが失敗したように見えます。間違ったクエリプランを実行しており、使用できるインデックスを使用していません。 OPTIMIZE TABLEなどを実行した後、正しいインデックスを使用できる場合があります。このような不適切なクエリプランがある場合、ランダムに選択される可能性があるため、マシンによって結果が異なります(私はそう思います)。
OPTIMIZEはテーブルを再構築します。これ(InnoDBの場合)は、断片化と無駄なスペースの一部を排除します。これにより、クエリで顕著な違いが生じることはほとんどありません。
また、OPTIMIZEはANALYZEを実行します。これは統計を変更する可能性があり、それによって別の(より良いまたは悪い)EXPLAINプランにつながります。
ANALYZEはOPTIMIZEよりもはるかに高速(InnoDB上)なので、ANALYZEを実行するだけです。
さまざまな非ANALYZEアクションにより、ANALYZEが実行されます。
ANALYZEはBTreeをランダムにプローブし、統計を収集します。時々、結果の統計は貧弱です。これを防ぐ方法は事実上ありません。長年にわたっていくつかの部分的なハックが作成されました。 5.6.7はANALYZEでこの問題の解消に近づきました。以下がその1つです。 http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_stats_persistent_sample_pages