Nous avons un site social où les membres peuvent s'évaluer pour la compatibilité ou la correspondance. Cette user_match_ratings
La table contient plus de 220 millions de lignes (9 gig data ou presque 20 gig dans les index). Les requêtes sur cette table s'affichent régulièrement dans slow.log (seuil> 2 secondes) et constituent la requête lente la plus fréquemment enregistrée dans le système:
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"
Query_time: 4 Lock_time: 0 Rows_sent: 3 Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"
Query_time: 5 Lock_time: 0 Rows_sent: 3 Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"
Query_time: 17 Lock_time: 0 Rows_sent: 3 Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"
SHOW COLUMNS FROM user_match_ratings;
Donne:
╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
║ id ║ int(11) ║ NO ║ PRI ║ NULL ║ auto_increment ║
║ rater_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rated_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rating ║ varchar(1) ║ NO ║ ║ NULL ║ ║
║ created_at ║ datetime ║ NO ║ ║ NULL ║ ║
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝
select * from mutual_match_ratings where id=221673540;
donne:
╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
║ id ║ rater_user_id ║ rated_user_id ║ rating ║ created_at ║
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
║ 221673540 ║ 5699713 ║ 3890950 ║ N ║ 2013-04-09 13:00:38 ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝
La table comporte 3 index:
rated_user_id
rater_user_id
et created_at
rated_user_id
et rater_user_id
affichez l'index de user_match_ratings;
donne:
╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
║ Table ║ Non_unique ║ Key_name ║ Seq_in_index ║ Column_name ║ Collation ║ Cardinality ║ Sub_part ║ Packed ║ Null ║ Index_type ║ Comment ║
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
║ user_match_ratings ║ 0 ║ PRIMARY ║ 1 ║ id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 1 ║ rater_user_id ║ A ║ 11039059 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 2 ║ created_at ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 1 ║ rated_user_id ║ A ║ 4014203 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 2 ║ rater_user_id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index3 ║ 1 ║ rated_user_id ║ A ║ 2480687 ║ NULL ║ NULL ║ BTREE ║ ║ ║
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝
Même avec les index, ces requêtes sont lentes.
Est-ce que la séparation de cette table/données vers une autre base de données sur un serveur qui a suffisamment de RAM pour stocker ces données en mémoire accélérerait-elle ces requêtes? Y a-t-il quoi que ce soit dans la façon dont les tables/index sont configurés que nous pouvons améliorer pour accélérer ces requêtes?
Actuellement, nous avons 16 Go de mémoire; Cependant, nous envisageons de mettre à niveau la machine existante à 32 Go ou d'ajouter une nouvelle machine avec au moins autant, peut-être des disques SSD.
Réflexions sur la question, jetées dans un ordre aléatoire:
L'index évident pour cette requête est: (rated_user_id, rating)
. Une requête qui obtient des données pour un seul million d'utilisateurs et nécessite 17 secondes fait quelque chose de mal: lire dans l'index (rated_user_id, rater_user_id)
Puis lire dans la table les valeurs (des centaines à des milliers) pour le rating
colonne, car rating
n'est dans aucun index. Ainsi, la requête doit lire de nombreuses lignes de la table qui se trouvent dans de nombreux emplacements de disque différents.
Avant de commencer à ajouter de nombreux index dans les tableaux, essayez d'analyser les performances de l'ensemble de la base de données, de l'ensemble des requêtes lentes, réexaminez les choix des types de données, le moteur que vous utilisez et les paramètres de configuration.
Envisagez de passer à une version plus récente de MySQL, 5.1, 5.5 ou même 5.6 (également: versions Percona et MariaDB.) Plusieurs avantages car les bugs ont été corrigés, l'optimiseur amélioré et vous pouvez définir le seuil bas pour les requêtes lentes à moins de 1 seconde (comme 10 millisecondes). Cela vous donnera de bien meilleures informations sur les requêtes lentes.
Le choix du type de données de rating
est bizarre. VARCHAR(1)
? Pourquoi pas CHAR(1)
? Pourquoi pas TINYINT
? Cela vous fera économiser de l'espace, à la fois dans la table et dans les index qui incluront (incluront) cette colonne. Une colonne varchar (1) a besoin d'un octet de plus sur char (1) et si elles sont utf8, les colonnes (var) char auront besoin de 3 (ou 4) octets, au lieu de 1 (tinyint).