Sus datos están mal agrupados .
InnoDB almacenará filas con PK "cerradas" físicamente juntas. Dado que sus tablas secundarias usan PK sustitutos, sus filas se almacenarán de forma aleatoria. Cuando llega el momento de hacer cálculos para la fila dada en la tabla "maestra", DBMS debe saltar por todos lados para recopilar las filas relacionadas de las tablas secundarias.
En lugar de claves sustitutas, intente usar claves más "naturales", con el PK principal en el borde delantero, similar a esto:
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:Esto supone que created
la resolución es lo suficientemente buena y la rating_no
se agregó para permitir múltiples calificaciones por entry_id
. Este es solo un ejemplo:puede variar los PK según sus necesidades.
Esto "forzará" las filas que pertenecen al mismo entry_id
almacenarse físicamente muy cerca uno del otro, por lo que se puede calcular SUM o AVG con solo un escaneo de rango en la clave PK/clustering y con muy pocas E/S.
Alternativamente (por ejemplo, si está utilizando MyISAM que no admite la agrupación), cubierta la consulta con índices para que las tablas secundarias no se toquen durante la consulta.
Además de eso, puede desnormalizar su diseño y almacenar en caché los resultados actuales en la tabla principal:
- Almacene SUM(score_adjustments.amount) como un campo físico y ajústelo a través de activadores cada vez que se inserte, actualice o elimine una fila de
score_adjustments
. - Almacenar SUM(rating_adjustments.rating) como "S" y COUNT(rating_adjustments.rating) como "C". Cuando se agrega una fila a
rating_adjustments
, agréguelo a S e incremente C. Calcule S/C en tiempo de ejecución para obtener el promedio. Maneje las actualizaciones y eliminaciones de manera similar.