web-dev-qa-db-fra.com

Pourquoi une requête d'insertion prend-elle parfois si longtemps?

C'est un problème assez simple. L'insertion de données dans la table fonctionne normalement, sauf quelques fois où la requête d'insertion prend quelques secondes. (Je pas essaie d'insérer des données en bloc.) J'ai donc configuré une simulation pour le processus d'insertion afin de déterminer pourquoi l'exécution de la requête d'insertion prend parfois plus de 2 secondes. Joshua a suggéré que le fichier d'index puisse être ajusté; J'ai enlevé l'id (champ de clé primaire), mais le délai se produit toujours.

J'ai une table MyISAM: daniel_test_insert (cette table commence complètement vide):

create table if not exists daniel_test_insert ( 
    id int unsigned auto_increment not null, 
    value_str varchar(255) not null default '', 
    value_int int unsigned default 0 not null, 
    primary key (id) 
)

J'y insère des données et parfois, une requête d'insertion nécessite plus de 2 secondes. Il n'y a pas de lectures sur cette table - Écrit uniquement, en série, par un seul programme threadé.

J'ai exécuté la même requête 100 000 fois pour trouver pourquoi la requête prenait beaucoup de temps. Jusqu'à présent, cela semble être un événement aléatoire.

Cette requête, par exemple, a pris 4.194 secondes (un temps très long pour une insertion):

Query: INSERT INTO daniel_test_insert SET value_int=12345, value_str='afjdaldjsf aljsdfl ajsdfljadfjalsdj fajd as f' - ran for 4.194 seconds
status               | duration | cpu_user  | cpu_system | context_voluntary | context_involuntary | page_faults_minor
starting             | 0.000042 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
checking permissions | 0.000024 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
Opening tables       | 0.000024 | 0.001000  | 0.000000   | 0                 | 0                   | 0                
System lock          | 0.000022 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
Table lock           | 0.000020 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
init                 | 0.000029 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
update               | 4.067331 | 12.151152 | 5.298194   | 204894            | 18806               | 477995           
end                  | 0.000094 | 0.000000  | 0.000000   | 8                 | 0                   | 0                
query end            | 0.000033 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
freeing items        | 0.000030 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
closing tables       | 0.125736 | 0.278958  | 0.072989   | 4294              | 604                 | 2301             
logging slow query   | 0.000099 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
logging slow query   | 0.000102 | 0.000000  | 0.000000   | 7                 | 0                   | 0                
cleaning up          | 0.000035 | 0.000000  | 0.000000   | 7                 | 0                   | 0

(Ceci est une version abrégée de la commande SHOW PROFILE, j'ai jeté les colonnes qui étaient toutes à zéro.)

Maintenant, la mise à jour contient un nombre incroyable de changements de contexte et de défauts de page mineurs. Opened_Tables augmente d’environ 1 toutes les 10 secondes sur cette base de données (ne manque pas d’espace table_cache)

Statistiques:

  • MySQL 5.0.89

  • Matériel: 32 Go de RAM/8 cœurs à 2,66 GHz; RAID 10 disques durs SCSI (SCSI II ???)

  • Les disques durs et le contrôleur RAID ont été interrogés: aucune erreur n'a été signalée .. Les processeurs sont inactifs à 50% environ.

  • iostat-x 5 (indique une utilisation inférieure à 10% pour les disques durs) charge maximale en moyenne environ 10 par 1 (normale pour notre machine à db)

  • Espace d'échange a 156k utilisé (32 Go de RAM)

Je n'arrive pas à savoir ce qui cause ce décalage. Cela ne se produit PAS sur nos esclaves à faible charge, mais uniquement sur notre maître à forte charge. Cela se produit également avec la mémoire et les tables innodb. Est-ce que quelqu'un a des suggestions? (Ceci est un système de production, donc rien d'exotique!) 

33
Daniel

J'ai remarqué le même phénomène sur mes systèmes. Les requêtes qui prennent normalement une milliseconde prendront soudainement 1-2 secondes. Toutes mes observations sont des instructions INSERT/UPDATE/REPLACE simples, à table unique, et non sur des SELECT. Aucune charge, blocage ou accumulation de fil n'est évident. 

Je pensais que c'était dû à l'effacement des pages sales, au vidage des modifications sur le disque ou à un mutex caché, mais je n'ai pas encore précisé.

Également exclu

  • Charge du serveur - pas de corrélation avec une charge élevée
  • Moteur - se produit avec InnoDB/MyISAM/Memory
  • Cache de requêtes MySQL - que ce soit activé ou non
  • Rotations de journaux - pas de corrélation dans les événements

La seule autre observation que j’ai à faire à ce stade vient du fait que j’utilise la même base de données sur plusieurs machines. J'ai une application de lecture lourde et j'utilise donc un environnement avec réplication - la majeure partie de la charge repose sur les esclaves. J'ai remarqué que même si la charge sur le maître est minimale, le phénomène se produit davantage là-bas. Même si je ne vois pas de problème de verrouillage, Innodb/Mysql a peut-être des problèmes avec la simultanéité (thread)? Rappelez-vous que les mises à jour sur l'esclave seront à thread unique.

MySQL version 5.1.48

Mettre à jour

Je pense avoir une piste pour le problème sur mon cas. Sur certains de mes serveurs, j'ai remarqué ce phénomène plus que d'autres. Voyant ce qui était différent entre les différents serveurs et peaufinant les choses, j'ai été conduit à la MySQL variable système innodbinnodb_flush_log_at_trx_commit.

J'ai trouvé le doc un peu difficile à lire, mais innodb_flush_log_at_trx_commit peut prendre les valeurs de 1,2,0:

  • Pour 1, le tampon de journal est vidé dans Le fichier journal pour chaque validation, et le fichier journal Est vidé sur le disque pour chaque validation.
  • Pour 2, le tampon de journal est vidé dans Le fichier journal pour chaque validation, et le fichier journal Est vidé sur le disque environ toutes les 1-2 secondes.
  • Pour 0, le tampon de journal est vidé dans le fichier journal Toutes les secondes et le fichier journal Est vidé sur le disque toutes les secondes.

Effectivement, dans l'ordre (1, 2, 0), comme indiqué et documenté, vous êtes censé avoir une performance croissante dans le commerce avec un risque accru.

Ceci dit, j’ai constaté que les serveurs avec innodb_flush_log_at_trx_commit=0 avaient de moins bonnes performances (c’est-à-dire 10 à 100 fois plus de "mises à jour longues") que les serveurs avec innodb_flush_log_at_trx_commit=2. De plus, les mauvaises instances se sont immédiatement améliorées lorsque je suis passé à 2 (notez que vous pouvez le changer à la volée).

Donc, ma question est la suivante: quelle est la vôtre? Notez que je ne blâme pas ce paramètre, mais que son contexte est lié à ce problème.

20
Riedsio

Le premier conseil que je vous donnerais est de désactiver la fonctionnalité autocommit et de valider manuellement.

LOCK TABLES a WRITE;
... DO INSERTS HERE
UNLOCK TABLES;

Cela améliore les performances car le tampon d'index est vidé sur le disque une seule fois, une fois toutes les instructions INSERT terminées. Normalement, il y aurait autant de vidages de tampon d'index qu'il y aurait d'instructions INSERT. 

Mais vous feriez mieux de le faire, et si cela est possible dans votre application, vous effectuez une insertion en bloc avec une seule sélection.

Cela se fait via la liaison de vecteur et c'est le moyen le plus rapide que vous puissiez utiliser.

Instead
of:
"INSERT INTO tableName values()"
DO
"INSERT INTO tableName values(),(),(),().......(n) " ,

Mais considérez cette option uniquement si la liaison de vecteur de paramètre est possible avec votre pilote mysql que vous utilisez.

Sinon, je tiendrais à la première possibilité et LOCK la table pour chaque 1000 inserts. Ne le verrouillez pas pour les insertions 100k, car vous obtiendrez un buffer overflow.

1
BitKFu

J'ai eu ce problème en utilisant les tables INNODB. (et les index INNODB sont encore plus lents à réécrire que MYISAM)

Je suppose que vous faites plusieurs autres requêtes sur d’autres tables. Le problème est donc que MySQL doit gérer les écritures sur disque dans des fichiers de plus en plus volumineux et doit allouer de l’espace supplémentaire à ces fichiers. 

Si vous utilisez les tables MYISAM, je vous suggère fortement d'utiliser 

LOAD DATA INFILE 'file-on-disk' INTO TABLE `tablename` 

commander; MYISAM est extrêmement rapide avec cela (même avec des clés primaires) et le fichier peut être formaté en tant que csv et vous pouvez spécifier les noms des colonnes (ou vous pouvez indiquer la valeur NULL pour le champ auto-incrémenté).

Voir la documentation MYSQL ici .

1
Jay

Pouvez-vous créer une table supplémentaire avec 400 colonnes (non nulles) et relancer votre test? Si le nombre d'insertions lentes augmente, cela peut indiquer que MySQL perd du temps à écrire vos enregistrements. (Je ne sais pas comment ça marche, mais il peut allouer plus de blocs ou déplacer quelque chose pour éviter la fragmentation… vraiment ne sais pas)

1
Plínio Pantaleão

Nous avons rencontré exactement le même problème et signalé ici: http://bugs.mysql.com/bug.php?id=62381

Nous utilisons 5.1.52 et nous n'avons pas encore de solution. Nous devrons peut-être désactiver le contrôle qualité pour éviter ce problème.

1
fxwan

Nous avons mis à niveau vers MySQL 5.1 et lors de cet événement, le cache Query est devenu un problème avec beaucoup de "éléments libres" états de thread. Nous avons ensuite supprimé le cache de requêtes.

La mise à niveau vers MySQL 5.1 ou la suppression du cache de requêtes a résolu ce problème.

FYI, aux futurs lecteurs.

-daniel

0
Daniel

Pour vérifier si votre disque se comporte mal et si vous êtes sous Windows, vous pouvez créer un fichier de commande par lots qui crée 10 000 fichiers:

@echo OFF
FOR /L %%G IN (1, 1, 10000) DO TIME /T > out%%G.txt

enregistrez-le dans un répertoire temporaire, comme test.cmd

Activer les extensions de commande exécutant CMD avec le paramètre/E: ON

CMD.exe /E:ON

Ensuite, exécutez votre lot et voyez si le temps entre le premier et le dernier fichier sortant diffère en secondes ou en minutes. 

Sous Unix/Linux, vous pouvez écrire un script Shell similaire. 

0
vulkanino

Pouvez-vous vérifier les statistiques sur le sous-système de disque? Est-ce que le I/O est saturé? Cela ressemble à du travail de base de données interne en cours sur le vidage des données sur disque/journal. 

0
bigtang

Y a-t-il un lecteur SSD sur le serveur? Certains disques SSD souffrent de "studder", ce qui pourrait causer votre symptôme.

Dans tous les cas, j'essaierais de savoir si le retard se produit dans MySQL ou dans le sous-système de disque.

Sous quel système d'exploitation se trouve votre serveur et sur quel système de fichiers se trouvent les données MySQL?

0
Guy Gordon

si vous utilisez plusieurs insertions en une boucle for, alors faites une pause après chaque boucle en utilisant la fonction sleep de PHP ("temps en secondes").

0
Manish

Lisez ceci sur Myisam Performance: http://adminlinux.blogspot.com/2010/05/mysql-allocating-memory-for-caches.html

Rechercher:

'La taille du bloc de clé MyISAM La taille du bloc de clé est importante' (moins les guillemets simples), c'est peut-être ce qui se passe. Je pense qu'ils ont corrigé certains de ces types de problèmes avec 5.1

0
Phill Pafford