Déconnexion au serveur mysql intempestive

Petit nouveau ! | 6 Messages

26 mai 2005, 13:40

Bonjour à tous. Je m'arrache un peu les cheveux, et j'aurais grand besoin de votre avis éclairé sur la question...

Je suis en ce moment en train de programmer un système en php/mysql me permettant d'automatiser un calcul de matrices de flux en faisant des cohortes réelles.
Tout fonctionne bien jusqu'à un type de requête précis :
Sur la base 'flux', deux tables, bea1994_flux, en gros 380 000 lignes, et ext1993_flux, en gros 55 000 lignes.
La requête suivante passe très bien sous ACCESS et demande environ 30 secondes de calcul :

Code : Tout sélectionner

SELECT bea1994_flux.ine, ext1993_flux.ine FROM bea1994_flux INNER JOIN ext1993_flux ON bea1994_flux.ine=ext1993_flux.ine GROUP BY bea1994_flux.ine, ext1993_flux.ine
Cette requête est disons la requête simplifiée du système.
Sur des tables d'essai de 450 lignes, tout se passe bien. Mais sur les tables réelles, tout plante, dès que la clause 'INNER JOIN' entre en jeu.
J'ai reproduit la chose en ligne de commande, et j'obtiens le message suivant après environ 30 secondes :
'error 2013 : la connexion au serveur mysql a été perdue pendant la requête'.
Si je demande une autre opération, il me sort :
'error 2006 : la connexion est perdue'.
Là, MySql se reconnecte et me donne un nouvel identifiant de connexion.

Pendant ce temps, l'ordinateur - sous Windows XP pro, un Pentium 4 2,2GHz avec 512Mo de RAM - plante complètement, évidemment, comme si la requête continuait à tourner dans le vide.

Je me suis dit qu'il pouvait s'agir de limitations des paramétrages par défaut, et ai donc procédé comme suit :

Code : Tout sélectionner

# mysql config file. # Copy this file to c:\my.cnf to set global options # # One can use all long options that the program supports. # Run the program with --help to get a list of available options # This will be passed to all mysql clients [client] #password=my_password port=3306 #socket=MySQL # Here is entries for some specific programs # The following values assume you have at least 32M ram # The MySQL server [mysqld] skip-innodb bind-address=127.0.0.1 port=3306 #socket=MySQL skip-locking set-variable = key_buffer=64M set-variable = max_allowed_packet=1000M set-variable = thread_stack=256K set-variable = table_cache=256k set-variable = sort_buffer=4M set-variable = net_buffer_length=8K server-id = 1 # Uncomment the following if you want to log updates #log-bin # Uncomment the following rows if you move the MySQL distribution to another # location basedir = C:/Program Files/EasyPHP1-7/mysql/ datadir = C:/Program Files/EasyPHP1-7/mysql/data/ # Uncomment the following if you are NOT using BDB tables #skip-bdb # Uncomment the following if you are using Innobase tables #innodb_data_file_path = ibdata1:100M #innodb_data_home_dir = c:\ibdata #innodb_log_group_home_dir = c:\iblogs #innodb_log_arch_dir = c:\iblogs #set-variable = innodb_mirrored_log_groups=1 #set-variable = innodb_log_files_in_group=3 #set-variable = innodb_log_file_size=5M #set-variable = innodb_log_buffer_size=8M #innodb_flush_log_at_trx_commit=1 #innodb_log_archive=0 #set-variable = innodb_buffer_pool_size=16M #set-variable = innodb_additional_mem_pool_size=200M #set-variable = innodb_file_io_threads=4 #set-variable = innodb_lock_wait_timeout=50 [mysqldump] quick set-variable = max_allowed_packet=160M [mysql] no-auto-rehash # Remove the next comment character if you are not familiar with SQL #safe-updates [isamchk] set-variable = key_buffer=64M set-variable = sort_buffer=4M [myisamchk] set-variable = key_buffer=64M set-variable = sort_buffer=4M [mysqlhotcopy] interactive-timeout *********************************
Les lignes du config d'Apage modifiées :

Code : Tout sélectionner

# Timeout: The number of seconds before receives and sends time out. # Timeout 30000
...Et le paramétrage de mysql dans php :

Code : Tout sélectionner

MySQL Support enabled Active Persistent Links 0 Active Links 0 Client API version 3.23.49 Directive Local Value Master Value mysql.allow_persistent On On mysql.connect_timeout -1 -1 mysql.default_host no value no value mysql.default_password no value no value mysql.default_port no value no value mysql.default_socket no value no value mysql.default_user no value no value mysql.max_links Unlimited Unlimited mysql.max_persistent Unlimited Unlimited mysql.trace_mode Off Off
En fait, ça ne change rien au problème...

Le error.log d'Apache ne signale rien. Je n'ai pas de log mysql. Pas d'erreur non plus dans le log Eaysy.php.

Mes versions : Apache 1.3.27, PHP 4.3.3, MySQL 4.0.15

On dirait que le serveur mySql n'est pas paramétré pour accepter des tables trop grosses, alors que je sais que ce système est tout de même sensé accepter de travailler sur des systèmes lourds...
Auriez-vous une idée pour me sortir de la panade ? Là je sèche...

Merci pour votre aide,
cordialement,
MyNight

ViPHP
ViPHP | 1380 Messages

26 mai 2005, 18:04

Plusieurs pistes de travail:

1- La journalisation des erreurs et des requête longues
Rajoute dans my.cnf ou my.ini les lignes suivantes

Code : Tout sélectionner

# journalisation des requêtes lentes dans le groupe [mysqld] [mysqld] log-slow-queries = /var/log/mysql/mysql-slow.log # journalisation des erreurs dans le groupe [safe_mysqld] [safe_mysqld] err-log = /var/log/mysql/mysql.err
Tes autres variables me semblent correctement dimensionnées.

2- Optimisation de ta requête
Ensuite, si tu ne trouves rien de ce côté, fais un EXPLAIN de ta requête:

Code : Tout sélectionner

EXPLAIN SELECT ...

Edit: complément d'information

Vérifie également les variables de config suivantes (ton my.cnf n'y touche pas, elle doivent donc avoir les valeurs par défaut)

Code : Tout sélectionner

mysql> SHOW VARIABLES LIKE '%timeout%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | connect_timeout | 5 | | delayed_insert_timeout | 300 | | innodb_lock_wait_timeout | 50 | | interactive_timeout | 28800 | | net_read_timeout | 30 | | net_write_timeout | 60 | | slave_net_timeout | 3600 | | wait_timeout | 28800 | +--------------------------+-------+
Si tu atteinds les butées de ces timeout, c'est qu'il y a un problème avec ta requête.
ripat

Petit nouveau ! | 6 Messages

27 mai 2005, 09:52

Hello Ripat !

Merci pour ta réponse :)

Les variables de config de timeout sont chez moi les mêmes que celles que tu montres.

La requête EXPLAIN donne ceci :

Code : Tout sélectionner

+--------------+------+---------------+------+---------+------+--------+---------------------------------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +--------------+------+---------------+------+---------+------+--------+---------------------------------+ | ext1993_flux | ALL | NULL | NULL | NULL | NULL | 51359 | Using temporary; Using filesort | | bea1994_flux | ALL | NULL | NULL | NULL | NULL | 338234 | Using where | +--------------+------+---------------+------+---------+------+--------+---------------------------------+ 2 rows in set (0.01 sec)
Cela te paraît-il normal ?

Voici ce que ça donne dans les logs - après plantage :

Code : Tout sélectionner

C:\PROGRA~1\EASYPH~1\MySql\bin\mysqld.exe, Version: 4.0.15-max-debug-log, started with: Tcp port: 3306 Unix socket: MySQL Time Id Command Argument # Time: 050527 9:39:51 # User@Host: root[root] @ localhost [127.0.0.1] # Query_time: 287 Lock_time: 0 Rows_sent: 0 Rows_examined: 10082 use flux; SELECT bea1994_flux.ine, ext1993_flux.ine FROM bea1994_flux INNER JOIN ext1993_flux ON bea1994_flux.ine=ext1993_flux.ine GROUP BY bea1994_flux.ine, ext1993_flux.ine;

Penses-tu que le plantage puisse venir d'un mauvais paramétrage des timeout, d'un dépassement quelconque ?...

Merci pour ton aide.

ViPHP
ViPHP | 1380 Messages

27 mai 2005, 13:14

Je crois que la solution est de, tout simplement, mettre des index sur les colonnes jointes. Essaye d'abord sur ext1993_flux.ine, fais ton SELECT, puis si ce n'est pas encore assez rapide, un autre index sur bea1994_flux.ine.

(ALTER TABLE ta_table ADD INDEX colonne)

Ca devrait marcher *beaucoup* plus vite...
ripat

Petit nouveau ! | 6 Messages

27 mai 2005, 13:58

:) Yes !!!

Pour le moment, pour la première fois, ça a fonctionné sans clash !!!

La requête a mis 21 secondes pour s'effectuer...

Merci 1000 fois, tu mets un terme à 3 jours de prise de tête dans tous les sens !!!

Dernière petite question : sais-tu s'il y a un moyen de repouser ces limitations de temps ?

ViPHP
ViPHP | 1380 Messages

27 mai 2005, 14:26

Pour les cardinalités de tes tables, 21 secondes, c'est long.

Es-tu certain que ce n'est pas l'affichage qui prend du temps?

Fais un

Code : Tout sélectionner

SELECT COUNT(*) table1 JOIN table2 ON .....
Et un nouveau EXPLAIN SELECT et dis nous ce que ça donne.
ripat

Petit nouveau ! | 6 Messages

27 mai 2005, 15:51

Voici ce que donne la requête que tu m'as demandée :

Code : Tout sélectionner

+----------+ | count(*) | +----------+ | 48084 | +----------+ 1 row in set (3.75 sec)

C'est effectivement l'affichage qui prend du temps.

La requête réelle est la suivante :

Code : Tout sélectionner

select bea1994_flux.ANNEE, bea1994_flux.MEFSTAT, mefstat11.LIB, Count(bea1994_flux.INE) AS CompteINE FROM (ext1993_flux INNER JOIN bea1994_flux ON ext1993_flux.INE=bea1994_flux.INE) INNER JOIN mefstat11 ON bea1994_flux.MEFSTAT = mefstat11.MEFSTAT GROUP BY bea1994_flux.MEFSTAT, mefstat11.LIB
Cela donne :

Code : Tout sélectionner

30 rows in set (7.63 sec)
L'explain de la requête donne :

Code : Tout sélectionner

+--------------+------+---------------+---------+---------+----------------------+-------+---------------------------------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +--------------+------+---------------+---------+---------+----------------------+-------+---------------------------------+ | ext1993_flux | ALL | INE | NULL | NULL | NULL | 51359 | Using temporary; Using filesort | | bea1994_flux | ref | INE,MEFSTAT | INE | 13 | ext1993_flux.INE | 1 | Using where | | mefstat11 | ref | MEFSTAT | MEFSTAT | 13 | bea1994_flux.MEFSTAT | 1 | Using where | +--------------+------+---------------+---------+---------+----------------------+-------+---------------------------------+ 3 rows in set (0.05 sec)
Le temps de traitement, vu le nombre de lignes, me paraît bon, qu'en penses-tu ?

ViPHP
ViPHP | 1380 Messages

27 mai 2005, 18:21

Il ne reste plus grand chose à changer. Du moins à la requête. S'il n'y a pas trop de clients simultanés et si tu vas devoir souvent faire tourner cette requête, tu peux essayer de doper ton serveur en lui faisant utiliser toute la mémoire utile.

Pour ce faire, tu peux changer la valeur de quelques variables du serveur telles que:

Code : Tout sélectionner

# pour rendre l'utilisation des index plus efficace key_buffer=64M # ou beaucoup plus si tu as de la mémoire # ce qui suit améliore les lectures séquentielles (à essayer) read_buffer_size=1M # ce qui suit devrait améliorer ton GROUP BY read_rnd_buffer_size=1M #
Pour mesurer l'impact d'un changement de valeur de ces variables tu dois:
  1. changer une variable à la fois
  2. faire ton SELECT mais !attention! tu dois court-circuiter le query_cache en rajoutant SELECT SQL_NO_CACHE table ... dans ta requête sinon, le serveur mysql, qui va voir arriver plusieurs requêtes identiques, va mettre les résulats en cache ce qui faussera le timing des requêtes suivantes. Tu ne verras pas l'impact réel de tes changements. Cette remarque vaut également quand tu fais un chronométrage de requêtes pour les optimiser.
  3. changer une autre variable (etc...)
Si tu as beaucoup de mémoire, tu peux y aller! Si tu as près de 1G de mémoire, tu peux mettre, par exemple:
key_buffer = 384M
ripat

Petit nouveau ! | 6 Messages

27 mai 2005, 22:51

Merci beaucoup Ripat. Dès lundi, j'essaye et je te tiens au courant. Bon week-end d'ici là !

Petit nouveau ! | 6 Messages

31 mai 2005, 09:30

Merci Ripat !

Tes conseils sont précieux.

A très bientôt, et merci encore !