巨大なテーブル(+ 1000万行)があり、日時列で時間間隔検索を使用して値を集計しています。現在、アプリで単一のページを作成するために、このテーブルに数回クエリを実行しているため、クエリの遅延が大きくなっています。
このテーブルには1つの特定のプロパティがあり、レコードは挿入後に更新されることはありません。
このシナリオには2つの解決策がありますが、どちらが優れているか、またデータベースの専門家がどちらを推奨しているのかわかりません。
例
私のデータは、次のような通貨市場に似ています: https://bitcoinity.org/markets 。どのようにして、さまざまな時間間隔(分、時間、日、月、年...)でクイッククエリを実行できますか?
このようなスキーマのよく知られたソリューションはありますか?
背景情報
スキーマの詳細
収益
CREATE TABLE `earnings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`earner_id` int(11) DEFAULT NULL,
`sale_id` int(11) DEFAULT NULL,
`amount` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_earnings_on_sale_id` (`sale_id`),
KEY `index_earnings_on_created_at` (`created_at`)
) ENGINE=Inno
サンプルクエリ
SELECT DISTINCT *, count(amount) total FROM earnings
WHERE (created_at BETWEEN '2015-09-01 07:00:00' AND '2015-10-01 06:59:59')
AND sale_id IN [....]
GROUP BY earner_id
このクエリは非常に単純であるだけでなく、月単位や過去10日間など、さまざまな期間で何度も実行されます。そのため、2番目のアイデア(2)で、各タイムスパムごとに合計をキャッシュする補助テーブルを検討しています。 (目的のデータ集計については、この例を参照してください https://bitcoinity.org/markets )
どのようにして、さまざまな時間間隔(分、時間、日、月、年...)でクイッククエリを実行できますか?
トリックは、実行時にそれをまったく行わないことです。これが標準SQLの「必要なときに選択」によって行われると考える場合は、エラーになります。
データは通常、受信時に集計され、集計された行は必要に応じて(分、時間など)別のテーブルに書き込まれます。
特に「現在から最後までのX」バータイプのクエリを表示する場合、グラフはデータのメモリ内コピーから作成されます。データベースに何度も尋ねることは、単に効率的ではありません。再生のためにログに記録しますが、最も必要なデータをメモリに保持します。
ティックデータをデータベースに保存する必要はまったくなく、集計のみです。少なくともこれが私が行うことです-ティックデータが必要なまれなケースですが、戻ってバイナリコードファイルを解析します。
確かに標準のSQLアプローチを使用できますが、パフォーマンスの低下にはかなりの費用がかかると予想されます。時系列集計は非常に具体的なシナリオです。
各SELECTで何行が集計されますか?
これは言うまでもありません-日時列にインデックスがあることを確認してください。クエリの実行内容に応じて、複合インデックスを日時と集計対象の列に配置して、インデックスのみを使用し、ディスクからのデータブロックの読み取りを回避することもできます。
SELECTクエリにEXPLAIN EXTENDEDをCREATE TABLEとともに投稿してください。JOINSを実行している場合は、それらのテーブルにもCREATE TABLEを含めてください。
テーブルエンジンにMyISAM InnoDBに切り替える を使用している場合。
インデックスが高度にフラグメント化されている場合は、 パーティション テーブルを日付で検討することをお勧めします。インデックス検索が問題の一部ではない場合、これは役に立ちません。
これらのクエリの出力をキャッシュでき、その後のリクエストがそのデータで引き続き有効である場合は、できるだけ多くのデータをキャッシュしてください。
また、my.cnfが使用パターンに合わせて設定されていることを確認してください。
追加の雑学として、InnoDBは、データファイルを高速ストレージ上に置くこと、および特に集約が行われている場合に、より多くのコアよりも高速なコアから恩恵を受けます。
[編集]Op投稿されたSQL
このクエリの最後に_ORDER BY NULL
_を追加すると、ファイルソートを削除できます。
クエリで集計されている行の数や、_sale_id
_列と_created_at
_列のカーディナリティがわからない場合、複合インデックスの方がパフォーマンスが良いかどうかを推測するのは困難です。あなたはそれをテストしたいかもしれません。
例:個別のインデックスを削除し、コンパウンドを作成します(sale_id、created_at):
ALTER TABLE earnings DROP INDEX index_earnings_on_sale_id, DROP INDEX index_earnings_on_created_at, ADD INDEX index_sale_id_created_at (sale_id, created_at);
あなたの例では、COUNT(amount)
の代わりにSUM(amount)
を実行しています。収益を総計するのではなく、erear_idでのみカウントすることを目標としている場合は、内部クエリで選択を行い、外部クエリでカウントすることもできます。これにより、クエリによってはパフォーマンスが向上する場合があります。
SELECT earner_id, COUNT(amount) AS total FROM ( SELECT earner_id, amount FROM earnings WHERE sale_id IN (428, 245) AND (created_at BETWEEN '2016-06-01 07:00:00' AND '2016-06-22 06:59:59') ) AS derived GROUP BY earner_id ORDER BY NULL