Mysql
 sql >> база данни >  >> RDS >> Mysql

Грешка в MySQL 1436:Превишаване на стека на нишките, с проста заявка

1436 – Превишаване на стека от нишки:използвани 6136 байта от стек от 131072 байта и са необходими 128 000 байта.

Грешката 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, функция 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 проверява дали има поне 128 000 байта в стека
  • тази проверка е неуспешна (правилно) и изпълнението на тригера завършва с грешка.

Количеството на стека, необходимо за изпълнението на тригера на 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

В моята система получих 256K на 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 (на теория от четене на кода, аз самият никога не съм го виждал).

Надявам се това да отговори на въпроса.

Поздрави,-- Марк Алф

Актуализация (11.03.2014 г.), за да направите „как да поправите“ по-очевидно.

Това, което се случва, по всяка вероятност е, че стойността по подразбиране за файла thread_stack е променена във файла my.cnf.

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

Актуализация (2021-04-28), проверете откъде идва нишката_стека:

Използвайте таблица 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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PHP - Импортиране на CSV файл в mysql база данни с помощта на LOAD DATA INFILE

  2. Работа с тригери в база данни на MySQL - урок

  3. Pymysql Cursor.fetchall() / Fetchone() връща нищо

  4. Функция MySQL ACOS() – Връща косинус на дъгата на число

  5. Изчислете децил от актуалност в MySQL