Vaše data jsou špatně seskupená .
InnoDB bude ukládat řádky s "blízkými" PK fyzicky blízko sebe. Protože vaše podřízené tabulky používají náhradní PK, budou jejich řádky uloženy náhodně. Když přijde čas provést výpočty pro daný řádek v "hlavní" tabulce, musí DBMS přeskakovat všude, aby shromáždil související řádky z podřízených tabulek.
Místo náhradních klíčů zkuste použít „přirozenější“ klíče s rodičovským PK na přední hraně, podobně jako toto:
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)
POZNÁMKA:Toto předpokládá created
Rozlišení je dostatečně dobré a rating_no
bylo přidáno, aby bylo možné více hodnocení na entry_id
. Toto je jen příklad – PK můžete měnit podle svých potřeb.
Tím se "vynutí" řádky patřící stejnému entry_id
být uloženy fyzicky blízko u sebe, takže SUM nebo AVG lze vypočítat pouhým skenováním rozsahu na klíči PK/clustering a s velmi malým počtem I/O.
Případně (např. pokud používáte MyISAM, který nepodporuje shlukování), obálka dotaz s indexy, takže podřízené tabulky se během dotazování vůbec nedotknou.
Kromě toho můžete denormalizovat svůj návrh a uložit aktuální výsledky do mezipaměti v nadřazené tabulce:
- Uložte SUM(score_adjustments.amount) jako fyzické pole a upravte jej pomocí spouštěčů při každém vložení, aktualizaci nebo odstranění řádku z
score_adjustments
. - Uložte SUM(rating_adjustments.rating) jako "S" a COUNT(rating_adjustments.rating) jako "C". Když je přidán řádek do
rating_adjustments
, přidejte jej k S a zvyšte C. Vypočítejte S/C za běhu, abyste získali průměr. S aktualizacemi a mazáním zacházejte podobně.