2010-12-02 6 views
2

Je vois un comportement de performance dans mysqld que je ne comprends pas.Une question de performance dans MySQL

J'ai une table t avec un identifiant de clé primaire et trois colonnes de données col1, ... col4.

Les données sont dans 4 fichiers TSV 'col1.tsv', ... 'col4.tsv'. La procédure que j'utilise pour les ingérer est:

CREATE TABLE t (
    id INT NOT NULL, 
    col1 INT NOT NULL, 
    col2 INT NOT NULL, 
    col3 INT NOT NULL, 
    col4 CHAR(12) CHARACTER SET latin1 NOT NULL); 

LOAD DATA LOCAL INFILE  # POP 1 
    'col1.tsv' INTO TABLE t (id, col1); 

ALTER TABLE t ADD PRIMARY KEY (id); 

SET GLOBAL hot_keys.key_buffer_size= # something suitable 
CACHE INDEX t IN hot_keys; 
LOAD INDEX INTO CACHE t; 

DROP TABLE IF EXISTS tmpt; 
CREATE TABLE tmpt (id INT NOT NULL, val INT NOT NULL); 
LOAD DATA LOCAL INFILE 'col2.tsv' INTO TABLE tmpt tt; 
INSERT INTO t (id, col2) # POP 2 
    SELECT tt.id, tt.val FROM tmpt tt 
    ON DUPLICATE KEY UPDATE col2=tt.val; 

DROP TABLE IF EXISTS tmpt; 
CREATE TABLE tmpt (id INT NOT NULL, val INT NOT NULL); 
LOAD DATA LOCAL INFILE 'col3.tsv' INTO TABLE tmpt tt; 
INSERT INTO t (id, col3) # POP 3 
    SELECT tt.id, tt.val FROM tmpt tt 
    ON DUPLICATE KEY UPDATE col3=tt.val; 

DROP TABLE IF EXISTS tmpt; 
CREATE TABLE tmpt (id INT NOT NULL, 
    val CHAR(12) CHARACTER SET latin1 NOT NULL); 
LOAD DATA LOCAL INFILE 'col4.tsv' INTO TABLE tmpt tt; 
INSERT INTO t (id, col4) # POP 4 
    SELECT tt.id, tt.val FROM tmpt tt 
    ON DUPLICATE KEY UPDATE col4=tt.val; 

Maintenant, voici la performance que je ne comprends pas. Parfois, les requêtes POP 2 et 3 INSERT INTO ... SELECT ... ON DUPLICATE KEY UPDATE tournent très vite avec mysqld occupant 100% d'un core et d'autres fois mysqld bogs à 1% de lecture CPU t.MYD, ie les données MyISAM de la table t fichier, à des décalages aléatoires.

J'ai eu beaucoup de mal à isoler dans quelles circonstances il est rapide et où il est lent, mais je l'ai trouvé un cas reproductible:

Dans la séquence ci-dessus, POP 2 et 3 sont très lents . Mais si je crée t sans col4 alors POP 2 et POP 3 sont très rapides. Pourquoi?

Et si, après cela, j'ajoute col4 avec une requête ALTER TABLE alors POP 4 exécute aussi très rapidement .

Encore une fois, lorsque les INSERT sont lents, mysqld est embourbé dans le fichier IO en lisant des décalages aléatoires dans le fichier de données MyISAM de la table t. Je ne comprends même pas pourquoi il lit ce fichier.

Version du serveur MySQL 5.0.87. OS X 10.6.4 sur Core 2 Duo iMac.


MISE À JOUR

J'ai finalement trouvé (ce que je pense) la réponse à cette question. La différence mystérieuse entre certains inserts étant lente et d'autres rapide dépend des données.

L'indice était: lorsque l'insertion est lente, mysqld cherche en moyenne 0,5 Go entre les lectures sur t.MYD. Quand il est rapide, les lectures successives ont de petits décalages relatifs. La confusion est survenue parce que certains des fichiers "col? .tsv" ont leurs rangs dans le même ordre de grandeur. la colonne id tandis que d'autres sont ordonnées au hasard par rapport à eux.

J'ai été en mesure de réduire considérablement le temps de traitement global en utilisant sort (1) sur les fichiers tsv avant de les charger et de les insérer.

+0

ce que vous essayez de faire - réponses droites seulement pls !! –

+0

Il lit ce fichier parce que vous insérez dans ce fichier? – Danosaure

+0

@ f00: J'essaye d'ingérer les fichiers TSV dans la table comme décrit dans les deuxième et troisième phrases de l'OP. –

Répondre

0

C'est une question assez ouverte ... voici une réponse ouverte et spéculative. :)

... lorsque les INSERT s'exécutent lentement, mysqld est embourbé dans le fichier IO en lisant des décalages aléatoires dans le fichier de données MyISAM de la table t. Je ne comprends même pas pourquoi il lit ce fichier.

Je peux penser à deux explications possibles:

  1. Même après qu'il sait qu'il ya une collision clé primaire, il faut voir ce qui était dans le champ qui sera mis à jour - si elle est par coïncidence la valeur de destination déjà, 0 dans ce cas, il ne sera pas effectuer la mise à jour - c'est-à-dire zéro lignes affectées.
  2. En outre, lorsque vous mettez à jour un champ, je crois que MySQL réécrit toute la ligne sur le disque (sinon plusieurs lignes en raison de la pagination), et non juste ce seul champ comme on pourrait le supposer.

Mais si je crée t sans col4 puis POP 2 et POP 3 sont très rapides. Pourquoi?

Si elle est une ligne fixe taille table MyISAM, qu'il ressemble en raison des types de données dans le tableau, puis y compris le champ CHAR, même si elle est vide, fera la table 75% plus grand sur le disque (4 octets par INT field = 16 octets, alors que CHAR(12) ajouterait 12 octets supplémentaires). Donc, en théorie, vous devrez lire/écrire 75% de plus.

Est-ce que votre jeu de données est en mémoire? Avez-vous envisagé d'utiliser des tables InnoDB ou Memory?

Addendum

Si l'ensemble de données utilisable/actif/chaud passe de montage en mémoire pour ne convient pas en mémoire, un ordre de diminution de l'ampleur de la performance est pas rare. Un couple se lit comme suit:

http://jayant7k.blogspot.com/2010/10/foursquare-outage-post-mortem.html

http://www.mysqlperformanceblog.com/2010/11/19/is-there-benefit-from-having-more-memory/

+0

Commentaires intéressants. Merci. La taille de la ligne est fixe. Les données ne sont pas toutes en mémoire. C'est une fois, lisez souvent l'application. Je ne pense pas que la différence de longueur de ligne entre les deux cas (avec et sans col4) puisse être la réponse. C'est une différence entre 20 octets par ligne et 32. La différence de performance est quelque chose comme 50 ou 100 X. Dans le cas rapide, 20 millions de lignes sont insérées en 5 minutes, au ralenti c'est la plupart du temps une journée de travail. –