web-dev-qa-db-fra.com

Comment accélérer les requêtes sur une grande table de 220 millions de lignes (9 gig data)?

Le problème:

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;"

Version de MySQL:

  • version du protocole: 10
  • version: 5.0.77-log
  • version bdb: Sleepycat Software: Berkeley DB 4.1.24: (29 janvier 2009)
  • version compiler la machine: x86_64 version_compile_os: redhat-linux-gn

Informations sur la table:

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   ║                ║
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝

Exemple de requête:

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  ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝

Index

La table comporte 3 index:

  1. index unique sur rated_user_id
  2. index composite sur rater_user_id et created_at
  3. index composite sur 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.

Ma question:

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.

33
Ranknoodle

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).

31
ypercubeᵀᴹ