Comment puis-je accélérer "afficher les colonnes" sur MySQL?


7

Mon application dépend de l'exécution de "afficher les colonnes" pour certaines tables. Il faut environ 60 ms pour s'exécuter, tandis que toutes nos autres requêtes prennent moins d'un ms. L'interrogation information_schemadirecte est encore plus lente.

La base de données contient environ 250 bases de données, avec 100 à 200 tables par base de données (environ 20k tables au total).

  • Comment savoir pourquoi ces opérations sont si lentes?
  • Y a-t-il peut-être un paramètre que je peux changer pour le faire fonctionner plus rapidement ou pour le mettre en cache côté SQL?

(L'application effectue environ 14 requêtes de ce type par chargement de page - je suis bien conscient que ce code hérité doit être nettoyé, mais je recherche des options possibles pendant que je travaille sur le correctif à long terme.)


1
par intérêt, dans quel scénario 60 ms seraient-ils trop lents pour examiner les colonnes d'une table? Ce n'est pas quelque chose que vous devriez faire à chaque demande

1
Que voulez-vous dire afficher les colonnes? Supprimer les noms des colonnes d'un tableau ou imprimer une colonne entière? Si ce sont les noms ... pourquoi ne pas simplement le saisir une fois et le stocker dans votre application ... si ce n'est pas possible, pourquoi ne pas simplement créer une autre table contenant toutes les colonnes basées sur la table?

@Jaitsu: Non, ce n'est pas quelque chose que nous devrions faire, mais c'est comme ça. Code hérité. Jusqu'à ce que j'aie le temps de le nettoyer et de le faire correctement, je veux voir si je peux l'accélérer. J'ai environ 14 d'entre eux qui exécutent chaque chargement de page.

@FlorinStingaciu: Oui, les noms des colonnes. Les placer dans une autre table peut accélérer les choses, mais cela se désynchronisera, ce qui va à l'encontre de l'objectif de demander directement la table.

1
@Mat: Pas une mauvaise idée. A voté pour migrer vers dba.

Réponses:


12

MySQL recalcule les statistiques des tables pour certaines opérations qui accèdent aux INFORMATION_SCHEMAtables ( SHOW COLUMNSn'est qu'un alias pratique pour les requêtes INFORMATION_SCHEMA.COLUMNS). Définissez innodb_stats_on_metadata sur false, ce qui empêchera ce recalcul de se produire lorsque vous demandez des métadonnées à partir de la table.

SET GLOBAL innodb_stats_on_metadata=0;

et ajoutez ce qui suit à my.cnf

[mysqld]
innodb_stats_on_metadata = 0

J'aurais dû mentionner que j'utilise réellement MyISAM. J'ai essayé de régler cela de toute façon, mais cela n'a donné aucun avantage.
mpen

Avez-vous envisagé ALTER TABLE foo ENGINE = InnoDB? :) Y a-t-il une bonne raison d'utiliser MyISAM?
Aaron Brown

Je pense que les raisons héritées sont principalement. J'ai peur de ce qui pourrait arriver si j'essayais; Je ne suis pas sûr que tous les FK s'aligneront. Je vais y réfléchir un peu plus.
mpen

@AaronBrown +1 pour cette réponse, car quiconque fait face à cette situation avec une base de données entièrement InnoDB a besoin de ces informations.
RolandoMySQLDBA

1
+1 pour [mysqld]y avoir mis . Il pourrait être évident pour beaucoup que ce paramètre irait sous mysqld, mais peut ne pas être évident pour ceux qui poseraient cette question. Soit dit en passant, cela a accéléré SELECT COUNT(*)sur l'une de mes information_schematables à 6 secondes de plus d'une minute. Toujours lent, mais une énorme amélioration.
Buttle Butkus

3

Je vous suggère de créer une base de données contenant les INFORMATION_SCHEMAtables (ou uniquement celles dont vous avez besoin) en tant que répliques. Indexez-les correctement et vous obtiendrez un gain de performances.

Le problème de synchronisation entre cette base de données et INFORMATION_SCHEMAest cependant délicat.

Vous pourriez avoir une procédure qui synchronise ces tables toutes les heures ou toutes les 5 minutes (à quelle fréquence la structure des tables est-elle modifiée?).

Une autre idée serait d'utiliser le proxy MySQL pour intercepter toutes les ALTER TABLEinstructions (et CREATEet DROPet CREATE INDEXet toutes les autres instructions qui modifient les informations dont vous avez besoin), puis synchroniser le schéma d'informations répliquées après la réussite de ces instructions.


Si vous n'avez besoin que des noms de colonne et pas d'autres informations, comme le type de données, la longueur ou les index disponibles, vous pouvez peut-être remplacer l'utilisation de SHOW COLUMNSrequêtes (rapides) qui renvoient une seule ligne, avec LIMIT 1ou aucune, avec ou LIMIT 0ou:

SELECT * FROM TableName WHERE FALSE ;

Malgré les conseils généraux contre l'utilisation de SELECT *, cela peut être un cas légitime où rien d'autre n'est utile. (tout le reste, mais *peut entraîner une erreur!)


2

Dans ce cas particulier, je pense que INFORMATION_SCHEMAc'est un hareng rouge. D'après mes propres tests de SHOW COLUMNSperformances, la innodb_stats_on_metadatavariable ne semble pas faire de différence sur les tables MyISAM ou InnoDB.

Cependant, à partir du manuel MySQL 5.0 ...

Certaines conditions empêchent l'utilisation d'une table temporaire en mémoire, auquel cas le serveur utilise à la place une table sur disque:

[...]

  • Les instructions SHOW COLUMNSet Les DESCRIBEutilisent BLOBcomme type pour certaines colonnes, donc la table temporaire utilisée pour les résultats est une table sur disque.

Cela semble avoir été supprimé du manuel à partir de MySQL 5.5, mais semble toujours s'appliquer dans cette version ...

mysql> SHOW VARIABLES LIKE 'version';
+---------------+-------------------------+
| Variable_name | Value                   |
+---------------+-------------------------+
| version       | 5.5.41-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0     |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW COLUMNS FROM mysql.user;
[...snip...]
42 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1     |
+-------------------------+-------+
1 row in set (0.00 sec)

Les informations de champ renvoyées avec un jeu de résultats de requête contiennent les mêmes informations que celles renvoyées par SHOW COLUMNS, donc a SELECT * FROM my_table LIMIT 0devrait réaliser la même chose sans créer de table temporaire sur disque par requête.

Un exemple rapide pour simplement saisir les noms de champs en PHP ...

$mysql = new mysqli('localhost', 'root', '', 'my_database');
$field_names = array();
$result = $mysql->query("SELECT * FROM my_table LIMIT 0");
$fields = $result->fetch_fields();
foreach ($fields as $fields)
{
    $field_names[] = $field->name;
}
var_dump($field_names);

La récupération des informations de champ de cette manière est un peu plus difficile à décoder. Vous devrez consulter la description de la MYSQL_FIELDstructure sous-jacente pour extraire les types de données et les indicateurs, mais cela fonctionne environ 7 fois plus rapidement sur mon système.


1

J'aime la première suggestion dans la réponse de @ yerpcube (+1), mais j'aimerais proposer quelque chose

  • créer une autre instance de base de données sur le port 3307
  • mysqldump la base de données de production dans un fichier texte SQL en utilisant les options suivantes:
    • --no-data
    • --routines
    • --triggers
    • --all-databasesou --databasessuivi d'une liste de bases de données que vous souhaitez
  • Chargez le fichier texte SQL dans l'instance MySQL du port 3307

Ainsi, votre mysqldump devrait ressembler à ceci:

mysqldump --no-data --routines --triggers --all-databases > ImportFile.sql

C'est ça. À l'avenir, tout ce que vous devez faire est de vous connecter à cette instance de base de données du port 3307 et d'exécuter toute requête liée au schéma au contenu de votre cœur. Si vous connaissez une table de la base de données de production qui change, juste mysqldump le schéma de production et rechargez-le à nouveau dans l'instance du port 3307.

AVERTISSEMENT: si vous installez une instance mysql sur la même machine que la production, assurez-vous absolument de vous connecter à cette instance en utilisant

mysql -u... -p... -h127.0.0.1 -P3307 < ImportFile.sql

Si vous exécutez

mysql -u... -p... -P3307 < ImportFile.sql

Il arrosera la production. Donc sois prudent !!!!

Une alternative serait d'utiliser simplement un serveur DB séparé.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.