特定の日付の行数をカウントするには、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, 01
、date, 02
、...)、全体としてはさらに多くのスペースが必要になりますが、少し速くなる可能性があります(個別に小さいため)。ただし、使用パターンやメモリ構成などの要因によっては、バッファプールが完全なインデックスを優先する場合があるため、テストする必要がある場合があります。
date
以降 主キーの一部である場合は、主キーをdate, esi
に変更することも検討できます。 。主キーで日付を見つけた場合、(すでにテーブルにアクセスしているため)テーブルデータにアクセスするための追加の手順は必要ないため、動作はカバーインデックスと同様になります。ただし、これはテーブルに対する重要な変更であり、他のすべてのクエリに影響を与える可能性があります(たとえば、esi
を使用します) 行を見つけるために)、慎重に検討する必要があります。
前述のように、別のオプションは、特に過去の日付の行を追加または変更しない場合(またはトリガーを使用して行を最新の状態に保つことができる場合)、事前に計算された値を格納する要約テーブルを作成することです。