データがひどく
InnoDBは、「近い」PKが物理的に近接している行を格納します。子テーブルは代理PKを使用するため、それらの行は事実上ランダムに格納されます。 「マスター」テーブルの特定の行の計算を行うときが来ると、DBMSは、子テーブルから関連する行を収集するために、あちこちをジャンプする必要があります。
代理キーの代わりに、次のように、親のPKをリーディングエッジにして、より「自然な」キーを使用してみてください。
score_adjustments:
entry_id: INT(11), FOREIGN KEY (entries.id)
created: DATETIME
amount: INT(4)
PRIMARY KEY (entry_id, created)
rating_adjustments:
entry_id: INT(11), FOREIGN KEY (entries.id)
rating_no: INT(11)
rating: DOUBLE
PRIMARY KEY (entry_id, rating_no)
注:これは、created
を前提としています。 の解像度は十分に良好で、rating_no
entry_id
ごとに複数の評価を許可するために追加されました 。これは単なる例です。必要に応じてPKを変更できます。
これにより、同じentry_id
に属する行が「強制」されます 物理的に近接して保存されるため、SUMまたはAVGは、PK /クラスタリングキーの範囲スキャンだけで、I/Oをほとんど使用せずに計算できます。
または(たとえば、クラスタリングをサポートしていないMyISAMを使用している場合)、カバー インデックスを使用したクエリ。クエリ中に子テーブルがまったく変更されないようにします。
さらに、設計を非正規化し、現在の結果を親テーブルにキャッシュすることができます:
- SUM(score_adjustments.amount)を物理フィールドとして保存し、
score_adjustments
から行が挿入、更新、または削除されるたびに、トリガーを介して調整します。 。 - SUM(rating_adjustments.rating)を「S」として保存および COUNT(rating_adjustments.rating)を「C」として。行が
rating_adjustments
に追加されたとき 、それをSに追加し、Cをインクリメントします。実行時にS / Cを計算して、平均を取得します。更新と削除を同様に処理します。