I tuoi dati sono mal cluster .
InnoDB memorizzerà le righe con le PK "vicine" fisicamente vicine tra loro. Poiché le tabelle figlie utilizzano PK surrogati, le loro righe verranno archiviate in modo casuale. Quando arriva il momento di fare calcoli per la riga data nella tabella "master", DBMS deve saltare dappertutto per raccogliere le righe correlate dalle tabelle figlio.
Invece di chiavi surrogate, prova a usare chiavi più "naturali", con la PK del genitore nel bordo di entrata, in modo simile a questo:
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)
NOTA:questo presuppone created
la risoluzione di ' è abbastanza buona e il rating_no
è stato aggiunto per consentire più valutazioni per entry_id
. Questo è solo un esempio:puoi variare i PK in base alle tue esigenze.
Questo "forzerà" le righe appartenenti allo stesso entry_id
da archiviare fisicamente vicini, quindi è possibile calcolare un SUM o un AVG semplicemente con una scansione dell'intervallo sulla chiave PK/clustering e con pochissimi I/O.
In alternativa (ad es. se utilizzi MyISAM che non supporta il clustering), copertina la query con gli indici in modo che le tabelle figlie non vengano toccate durante l'interrogazione.
Inoltre, potresti denormalizzare il tuo design e memorizzare nella cache i risultati correnti nella tabella padre:
- Memorizza SUM(score_adjustments.amount) come campo fisico e regolalo tramite trigger ogni volta che una riga viene inserita, aggiornata o eliminata da
score_adjustments
. - Memorizza SUM(rating_adjustments.rating) come "S" e COUNT(rating_adjustments.rating) come "C". Quando una riga viene aggiunta a
rating_adjustments
, aggiungilo a S e incrementa C. Calcola S/C in fase di esecuzione per ottenere la media. Gestisci gli aggiornamenti e le eliminazioni in modo simile.