sql >> データベース >  >> RDS >> Mysql

MySQLサーバーでの非常に単純なAVG()集約クエリは、途方もなく長い時間がかかります

    特定の日付の行数をカウントするには、MySQLはインデックス内でその値を見つけ(これは非常に高速ですが、結局のところ、インデックスが作成されます)、インデックスの後続のエントリを読み取る必要があります 次の日付が見つかるまで。 esiのデータ型によって異なります 、これは合計で70万行をカウントするために数MBのデータを読み取ることになります。一部のMBの読み取りにはそれほど時間はかかりません(インデックスを使用する頻度によっては、そのデータがすでにバッファプールにキャッシュされている場合もあります)。

    インデックスに含まれていない列の平均を計算するために、MySQLは再びインデックスを使用して、その日付のすべての行を検索します(以前と同じ)。ただし、さらに、検出されたすべての行について、その行の実際のテーブルデータを読み取る必要があります。つまり、主キーを使用して行を検索し、いくつかのバイトを読み取り、これを700k回繰り返します。この「ランダムアクセス」 たくさん 最初のケースのシーケンシャル読み取りよりも低速です。 (これは、「一部のバイト」が innodb_page_size (デフォルトでは16KB)、したがって、count(*)の「一部のMB」と比較して、最大700k * 16KB=11GBを読み取る必要がある場合があります。;また、メモリ構成によっては、このデータの一部がキャッシュされず、ディスクから読み取る必要がある場合があります。)

    これに対する解決策は、使用されているすべての列をインデックス(「カバーインデックス」)に含めることです。 date, 01にインデックスを作成します 。そうすれば、MySQLはテーブル自体にアクセスする必要がなく、最初の方法と同様に、インデックスを読み取るだけで続行できます。インデックスのサイズが少し大きくなるため、MySQLは「もう少しMB」を読み取る必要があります(そしてavgを実行します -操作)、しかしそれでも数秒の問題であるはずです。

    コメントで、24列の平均を計算する必要があるとおっしゃいました。 avgを計算する場合 同時に複数の列の場合、それらすべてにカバーインデックスが必要になります。 date, 01, 02, ..., 24 テーブルへのアクセスを防ぐため。すべての列を含むインデックスには、テーブル自体と同じ量のストレージスペースが必要であることに注意してください(そのようなインデックスの作成には長い時間がかかります)。したがって、これらのリソースの価値があるかどうかは、このクエリの重要性に依存する可能性があります。

    MySQL-インデックスあたり16列の制限を回避するには 、2つのインデックス(および2つのクエリ)に分割できます。作成します。インデックスdate, 01, .., 12 およびdate, 13, .., 24 、次に使用

    select * from (select `date`, avg(`01`), ..., avg(`12`) 
                   from mytable where `date` = ...) as part1
    cross join    (select avg(`13`), ..., avg(`24`) 
                   from mytable where `date` = ...) as part2;
    

    クエリをこのように書く明確な理由はないので、これをよく文書化してください。ただし、それだけの価値があるかもしれません。

    1つの列の平均をとるだけの場合は、24個の個別のインデックスを追加できます(date, 01date, 02 、...)、全体としてはさらに多くのスペースが必要になりますが、少し速くなる可能性があります(個別に小さいため)。ただし、使用パターンやメモリ構成などの要因によっては、バッファプールが完全なインデックスを優先する場合があるため、テストする必要がある場合があります。

    date以降 主キーの一部である場合は、主キーをdate, esiに変更することも検討できます。 。主キーで日付を見つけた場合、(すでにテーブルにアクセスしているため)テーブルデータにアクセスするための追加の手順は必要ないため、動作はカバーインデックスと同様になります。ただし、これはテーブルに対する重要な変更であり、他のすべてのクエリに影響を与える可能性があります(たとえば、esiを使用します) 行を見つけるために)、慎重に検討する必要があります。

    前述のように、別のオプションは、特に過去の日付の行を追加または変更しない場合(またはトリガーを使用して行を最新の状態に保つことができる場合)、事前に計算された値を格納する要約テーブルを作成することです。



    1. ハロウィーンの問題–パート4

    2. MySQLDECLAREのSELECTINTO変数により構文エラーが発生しますか?

    3. JDBCがSHOWDATABASESコマンドを実行していません

    4. execSQL:bindargsの方が優れていますか?