Din data är dåligt klustrad .
InnoDB kommer att lagra rader med "nära" PK:er fysiskt nära varandra. Eftersom dina underordnade tabeller använder surrogat-PK:er kommer deras rader att lagras slumpmässigt. När det är dags att göra beräkningar för den givna raden i "master"-tabellen, måste DBMS hoppa överallt för att samla de relaterade raderna från de underordnade tabellerna.
Istället för surrogatnycklar, försök att använda mer "naturliga" nycklar, med förälderns PK i framkant, liknande detta:
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)
OBS:Detta förutsätter att created
s upplösning är tillräckligt bra och rating_no
lades till för att tillåta flera betyg per entry_id
. Detta är bara ett exempel - du kan variera PK efter dina behov.
Detta kommer att "tvinga" rader som tillhör samma entry_id
lagras fysiskt nära varandra, så att en SUMMA eller AVG kan beräknas med enbart en räckviddsskanning på PK/klustringsnyckeln och med väldigt få I/O.
Alternativt (t.ex. om du använder MyISAM som inte stöder klustring), omslag frågan med index så att de underordnade tabellerna inte berörs alls under sökning.
Utöver det kan du avnormalisera din design och cachelagra de aktuella resultaten i den överordnade tabellen:
- Lagra SUM(score_adjustments.amount) som ett fysiskt fält och justera det via triggers varje gång en rad infogas, uppdateras eller tas bort från
score_adjustments
. - Lagra SUM(rating_adjustments.rating) som "S" och COUNT(rating_adjustments.rating) som "C". När en rad läggs till i
rating_adjustments
, lägg till det till S och öka C. Beräkna S/C vid körning för att få medelvärdet. Hantera uppdateringar och raderingar på liknande sätt.