Question

Comme c'est mon premier post, il semble que je ne peux poster 1 lien pour que j'ai énuméré les sites que je fais allusion au fond. En un mot, mon objectif est de rendre la base de données renvoient les résultats plus rapidement, je l'ai essayé d'inclure autant d'informations pertinentes que je pouvais penser au cadre de l'aide Les questions au bas du poteau.

Info machine


8 processors
model name      : Intel(R) Xeon(R) CPU           E5440  @ 2.83GHz
cache size      : 6144 KB
cpu cores       : 4 

top - 17:11:48 up 35 days, 22:22, 10 users,  load average: 1.35, 4.89, 7.80
Tasks: 329 total,   1 running, 328 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni, 87.4%id, 12.5%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   8173980k total,  5374348k used,  2799632k free,    30148k buffers
Swap: 16777208k total,  6385312k used, 10391896k free,  2615836k cached

Cependant, nous cherchons à déplacer l'installation de MySQL vers une autre machine du cluster qui a 256 Go de RAM

Tableau Infos


Mon MySQL Table ressemble à

CREATE TABLE ClusterMatches 
(
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    cluster_index INT, 
    matches LONGTEXT,
    tfidf FLOAT,
    INDEX(cluster_index)   
);

Il a environ 18M lignes, il y a 1M de cluster_index unique et 6K matchs uniques. La requête SQL je générer en PHP ressemble.

requête SQL


$sql_query="SELECT `matches`,sum(`tfidf`) FROM 
(SELECT * FROM Test2_ClusterMatches WHERE `cluster_index` in (".$clusters.")) 
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) DESC LIMIT 0, 10;";

où $ cluster contient une chaîne d'environ 3 000 séparés par des virgules de cluster_index. Cette requête utilise environ 50 000 lignes et prend environ 15 secondes pour exécuter, lorsque la même requête est exécutée à nouveau, il faut environ 1 s à courir.

Utilisation


  1. Le contenu de la table peut être supposée être statique.
  2. Faible nombre d'utilisateurs simultanés
  3. La requête ci-dessus est actuellement la seule requête qui sera exécutée sur la table

sous-requête


Sur la base de ce poste [stackoverflow: Cache / Réutiliser une sous-requête MySQL]. [1] et l'amélioration dans le temps de requête Je crois que mon sous-requête peut être indexé

mysql> EXPLAIN EXTENDED SELECT `matches`,sum(`tfidf`) FROM 
(SELECT * FROM ClusterMatches WHERE `cluster_index` in (1,2,...,3000) 
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) ASC LIMIT 0, 10;

+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
| id | select_type | table                | type  | possible_keys | key           | key_len | ref  | rows  | Extra                           |
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
|  1 | PRIMARY     |  derived2            | ALL   | NULL          | NULL          | NULL    | NULL | 48528 | Using temporary; Using filesort | 
|  2 | DERIVED     | ClusterMatches       | range | cluster_index | cluster_index | 5       | NULL | 53689 | Using where                     | 
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+

D'après cet article ancien [Optimizing MySQL: Requêtes et index] [2] dans les informations supplémentaires - les mauvais à voir ici sont « à l'aide temporaire » et « en utilisant filesort »

Info Configuration MySQL


cache de requête est disponible, mais efficacement mis hors tension lorsque la taille est actuellement fixé à zéro


mysqladmin variables;
+---------------------------------+----------------------+
| Variable_name                   | Value                |
+---------------------------------+----------------------+
| bdb_cache_size                  | 8384512              | 
| binlog_cache_size               | 32768                | 
| expire_logs_days                | 0                    |
| have_query_cache                | YES                  | 
| flush                           | OFF                  |
| flush_time                      | 0                    |
| innodb_additional_mem_pool_size | 1048576              |
| innodb_autoextend_increment     | 8                    |
| innodb_buffer_pool_awe_mem_mb   | 0                    |
| innodb_buffer_pool_size         | 8388608              |
| join_buffer_size                | 131072               |
| key_buffer_size                 | 8384512              |
| key_cache_age_threshold         | 300                  |
| key_cache_block_size            | 1024                 |
| key_cache_division_limit        | 100                  |
| max_binlog_cache_size           | 18446744073709547520 | 
| sort_buffer_size                | 2097144              |
| table_cache                     | 64                   | 
| thread_cache_size               | 0                    | 
| query_cache_limit               | 1048576              |
| query_cache_min_res_unit        | 4096                 |
| query_cache_size                | 0                    |
| query_cache_type                | ON                   |
| query_cache_wlock_invalidate    | OFF                  |
| read_rnd_buffer_size            | 262144               |
+---------------------------------+----------------------+

Sur la base de cet article sur [Mysql tournant Base de données de performance] [3] Je crois que les valeurs que je dois tweak sont

  1. table_cache
  2. key_buffer
  3. sort_buffer
  4. record_buffer
  5. record_rnd_buffer (GROUP BY et ORDER BY termes)

Domaines d'amélioration identifiés - MySQL tweaks requête


  1. Modification du type de données pour les matchs à un indice qui est un pointage int à une autre table [MySQL va en effet utiliser un format de ligne de dynamique si elle contient des champs de longueur variable comme le texte ou blob, qui, dans ce cas, des moyens de tri a besoin d'être fait sur le disque. La solution est de ne pas qu'ils renoncent à ces types de données, mais plutôt de se séparer de ces champs dans une table associée.] [4]
  2. L'indexation de la nouvelle match_index Feild afin que le GROUP BY matches se produit plus rapidement, en fonction de la déclaration [ « Vous devriez probablement créer des indices pour tout champ sur lequel vous sélectionnez, le regroupement, la commande ou l'assemblage. »] [5]

Outils


Pour effectuer tweak Je prévois d'utiliser

  1. [Expliquer] [6] faisant référence à [le format de sortie] [7]
  2. [ab - Outil d'étalonnage du serveur HTTP Apache] [8]
  3. [Profilage] [9] [données du journal] [10]

Future Database Taille


Le but est de construire un système qui peut avoir 1 M valeurs cluster_index uniques 1M valeurs de correspondance, environ 3.000.000.000 lignes de la table avec un temps de réponse à la requête d'environ 0.5s (nous pouvons ajouter plus de RAM que nécessaire et distribuer la base de données à travers le cluster)

Questions


  1. Je pense que nous voulons garder l'ensemble recordset dans la RAM de sorte que la requête ne marche pas toucher le disque, si nous gardons la base de données entière dans le cache de MySQL qui n'éliminent le besoin de memcachedb?
  2. essaye de maintenir la base de données entière dans le cache MySQL une mauvaise stratégie comme pas conçu pour être persistant? Would quelque chose comme memcachedb ou Redis être une meilleure approche, si oui, pourquoi?
  3. est la table temporaire « résultat » qui est créé par la requête automatiquement détruit lorsque les finitions de la requête?
  4. Faut-il passer de InnoDB MyISAM [comme bon pour la lecture lourde data où comme InnoDB est bon pour les poids lourds d'écriture] [11]?
  5. mon cache ne marche pas semble être sur zéro comme dans mon [Configuration du cache de requêtes] [12], pourquoi la requête se produit actuellement plus la deuxième fois que je le lance?
  6. je peux restructurer ma requête pour éliminer « l'utilisation temporaire » et « en utilisant filesort » survenant, devrais-je utiliser une jointure au lieu d'une sous-requête?
  7. Comment voyez-vous la taille de MySQL [Cache de données] [13]?
  8. quel genre de tailles pour les valeurs table_cache, key_buffer, sort_buffer record_buffer, record_rnd_buffer suggérez-vous comme point de départ?

Liens


  • 1: stackoverflow.com/questions/658937/cache-re-use-a-subquery-in-mysql
  • 2: databasejournal.com/features/mysql/article.php/10897_1382791_4/Optimizing-MySQL-Queries-and-Indexes.htm
  • 3: debianhelp.co.uk/mysqlperformance.htm
  • 4: 20bits.com/articles/10-tips-for-optimizing-mysql-queries-that-dont-suck /
  • 5: 20bits.com/articles/10-tips-for-optimizing-mysql-queries-that-dont-suck /
  • 6: dev.mysql.com/doc/refman/5.0/en/explain.html
  • 7: dev.mysql.com/doc/refman/5.0/en/explain-output.html
  • 8: httpd.apache.org/docs/2.2/programs/ab.html
  • 9: mtop.sourceforge.net /
  • 10: dev.mysql.com/doc/refman/5.0/en/slow-query-log.html
  • 11: 20bits.com/articles/10-tips-for-optimizing-mysql-queries-that-dont-suck /
  • 12: dev.mysql.com/doc/refman/5.0/en/query-cache-configuration.html
  • 13: dev.mysql.com/tech-resources/articles/mysql-query-cache.html
Était-ce utile?

La solution

Modification de la table


D'après les conseils donnés dans cet article sur Comment choisir les index pour et par ordre groupe par des requêtes la table ressemble maintenant

CREATE TABLE ClusterMatches 
(
    cluster_index INT UNSIGNED, 
    match_index INT UNSIGNED,
    id INT NOT NULL AUTO_INCREMENT,
    tfidf FLOAT,
    PRIMARY KEY (match_index,cluster_index,id,tfidf)
);
CREATE TABLE MatchLookup 
(
    match_index INT UNSIGNED NOT NULL PRIMARY KEY,
    image_match TINYTEXT
);

L'élimination des sous-requêtes

La requête sans trier les résultats par la somme (TFIDF) ressemble

SELECT match_index, SUM(tfidf) FROM ClusterMatches 
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;

Ce qui élimine l'utilisation temporaire et l'utilisation filesort

explain extended SELECT match_index, SUM(tfidf) FROM ClusterMatches 
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
| id | select_type | table                | type  | possible_keys | key     | key_len | ref  | rows  | Extra                    |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
|  1 | SIMPLE      | ClusterMatches       | range | PRIMARY       | PRIMARY | 4       | NULL | 14938 | Using where; Using index | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+

Tri problème

Cependant, si j'ajoute ORDER BY SUM (tfdif) dans

SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index 
ORDER BY total DESC LIMIT 0,10;
+-------------+--------------------+
| match_index | total              |
+-------------+--------------------+
|         868 |   0.11126546561718 | 
|        4182 | 0.0238558370620012 | 
|        2162 | 0.0216601379215717 | 
|        1406 | 0.0191618576645851 | 
|        4239 | 0.0168981291353703 | 
|        1437 | 0.0160425212234259 | 
|        2599 | 0.0156466849148273 | 
|         394 | 0.0155945559963584 | 
|        3116 | 0.0151005545631051 | 
|        4028 | 0.0149106932803988 | 
+-------------+--------------------+
10 rows in set (0.03 sec)

Le résultat est avantageusement rapide à cette échelle mais ayant les ORDER BY moyens SUM (en TFIDF) il utilise temporaire et filesort

explain extended SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches 
WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY match_index 
ORDER BY total DESC LIMIT 0,10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
| id | select_type | table                | type  | possible_keys | key     | key_len | ref  | rows  | Extra                                                     |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
|  1 | SIMPLE      | ClusterMatches       | range | PRIMARY       | PRIMARY | 4       | NULL | 65369 | Using where; Using index; Using temporary; Using filesort | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+

Solutions possibles?

Im la recherche d'une solution qui n'utilise pas temporaire ou filesort, le long des lignes de

SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches 
WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY cluster_index, match_index 
HAVING total>0.01 ORDER BY cluster_index;
où j'ai besoin de coder un seuil pour un total, toutes les idées?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top