Mysql
 sql >> Base de données >  >> RDS >> Mysql

Erreur MySQL 1436 :dépassement de la pile de threads, avec une simple requête

1436 - Débordement de la pile de threads :6 136 octets utilisés sur une pile de 131 072 octets et 128 000 octets nécessaires.

L'erreur 1436 correspond à ER_STACK_OVERRUN_NEED_MORE dans le code mysql 5.1 :

[email protected]:include> pwd
/home/malff/BZR_TREE/mysql-5.1/include
[email protected]:include> grep 1436 mysqld_error.h
#define ER_STACK_OVERRUN_NEED_MORE 1436

Le code affichant l'erreur vue est dans sql/sql_parse.cc,function check_stack_overrun() :

bool check_stack_overrun(THD *thd, long margin,
                         uchar *buf __attribute__((unused)))
{
  long stack_used;
  DBUG_ASSERT(thd == current_thd);
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
      (long) (my_thread_stack_size - margin))
  {
    char ebuff[MYSQL_ERRMSG_SIZE];
    my_snprintf(ebuff, sizeof(ebuff), ER(ER_STACK_OVERRUN_NEED_MORE),
                stack_used, my_thread_stack_size, margin);
    my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR));

D'après les valeurs vues, margin est 128000 et my_thread_stack_size est 131072.

Le seul appel à check_stack_overrun() qui tente de réserver 128 000 octets provient de :

bool
sp_head::execute(THD *thd)
{
  /* Use some extra margin for possible SP recursion and functions */
  if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
    DBUG_RETURN(TRUE);

La valeur de STACK_MIN_SIZE est 16 000 :

[email protected]:sql> pwd
/home/malff/BZR_TREE/mysql-5.1/sql
[email protected]:sql> grep STACK_MIN_SIZE *.h
mysql_priv.h:#define STACK_MIN_SIZE          16000   // Abort if less stack during eval.

Jusqu'à présent, tout fonctionne comme prévu pour le serveur :

  • le code exécute un déclencheur, qui est implémenté avec sp_head::execute.
  • l'environnement d'exécution MySQL vérifie qu'il y a au moins 128 000 octets sur la pile
  • cette vérification échoue (à juste titre) et l'exécution du déclencheur se termine par une erreur.

La quantité de pile nécessaire à l'exécution du déclencheur MySQL ne dépend pas de la complexité du déclencheur lui-même, ni du contenu/de la structure des tables impliquées.

Qu'est-ce que le réel la question est, je suppose, pourquoi le thread_stack est-il uniquement à 128K (131072).

La variable serveur nommée 'thread_stack' est implémentée en C comme 'my_thread_stack_size' dans sql/mysqld.cc :

  {"thread_stack", OPT_THREAD_STACK,
   "The stack size for each thread.", &my_thread_stack_size,
   &my_thread_stack_size, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
   1024L*128L, ULONG_MAX, 0, 1024, 0},

1024L*128L est la valeur minimale pour ce paramètre. La valeur par défaut est DEFAULT_THREAD_STACK, qui est définie dans include/my_pthread.h :

#ifndef DEFAULT_THREAD_STACK
#if SIZEOF_CHARP > 4
/*
  MySQL can survive with 32K, but some glibc libraries require > 128K stack
  To resolve hostnames. Also recursive stored procedures needs stack.
*/
#define DEFAULT_THREAD_STACK    (256*1024L)
#else
#define DEFAULT_THREAD_STACK    (192*1024)
#endif
#endif

Ainsi, par défaut, la taille de la pile doit être de 192 Ko (32 bits) ou de 256 Ko (architectures 64 bits).

Tout d'abord, vérifiez comment le binaire mysqld a été compilé, pour voir quelle est la valeur par défaut :

[email protected]:sql> pwd
/home/malff/BZR_TREE/mysql-5.1/sql
[email protected]:sql> ./mysqld --no-defaults --verbose --help | grep thread_stack
...
  --thread_stack=#    The stack size for each thread.
thread_stack                      262144

Sur mon système, j'ai obtenu 256K sur une plate-forme 64 bits.

S'il y a des valeurs différentes, peut-être que quelqu'un construit le serveur avec différentes options de compilation, telles que -DDEFAULT_THREAD_STACK (ou vient de modifier la source) ... Je me demanderais d'où vient le binaire dans ce cas.

Deuxièmement, vérifiez my.cnf pour les valeurs par défaut fournies dans le fichier de configuration lui-même. Une ligne définissant explicitement une valeur sur thread_stack (et avec une valeur faible) provoquerait définitivement l'erreur observée.

Enfin, vérifiez le fichier journal du serveur pour une erreur telle que celle-ci (voir sql/mysqld.cc) :

sql_print_warning("Asked for %lu thread stack, but got %ld",
                  my_thread_stack_size, (long) stack_size);

Le code du serveur appelle :

  • pthread_attr_setstacksize() pour définir la taille de la pile
  • pthread_attr_getstacksize() pour vérifier la quantité de pile qu'un thread a réellement et se plaint dans le journal si la bibliothèque pthread en utilise moins.

Pour faire court, l'erreur se produit car le thread_stack est trop petit par rapport aux valeurs par défaut fournies avec le serveur. Cela peut arriver :

  • lors de la création de versions personnalisées du serveur, avec différentes options de compilation
  • lors de la modification de la valeur par défaut dans le fichier my.cnf
  • si quelque chose n'allait pas dans la bibliothèque pthread elle-même (en théorie, en lisant le code, je ne l'ai jamais vu moi-même).

J'espère que cela répond à la question.

Cordialement,-- Marc Alff

Mise à jour (2014-03-11), pour rendre le "comment réparer" plus évident.

Ce qui se passe, selon toute vraisemblance, c'est que la valeur par défaut du fichier thread_stack a été modifiée dans le fichier my.cnf.

Comment résoudre ce problème est trivial alors, trouvez où thread_stack est défini dans le fichier my.cnf, et supprimez le paramètre (faites confiance au code du serveur pour fournir une valeur par défaut décente, afin que cela ne se reproduise pas la prochaine fois) ou augmentez la pile taille.

Mise à jour (2021-04-28), vérifiez d'où vient le thread_stack :

Utiliser le tableau performance_schema.variables_info pour savoir d'où vient une variable donnée.

mysql> select * from variables_info where VARIABLE_NAME = 'thread_stack';
+---------------+-----------------+---------------+-----------+----------------------+----------+----------+----------+
| VARIABLE_NAME | VARIABLE_SOURCE | VARIABLE_PATH | MIN_VALUE | MAX_VALUE            | SET_TIME | SET_USER | SET_HOST |
+---------------+-----------------+---------------+-----------+----------------------+----------+----------+----------+
| thread_stack  | COMPILED        |               | 131072    | 18446744073709550592 | NULL     | NULL     | NULL     |
+---------------+-----------------+---------------+-----------+----------------------+----------+----------+----------+
1 row in set (0.01 sec)

Ici, la valeur par défaut est la valeur d'usine (compilée dans le binaire mysqld).

Autre exemple :

mysql> select * from variables_info where VARIABLE_NAME = 'thread_stack';
+---------------+-----------------+----------------------------------------------------------------+-----------+----------------------+----------+----------+----------+
| VARIABLE_NAME | VARIABLE_SOURCE | VARIABLE_PATH                                                  | MIN_VALUE | MAX_VALUE            | SET_TIME | SET_USER | SET_HOST |
+---------------+-----------------+----------------------------------------------------------------+-----------+----------------------+----------+----------+----------+
| thread_stack  | EXPLICIT        | /home/malff/CODE/GIT/GIT_TRUNK/build-dbg/mysql-test/var/my.cnf | 131072    | 18446744073709550592 | NULL     | NULL     | NULL     |
+---------------+-----------------+----------------------------------------------------------------+-----------+----------------------+----------+----------+----------+
1 row in set (0.00 sec)

Ici, le thread_stack est défini dans le fichier my.cnf signalé.

Réfman :

https://dev.mysql .com/doc/refman/8.0/en/performance-schema-variables-info-table.html