Le problème:
Nous avons un site social où les membres peuvent s'évaluer pour la compatibilité ou la correspondance. Ce user_match_ratings
tableau 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-gnu
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 ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝
Les index
La table comporte 3 index:
- index unique sur
rated_user_id
- indice composite sur
rater_user_id
etcreated_at
- indice composite sur
rated_user_id
etrater_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.
la source
SELECT QUERY
. Souhaitez-vous s'il vous plaît suggérer? PS Votre question m'a forcé à rejoindre cette communauté (y);)Réponses:
Réflexions sur la question, jetées dans un ordre aléatoire:
L'indice é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'(rated_user_id, rater_user_id)
index puis lire dans le tableau les valeurs (des centaines à des milliers) pour larating
colonne, commerating
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.
Pensez à passer à une version plus récente de MySQL, 5.1, 5.5 ou même 5.6 (également: versions Percona et MariaDB.) (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 ne pasCHAR(1)
? Pourquoi ne pasTINYINT
? 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).la source
J'ai géré des tables pour le gouvernement allemand avec parfois 60 millions d'enregistrements.
Nous avions beaucoup de ces tables.
Et nous avions besoin de connaître plusieurs fois le nombre total de lignes d'une table.
Après avoir parlé avec les programmeurs Oracle et Microsoft, nous n'étions pas si heureux ...
Ainsi, nous, le groupe des programmeurs de bases de données, avons décidé que dans chaque table est un enregistrement toujours l'enregistrement dans lequel le nombre total d'enregistrements est stocké. Nous avons mis à jour ce nombre, en fonction des lignes INSERT ou DELETE.
Nous avons essayé toutes les autres façons. C'est de loin le moyen le plus rapide.
Nous utilisons cette méthode depuis 1998 et n'avons jamais eu de mauvais nombre de lignes, dans toutes nos millions de tables d'enregistrement.
la source
count(*)
a quelques améliorations.Je vais essayer de partitionner les types de notation, comme:
mutual_match_ratings_N, mutual_match_ratings_S, etc.
Vous devez effectuer une requête pour chaque type, mais c'est peut-être plus rapide que dans l'autre sens. Essaie.
Cela suppose que vous avez un nombre fixe de types de notation et que vous n'avez pas besoin de ce tableau pour d'autres requêtes qui seraient pires avec cette nouvelle structure.
Si tel est le cas, vous devez rechercher une autre approche ou conserver deux copies de la table (votre table initiale et celles partitionnées) si cela est abordable en termes d'espace et de maintenabilité (ou logique d'application).
la source