MySQL错误1436:线程堆栈溢出,简单查询

Ren*_*Pot 30 mysql triggers

我正在对表进行非常简单的更新,这也会触发一个非常简单的触发器,它会给我错误

#1436 - Thread stack overrun:  6136 bytes used of a 131072 byte stack, and 128000 bytes needed.
Run Code Online (Sandbox Code Playgroud)

我执行的查询:

UPDATE field_values SET value = 'asaf' WHERE field_values.id =1
Run Code Online (Sandbox Code Playgroud)

值字段是一个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 ;
Run Code Online (Sandbox Code Playgroud)

为什么会出现此错误?这不像是涉及任何繁重的查询.还要注意,数据库几乎是空的,只有2行,community_fields_values而且没有行user_field_log

MySQL版本:5.1.44

Mar*_*lff 55

1436 - 线程堆栈溢出:用于131072字节堆栈的6136个字节,需要128000个字节.

错误1436对应于mysql 5.1代码中的ER_STACK_OVERRUN_NEED_MORE:

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
Run Code Online (Sandbox Code Playgroud)

打印错误的代码在sql/sql_parse.cc中,函数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));
Run Code Online (Sandbox Code Playgroud)

根据所看到的值,保证金为128000,my_thread_stack_size为131072.

尝试保留128000字节的check_stack_overrun()的唯一调用来自:

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);
Run Code Online (Sandbox Code Playgroud)

STACK_MIN_SIZE的值是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.
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切都按预期的方式运行:

  • 代码执行一个触发器,使用sp_head :: execute实现.
  • MySQL运行时检查堆栈上至少有128000个字节
  • 此检查失败(正确如此),并且触发器执行以错误结束.

MySQL触发器执行所需的堆栈数量不取决于触发器复杂性本身或所涉及的表的内容/结构.

在什么真正的问题是,我想,为什么只在128K(131072)的thread_stack.

名为'thread_stack'的服务器变量在C中实现为sql/mysqld.cc中的'my_thread_stack_size':

  {"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},
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

因此,默认情况下,堆栈大小应为192K(32位)或256K(64位架构).

首先,检查mysqld二进制文件是如何编译的,看看默认值是什么:

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
Run Code Online (Sandbox Code Playgroud)

在我的系统上,我在64位平台上获得256K.

如果有不同的值,也许有人用不同的编译选项构建服务器,例如-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);
Run Code Online (Sandbox Code Playgroud)

服务器代码调用:

  • pthread_attr_setstacksize()来设置堆栈大小
  • pthread_attr_getstacksize()用于验证线程实际拥有多少堆栈,如果pthread库使用较少,则会在日志中抱怨.

长话短说,可以看到错误,因为与服务器附带的默认值相比,thread_stack太小了.这可能发生:

  • 在使用不同的编译选项进行服务器的自定义构建时
  • 更改my.cnf文件中的默认值时
  • 如果pthread库本身出了问题(理论上从阅读代码,我自己从未见过它).

我希望这回答这个问题.

此致, - Marc Alff

更新(2014-03-11),使"如何修复"更加明显.

很有可能的是,在my.cnf文件中更改了thread_stack文件的默认值.

如何修复它是微不足道的,找到my.cnf文件中设置thread_stack的位置,并删除设置(信任服务器代码以提供合适的默认值,因此下次不会再次发生)或增加堆栈尺寸.

  • 这极大地解释了"这里发生了什么".然而,它混淆了"如何修复"部分. (9认同)

Jay*_*aph 34

虽然不是解决方案,但快速修复可能是通过在my.cnf中增加thread_stack大小来增加它:

thread_stack = 256K

正如用户"foo"指出的那样,发布整个触发器代码可能更有助于检测真正的问题.

  • 实际上,这个*是*解决方案 - 触发复杂性和线程堆栈大小并不是特别相关,当你的线程堆栈太小时,你会把它变大.在64位系统上低于256K的任何东西都太小了. (4认同)