MySql insère une charge CPU élevée

J’ai nginx recevant des requêtes POST et un petit script PHP qui met le corps de la requête sur MySql. Le problème est une utilisation très élevée du processeur MySQL quand j’ai 300 POST par seconde. Je m’attendais à ce que MySQL soit une solution rapide capable de gérer plus de 300 insertions par seconde. J’utilise la petite instance Amazon EC2, Amazon Linux.

top - 18:27:06 up 3 days, 1:43, 2 users, load average: 4.40, 5.39, 5.76 Tasks: 178 total, 4 running, 174 sleeping, 0 stopped, 0 zombie Cpu(s): 24.6%us, 13.4%sy, 0.0%ni, 0.0%id, 1.1%wa, 0.0%hi, 4.9%si, 56.0%st Mem: 1717480k total, 1640912k used, 76568k free, 193364k buffers Swap: 917500k total, 5928k used, 911572k free, 824136k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7677 mysql 20 0 313m 153m 6124 S 39.0 9.2 393:49.11 mysqld 16529 nginx 20 0 157m 151m 820 R 15.2 9.0 28:36.50 nginx 29793 php 20 0 36780 3240 1896 S 2.5 0.2 0:00.34 php-fpm 29441 php 20 0 36780 3204 1892 S 2.2 0.2 0:00.78 php-fpm 29540 php 20 0 36780 3204 1900 S 2.2 0.2 0:00.82 php-fpm 29603 php 20 0 36780 3220 1892 S 2.2 0.2 0:00.61 php-fpm 29578 php 20 0 36780 3200 1900 S 1.9 0.2 0:00.42 php-fpm 29950 php 20 0 36780 3192 1900 S 1.9 0.2 0:00.48 php-fpm 30030 php 20 0 36780 3180 1888 S 1.9 0.2 0:00.08 php-fpm 30025 php 20 0 36780 3200 1888 S 1.6 0.2 0:00.11 php-fpm 29623 php 20 0 36780 3184 1892 S 1.3 0.2 0:00.49 php-fpm 29625 php 20 0 36780 3236 1900 S 1.3 0.2 0:00.46 php-fpm 29686 php 20 0 36780 3364 1900 R 1.3 0.2 0:00.51 php-fpm 29863 php 20 0 36780 3184 1892 S 1.3 0.2 0:00.23 php-fpm 30018 php 20 0 36780 3192 1892 S 1.3 0.2 0:00.19 php-fpm 29607 php 20 0 36780 3224 1900 S 1.0 0.2 0:00.42 php-fpm 29729 php 20 0 36780 3180 1888 R 1.0 0.2 0:00.41 php-fpm 

Voici mon code PHP:

 query("insert into rawreports(date, data, project_id) values ('$date', '$data', '$project_id')") ?> 

J’ai essayé mysql_connect, mysql_pconnect, mysqli (“localhost”, …), mysqli (“p: localhost”, …) – toujours le même. Aucune requête n’est en cours d’exécution sur la firebase database, à l’exception de ces insertions.

Voici ma table:

 CREATE TABLE `rawreports` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `date` datetime NOT NULL, `data` mediumtext NOT NULL, `project_id` varchar(100) NOT NULL, PRIMARY KEY (`id`) ); 

C’est assez simple, pas d’index, juste pour stocker les données POST pour un traitement ultérieur. Dans la plupart des cas, le champ “data” contient environ 3 kilo-octets. J’ai essayé innodb et myisam – toujours le même.

Voici mon SHOW PROCESSLIST, rien que des insertions multiples:

 mysql> show processlist; +---------+----------------------+-----------+-----------------+---------+------+------------------+------------------------------------------------------------------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +---------+----------------------+-----------+-----------------+---------+------+------------------+------------------------------------------------------------------------------------------------------+ | 3872248 | root | localhost | NULL | Query | 0 | NULL | show processlist | | 3901991 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902003 | root | localhost | errorreportsraw | Sleep | 0 | | NULL | | 3902052 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902053 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902054 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902055 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902056 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902057 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902058 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902059 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902060 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"protocol_version":" | | 3902061 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902062 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902063 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902064 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902065 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902066 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902067 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902068 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902069 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902070 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902071 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902072 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902073 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902074 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902075 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902076 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902077 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902078 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902079 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902080 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902081 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902082 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902083 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902084 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902085 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902086 | root | localhost | errorreportsraw | Query | 0 | update | insert into rawreports(date, data, project_id) values ('2012-05-05 17-01-37', '{"exceptions":[{"stac | | 3902087 | unauthenticated user | localhost | NULL | Connect | NULL | Reading from net | NULL | +---------+----------------------+-----------+-----------------+---------+------+------------------+------------------------------------------------------------------------------------------------------+ 39 rows in set (0.00 sec) 

Voici le PROFIL lorsque je fais la même insertion manuellement alors que le serveur est toujours sous pression:

 set profiling=1; insert into rawreports(date, data, project_id) values('2012-05-04 00:58:08','[3000-chars-data-here]','5'); show profile ALL for query 1; Status Duration CPU_user CPU_system Context_voluntary Context_involuntary Block_ops_in Block_ops_out Messages_sent Messages_received Page_faults_major Page_faults_minor Swaps Sourc starting 0.000231 0.000000 0.000000 0 0 0 0 0 0 0 0 0 NULL NULL NULL checking permissions 0.000030 0.000000 0.000000 0 0 0 0 0 0 0 0 0 check_access sql_parse.cc 4745 Opening tables 0.000057 0.001000 0.000000 0 0 0 0 0 0 0 0 0 open_tables sql_base.cc 4836 System lock 0.000030 0.000000 0.000000 0 0 0 0 0 0 0 0 0 mysql_lock_tables lock.cc 299 init 0.000037 0.000000 0.000000 0 0 0 0 0 0 0 0 0 mysql_insert sql_insert.cc 721 update 0.075716 0.001999 0.011998 166 2 0 0 0 0 0 0 0 mysql_insert sql_insert.cc 806 Waiting for query cache lock 0.000087 0.000000 0.000000 0 0 0 0 0 0 0 0 0 lock sql_cache.cc 552 update 0.000037 0.000000 0.000000 0 0 0 0 0 0 0 0 0 NULL NULL NULL end 0.000024 0.000000 0.000000 0 0 0 0 0 0 0 0 0 mysql_insert sql_insert.cc 1049 query end 0.000042 0.000000 0.000000 0 0 0 0 0 0 0 0 0 mysql_execute_command sql_parse.cc 4434 closing tables 0.000031 0.000000 0.001000 0 0 0 0 0 0 0 0 0 mysql_execute_command sql_parse.cc 4486 freeing items 0.000126 0.000000 0.000000 0 1 0 0 0 0 0 0 0 mysql_parse sql_parse.cc 5634 logging slow query 0.000030 0.000000 0.000000 0 0 0 0 0 0 0 0 0 log_slow_statement sql_parse.cc 1460 cleaning up 0.000024 0.000000 0.000000 0 0 0 0 0 0 0 0 0 dispatch_command sql_parse.cc 1416 

J’utilise MySql 5.5.20. Essayé à la fois InnoDB et MyISAM – les deux identiques.
Voici ma sortie iostat:

 # iostat -x Linux 3.2.12-3.2.4.amzn1.i686 05/15/2012 _i686_ (1 CPU) avg-cpu: %user %nice %system %iowait %steal %idle 23.67 0.03 18.39 4.09 52.87 0.95 Device: rrqm/s wrqm/sr/sw/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util xvdap1 0.00 1.74 0.03 0.78 1.50 25.12 32.85 0.01 14.03 5.92 0.48 xvdap3 0.00 0.00 0.01 0.01 0.05 0.08 10.47 0.00 5.72 1.09 0.00 xvdf 0.40 18.59 23.14 117.55 753.12 3465.25 29.98 0.53 3.74 2.38 33.46 

La chose la plus évidente est d’insérer par lots et de les mettre tous ensemble, pas un par un. Mais je ne peux pas le faire car chaque insertion est une requête POST distincte, séparée de l’exécution du script PHP. Ils sont tous exécutés simultanément et n’interfèrent pas les uns avec les autres.

Semble être une tâche assez simple, qu’est-ce que mon processeur fait vraiment si dur? Ne pas avoir beaucoup d’expérience avec mysql, php, linux. Je manque probablement quelque chose. Merci pour toutes les idées!

Par “plus tard” traitement, vous voulez dire 1 heure ou 1 jour plus tard? Si tel est le cas, alors j’écrirais les informations dans un fichier CSV que vous faites pivoter une fois par heure et ensuite, lorsque vous devez effectuer votre traitement “ultérieur”, vous pouvez charger les fichiers dans MySQL en utilisant LOAD DATA INFILE

http://dev.mysql.com/doc/refman/5.1/en/load-data.html

LOAD DATA INFILE a chargé des centaines de Mo d’informations en moins d’une minute, cette méthode serait un excellent moyen d’accélérer votre réponse Web.

Est-ce que vous utilisez ce code PHP en boucle? Vous pouvez insérer plus d’une ligne à la fois dans une même requête, ce qui peut aider à réduire la charge du processeur. Tout ce que vous avez à faire est de fournir une liste de valeurs séparées par des virgules, par exemple:

 insert into rawreports(date, data, project_id) values (('$date1', '$data1', '$project_id1'),('$date2', '$data2', '$project_id2'), .....)") 

même si vous exécutez une boucle, vous n’avez pas besoin de répéter le new mysqli() pour chaque itération.

pas d’index,

Ce n’est pas tout à fait vrai.
Je supprimerais un index en premier lieu.
Dans une table de type journal, cela n’a aucun sens.

Par ailleurs, vous pouvez également utiliser des journaux textuels. Pour le traitement ultérieur.

Pour obtenir des informations détaillées, vous pouvez exécuter ces commandes depuis la console mysql pendant que votre serveur est sous sa charge habituelle:

 > SET profiling = 1; > INSERT an example query > SHOW PROFILE ALL 

essayez aussi idiot

 SHOW PROCESSLIST; 

qui révélera sans doute quelque chose d’utile mais au moins qui vaut la peine d’être essayé

Bien que je sois d’accord que l’utilisation d’un fichier journal en texte brut ou d’une firebase database NoSQL est une meilleure option, si vous utilisez MySQL, je pense que votre goulot d’étranglement est PHP. Utilisez-vous des connexions persistantes et des instructions préparées? Sinon, c’est une grosse perte de temps.

Vous pouvez essayer de connecter nginx directement à MySQL en utilisant le module hsock .

Notez également qu’une petite instance EC2 n’a pas de bonnes performances d’E / S. Vous devez effectuer une mise à niveau importante pour obtenir le type de performances d’E / S nécessaires pour prendre en charge 300 messages par seconde.

N’utilisez pas les données d’insertion directement dans MySQL. Créez plutôt deux fichiers csv. Dites even_rawreports.csv et odd_rawreports.csv . Maintenant, même pendant les heures paires (entre 2h00 ou 2h59), continuez à enregistrer chaque requête POST dans even_rawreports.csv et pendant les heures impaires, connectez-vous à odd_rawreports.csv .

Écrivez un travail cron qui s’exécute toutes les heures et lisez even_rawreports.csv aux heures impaires et odd_rawreports.csv aux heures paires.

Dans le cron Utilisez la requête suivante pour charger les données dans mysql directement à partir du fichier CSV en une seule requête.

 LOAD DATA INFILE 'even_rawreports.csv' INTO TABLE rawreports (col1,col2,...) FIELDS TERMINATED BY '|' OPTIONALLY ENCLOSED BY '"'; 

De toutes les réponses, je peux voir que vous pouvez avoir les choix suivants pour traiter le problème après parsing:

1) Amazon EC2 ou tout autre serveur cloud aura une vitesse d’écriture inférieure à celle d’un serveur dédié classique car les iops HDD sont partagés entre tous les serveurs virtuels hébergés sur un nœud physique. Donc, avoir une machine séparée pour DB est une bonne idée et vous pouvez même essayer Amazon Simple DB. Cela devrait valoir la peine d’essayer.

2) Vous pouvez essayer d’utiliser la firebase database NoSQL la plus simple, comme MongoDB, qui présente une multitude de vitesses dans les opérations d’écriture par rapport à MySQL. J’ai comparé cela sur ma machine locale et j’ai trouvé des résultats surprenants. Ainsi, au lieu de faire une approche CSV, vous pouvez réellement stocker votre firebase database dans NoSQL DB et, si nécessaire, la transmettre ultérieurement à MySQL pour toute requête relationnelle utilisant un travail par lots.

3) Utilisez n’importe quel opcode / accélérateur PHP si vous n’en utilisez pas.

Mais je pense que MySQL se maintient et ralentit et augmente même l’utilisation du processeur en raison de la vitesse d’écriture de l’espace disque …

Voir si l’article ci-dessous peut vous aider: http://kevin.vanzonneveld.net/techblog/article/improve_mysql_insert_performance/

Avez-vous essayé INSERT DELAYED?

J’ai lu que vous avez 300 messages / sec provenant de différents clients, mais de toute façon, vous pouvez utiliser un bundle pour insérer plusieurs lignes à la fois.

Logique séparée qui collecte les articles et insère des lignes. Vous avez donc besoin de 2 scripts – le premier vient de collecter tous les messages et enregistre les données dans un fichier par exemple. Second s’exécute périodiquement une fois par seconde et insère toutes les données dans une table.

BTW: vous pouvez utiliser NOW () pour insérer la date actuelle -> insérer dans les valeurs rawreports (date, data, project_id) (NOW (),

L’introduction d’une autre firebase database / fichier binary pour supporter MySQL semble être une option de dernier recours. Avant que vous y arriviez, j’aimerais que vous considériez ceci:

Créez une table à l’aide du MEMORY Storage Engine que vous viderez à intervalles réguliers dans votre stockage principal.

Vous pouvez essayer PDO avec une instruction préparée pour tirer parti de la communication binary entre le client et le serveur.

Modifiez simplement les parameters à chaque fois et exécutez-le.

Outre la réduction de la charge de transmission sur le serveur, cela pourrait aider à résoudre les conflits de CPU car MySQL n’aurait pas à parsingr la requête à chaque fois.

Vous devrez mettre en queue (queue) les données pour en tirer parti.

Basé sur iostat, vous sur 5.5, je pense que vous êtes limité par le processeur et les E / S de disque de votre instance d’Amazon. Pouvez-vous temporairement passer à une instance plus rapide, mesurer les résultats? Il n’y a pas beaucoup de place pour l’amélioration. La phase de mise à jour de 75ms de MySQL montre que MySQL vous tue, pas PHP. Je suis surpris que 300 soit aussi la limite. Je ne suis pas sûr des exigences d’intégrité des données, mais MongoDB peut bien fonctionner pour vous et possède même une bibliothèque PHP prise en charge.

Avez-vous essayé d’utiliser le moteur de stockage d’archives? Si vous n’avez pas besoin de mettre à jour / supprimer / remplacer, vous devriez essayer cette option. Veuillez consulter http://dev.mysql.com/doc/refman/5.0/en/archive-storage-engine.html pour plus de détails.