【问题标题】:MySQL error 1436: Thread stack overrun, with simple queryMySQL 错误 1436:线程堆栈溢出,使用简单查询
【发布时间】:2012-02-07 22:40:40
【问题描述】:

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

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

为什么会出现这个错误?这不像涉及任何繁重的查询。另请注意,数据库几乎是空的,community_fields_values 中只有 2 行,user_field_log 中没有行

MySQL 版本:5.1.44

【问题讨论】:

  • 能否请您发布整个触发代码
  • @f00 我添加了整个触发器
  • @jan 我在主题中添加了版本

标签: mysql triggers


【解决方案1】:

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

打印所见错误的代码在 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));

从看到的值来看,margin 是 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);

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.

到目前为止,服务器一切正常:

  • 代码执行一个触发器,它是用 sp_head::execute。
  • MySQL 运行时检查堆栈上是否至少有 128000 个字节
  • 此检查失败(正确),触发器执行以错误结束。

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

真正的问题是什么,我猜,为什么 thread_stack 只有 128K (131072)。

名为“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},

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二进制文件是如何编译的,看看默认值是多少:

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

在我的系统上,我在 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);

服务器代码调用:

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

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

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

我希望这能回答这个问题。

问候, ——马克·阿尔夫

更新 (2014-03-11),使“如何修复”更加明显。

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

然后如何修复它是微不足道的,找到 my.cnf 文件中设置 thread_stack 的位置,然后删除该设置(信任服务器代码提供合适的默认值,因此下次不会再次发生这种情况)或增加堆栈大小。

更新(2021-04-28),检查thread_stack来自哪里:

使用表 performance_schema.variables_info 找出给定变量的来源。

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)

这里默认是出厂值(编译成mysqld二进制)。

另一个例子:

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)

这里thread_stack设置在报告的my.cnf文件中。

裁判员:

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

【讨论】:

  • 这很好地解释了“这里发生了什么”。但是,它混淆了“如何修复”部分。
  • 我还在 EL6 (CentOS6) 系统(64 位)上获得了 131072 字节的堆栈。很好的答案,尽管我的投票投给了另一个帖子,该帖子给出了一个简短而简洁的解决方案:-) 后编辑:发现有人实际上在配置中有硬编码的 thread_stack = 128k。可能它已经站在那里十多年了:-)
  • @tobixen,感谢 cmets。就个人而言,我更喜欢我的回答(当然是有偏见的),尤其是最后一部分:“删除设置......所以下次不会再发生这种情况”。它可能(必须再等十年才能找到)是一个更好的解决方案。
  • 我想知道 128k 设置是否来自 RHEL6 mysql 包,因为我在我们的 puppet 存储库中找不到它。如果是这样,这可能是一个应该报告给 RedHat 的“错误”,但我现在没有动力跟进它。
  • @mr.loop,表 performance_schema.variables_info 将显示系统变量值的来源,请参阅更新的答案。
【解决方案2】:

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

thread_stack = 256K

正如用户“foo”所指出的,发布整个触发代码可能更有助于检测真正的问题。

【讨论】:

  • 非常适合一次性程序,您只需要进行复杂的修改就可以忘记了。
  • 其实这是的解决方案——触发器复杂度和线程堆栈大小没有特别的关系,当你的线程堆栈太小时,你就做大。在 64 位系统上低于 256K 的任何内容都太小了。
  • 这对我有用,如果您在查找 my.cnf 文件时遇到问题,请使用“显示变量”来获取您的 mysql basdir,并为您指明正确的方向。文件也可能称为 my.ini。您需要停止并启动任务/服务,以便此更新生效。
猜你喜欢
  • 2012-10-06
  • 1970-01-01
  • 2021-05-26
  • 1970-01-01
  • 2012-05-09
  • 1970-01-01
  • 2020-03-26
  • 2013-06-06
相关资源
最近更新 更多