Ошибка MySQL 1436: переполнение стека строк, с простым запросом - программирование

Ошибка MySQL 1436: переполнение стека строк, с простым запросом

Я делаю очень простое обновление в таблице, которое также запускает действительно простой триггер, и это дает мне ошибку

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

Выполняемый запрос:

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

Поле значений - это поле text. Так что теоретически это может стать тихим большим. Это не так в этой ситуации.

Триггер, который выполняется:

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 ;

Почему эта ошибка отображается? Это не похоже на какой-либо тяжелый запрос. Также обратите внимание, что база данных почти пуста, всего 2 строки в community_fields_values и никаких строк в user_field_log

Версия MySQL: 5.1.44

4b9b3361

Ответ 1

1436 - Переполнение стека потоков: 6136 байт, используемых для стека 131072 байт, и требуется 128000 байт.

Ошибка 1436 соответствует значению ER_STACK_OVERRUN_NEED_MORE в коде 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

Код печати ошибки отображается в 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));

Из видимых значений маржа составляет 128000, а my_thread_stack_size - 131072.

Единственный вызов check_stack_overrun(), который пытается зарезервировать 128000 байт, - это:

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);

Значение STACK_MIN_SIZE равно 16000:

[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.

Пока все работает так, как ожидалось для сервера:

  • код выполняет триггер, который реализуется с помощью sp_head:: Execute.
  • время выполнения MySQL проверяет, что в стеке имеется не менее 128000 байт.
  • эта проверка завершается неудачно (это правильно), и выполнение триггера заканчивается ошибкой.

Объем стека, необходимый для запуска триггера MySQL, зависит не от самой сложности триггера, а от содержания/структуры используемых таблиц.

Какой реальный вопрос, я думаю, почему thread_stack только на 128K (131072).

Серверная переменная с именем "thread_stack" реализована в C как "my_thread_stack_size" в 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 - минимальное значение для этого параметра. Значение по умолчанию - DEFAULT_THREAD_STACK, которое определено в 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

Итак, по умолчанию размер стека должен быть 192K (32 бита) или 256K (архитектуры 64 бит).

Сначала проверьте, как скомпилирован бинарный файл mysqld, чтобы узнать, что является значением по умолчанию:

[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

В моей системе я получил 256 КБ на 64-битной платформе.

Если существуют разные значения, возможно, кто-то построит сервер с различными параметрами компиляции, такими как -DDEFAULT_THREAD_STACK (или просто изменил исходный код)... Я бы спросил, откуда происходит двоичный файл.

Во-вторых, проверьте my.cnf на значения по умолчанию, указанные в самом файле конфигурации. Строка, устанавливающая значение в thread_stack явно (и с низким значением), окончательно приведет к ошибке.

Наконец, проверьте файл журнала сервера на наличие такой ошибки (см. sql/mysqld.cc):

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

Код сервера вызывает:

  • pthread_attr_setstacksize(), чтобы установить размер стека
  • pthread_attr_getstacksize(), чтобы проверить, сколько стека имеет поток и жалуется в журнале, если библиотека pthread используется меньше.

Короче говоря, ошибка видна, потому что thread_stack слишком мал по сравнению со значениями по умолчанию, поставляемыми с сервером. Это может произойти:

  • при выполнении пользовательских сборок сервера с различными компиляциями Параметры
  • при изменении значения по умолчанию в файле my.cnf
  • если что-то пошло не так в самой библиотеке pthread (теоретически из читая код, я никогда не видел его сам).

Надеюсь, это ответ на вопрос.

С уважением, - Марк Альф

Update (2014-03-11), чтобы сделать "как исправить" более очевидным.

Что происходит, по всей вероятности, является то, что значение по умолчанию для файла thread_stack было изменено в файле my.cnf.

Как исправить это тривиально, найдите, где thread_stack установлен в файле my.cnf, и либо удалите параметр (доверяя серверному коду, чтобы обеспечить достойное значение по умолчанию, так что это не повторится в следующий раз) или увеличьте размер стека.

Ответ 2

Хотя это не решение, но быстрым решением может быть увеличение размера thread_stack, увеличивая его в my.cnf:

thread_stack = 256K

Как указал пользователь "foo", размещение всего кода триггера может быть более полезным для обнаружения реальной проблемы.