Dine data er dårligt clustered .
InnoDB vil gemme rækker med "tætte" PK'er fysisk tæt på hinanden. Da dine underordnede tabeller bruger surrogat-PK'er, vil deres rækker blive gemt tilfældigt. Når tiden er inde til at lave beregninger for den givne række i "master"-tabellen, skal DBMS hoppe over det hele for at samle de relaterede rækker fra de underordnede tabeller.
I stedet for surrogatnøgler kan du prøve at bruge mere "naturlige" nøgler, med forældrenes PK i forkant, svarende til dette:
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)
BEMÆRK:Dette forudsætter created
's opløsning er fin nok og rating_no
blev tilføjet for at tillade flere vurderinger pr. entry_id
. Dette er blot et eksempel - du kan variere PK'erne efter dine behov.
Dette vil "tvinge" rækker, der tilhører den samme entry_id
skal opbevares fysisk tæt sammen, så en SUM eller AVG kan beregnes ved blot en rækkeviddescanning på PK/clustering-tasten og med meget få I/O'er.
Alternativt (f.eks. hvis du bruger MyISAM, der ikke understøtter clustering), cover forespørgslen med indekser, så de underordnede tabeller slet ikke berøres under forespørgsel.
Oven i det kan du denormalisere dit design og cache de aktuelle resultater i den overordnede tabel:
- Gem SUM(score_adjustments.amount) som et fysisk felt og juster det via triggere, hver gang en række indsættes, opdateres eller slettes fra
score_adjustments
. - Gem SUM(rating_adjustments.rating) som "S" og COUNT(rating_adjustments.rating) som "C". Når en række tilføjes til
rating_adjustments
, føj det til S og øg C. Beregn S/C ved kørsel for at få gennemsnittet. Håndter opdateringer og sletninger på samme måde.