Voici plusieurs choses que j'essaierais, par ordre de difficulté croissante :
(plus facile) - Assurez-vous d'avoir le bon index de couverture
CREATE INDEX ix_temp ON relations (relation_title, object_title);
Cela devrait maximiser les performances compte tenu de votre schéma existant, car (à moins que votre version de l'optimiseur de MySQL ne soit vraiment stupide !) cela minimisera la quantité d'E/S nécessaires pour satisfaire votre requête (contrairement si l'index est dans l'ordre inverse où l'index entier doit être scanné) et il couvrira la requête afin que vous n'ayez pas à toucher à l'index clusterisé.
(un peu plus difficile) - assurez-vous que vos champs varchar sont aussi petits que possible
L'un des défis de performance avec les index varchar sur MySQL est que, lors du traitement d'une requête, la taille complète déclarée du champ sera extraite dans la RAM. Donc, si vous avez un varchar(256) mais que vous n'utilisez que 4 caractères, vous payez toujours l'utilisation de la RAM de 256 octets pendant le traitement de la requête. Aie! Donc, si vous pouvez facilement réduire vos limites de varchar, cela devrait accélérer vos requêtes.
(plus difficile) - Normaliser
30% de vos lignes ayant une seule valeur de chaîne est un cri clair pour la normalisation dans une autre table afin de ne pas dupliquer les chaînes des millions de fois. Envisagez de normaliser en trois tables et d'utiliser des ID entiers pour les joindre.
Dans certains cas, vous pouvez normaliser sous les couvertures et masquer la normalisation avec des vues qui correspondent au nom de la table actuelle ... alors vous n'avez qu'à rendre vos requêtes INSERT/UPDATE/DELETE conscientes de la normalisation mais vous pouvez laisser vos SELECT seuls .
(le plus difficile) - Hachez vos colonnes de chaîne et indexez les hachages
Si normaliser signifie changer trop de code, mais que vous pouvez modifier un peu votre schéma, vous pouvez envisager de créer des hachages 128 bits pour vos colonnes de chaîne (en utilisant le Fonction MD5 ). Dans ce cas (contrairement à la normalisation), vous n'avez pas à modifier toutes vos requêtes, uniquement les INSERT et certains des SELECT. Quoi qu'il en soit, vous voudrez hacher vos champs de chaîne, puis créer un index sur les hachages, par ex.
CREATE INDEX ix_temp ON relations (relation_title_hash, object_title_hash);
Notez que vous devrez jouer avec le SELECT pour vous assurer que vous effectuez le calcul via l'index de hachage et que vous n'extrayez pas l'index clusterisé (nécessaire pour résoudre la valeur de texte réelle de object_title afin de satisfaire la requête).
De plus, si relation_title a une petite taille varchar mais que le titre de l'objet a une taille longue, vous pouvez potentiellement hacher uniquement object_title et créer l'index sur (relation_title, object_title_hash)
.
Notez que cette solution n'est utile que si l'un de ces champs ou les deux sont très longs par rapport à la taille des hachages.
Notez également qu'il existe des impacts intéressants sur la sensibilité à la casse/le classement du hachage, car le hachage d'une chaîne en minuscules n'est pas le même que le hachage d'une chaîne en majuscules. Vous devrez donc vous assurer d'appliquer la canonisation aux chaînes avant de les hacher - en d'autres termes, hachez uniquement les minuscules si vous êtes dans une base de données insensible à la casse. Vous pouvez également supprimer les espaces du début ou de la fin, selon la façon dont votre base de données gère les espaces de début/fin.