web-dev-qa-db-fra.com

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

Je fais une mise à jour très simple sur une table, qui déclenche également un déclencheur vraiment simple, et cela me donne l'erreur

#1436 - Thread stack overrun:  6136 bytes used of a 131072 byte stack, and 128000 bytes needed.

La requête que j'exécute:

UPDATE field_values SET value = 'asaf' WHERE field_values.id =1

Le champ de valeur est un champ text. Donc, en théorie, cela pourrait devenir assez gros. Ce qui n'est pas le cas dans cette situation.

Le déclencheur qui s'exécute est:

DELIMITER $$
    CREATE TRIGGER field_value_update_trigger BEFORE UPDATE ON community_fields_values
    FOR EACH ROW BEGIN
      INSERT INTO user_field_log (user_id, field_id, value) VALUES (NEW.user_id, NEW.field_id, NEW.value);
    END;
$$
DELIMITER ;

Pourquoi cette erreur s'affiche-t-elle? Ce n'est pas comme s'il y avait une requête lourde impliquée. Notez également que la base de données est presque vide, seulement 2 lignes dans community_fields_values et aucune ligne dans le user_field_log

Version MySQL: 5.1.44

29
Rene Pot

1436 - Dépassement de pile de threads: 6136 octets utilisés d'une pile de 131072 octets et 128000 octets nécessaires.

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

malff@linux-8edv:include> pwd
/home/malff/BZR_TREE/mysql-5.1/include
malff@linux-8edv: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, fonction 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, la marge est 128000 et my_thread_stack_size est 131072.

Le seul appel à check_stack_overrun () qui essaie de réserver 128000 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 16000:

malff@linux-8edv:sql> pwd
/home/malff/BZR_TREE/mysql-5.1/sql
malff@linux-8edv: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.
  • le runtime MySQL vérifie qu'il y a au moins 128000 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.

Quelle est la question réelle, je suppose, pourquoi la thread_stack est-elle 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 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:

malff@linux-8edv:sql> pwd
/home/malff/BZR_TREE/mysql-5.1/sql
malff@linux-8edv: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 256 Ko 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, comme -DDEFAULT_THREAD_STACK (ou juste modifié la source) ... Je me demande 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 une valeur à thread_stack explicitement (et avec une valeur faible) provoquerait définitivement l'erreur vue.

Enfin, vérifiez le fichier journal du serveur pour une erreur comme 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 serveur appelle:

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

Pour faire court, l'erreur est constatée 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 s'est mal passé 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 plus évident le "comment réparer".

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 alors trivial, recherchez où thread_stack est défini dans le fichier my.cnf et supprimez le paramètre (en faisant confiance au code du serveur pour fournir une valeur par défaut décente, donc cela ne se reproduira pas la prochaine fois) ou augmentez la pile Taille.

55
Marc Alff

Bien que ce ne soit pas une solution, mais une solution rapide pourrait être d'augmenter la taille de thread_stack en l'incrémentant dans votre my.cnf:

thread_stack = 256K

Comme l'a souligné l'utilisateur "foo", la publication de l'intégralité du code de déclenchement pourrait être plus utile pour détecter le vrai problème.

33
JayTaph