sql >> Databáze >  >> RDS >> Mysql

Chyba MySQL 1436:Přetečení zásobníku vláken s jednoduchým dotazem

1436 – Přetečení zásobníku vláken:6136 bajtů využito ze zásobníku 131072 bajtů a potřeba 128000 bajtů.

Chyba 1436 odpovídá ER_STACK_OVERRUN_NEED_MORE v kódu 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

Kód vypisující viděnou chybu je v sql/sql_parse.cc, funkce 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));

Ze zobrazených hodnot je margin 128000 a my_thread_stack_size je 131072.

Jediné volání funkce check_stack_overrun(), které se pokouší rezervovat 128 000 bajtů, pochází z:

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

Hodnota STACK_MIN_SIZE je 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.

Zatím vše pro server funguje podle očekávání:

  • kód spustí spouštěč, který je implementován withsp_head::execute.
  • Běhový modul MySQL kontroluje, zda je v zásobníku alespoň 128 000 bajtů
  • tato kontrola se nezdaří (právem) a provádění spouštěče skončí chybou.

Množství zásobníku potřebné pro spuštění spouštěče MySQL nezávisí na samotné složitosti spouštění ani na obsahu/struktuře příslušných tabulek.

Co je skutečné Otázkou je, myslím, proč je thread_stack pouze 128 kB (131072).

Serverová proměnná s názvem 'thread_stack' je implementována v C jako 'my_thread_stack_size' v 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 je minimální hodnota pro tento parametr. Výchozí hodnota je DEFAULT_THREAD_STACK, která je definována v 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

Ve výchozím nastavení by tedy velikost zásobníku měla být 192 kB (32 bitů) nebo 256 kB (64 bitové architektury).

Nejprve zkontrolujte, jak byl zkompilován binární soubor mysqld, abyste viděli, jaká je výchozí hodnota:

[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

V mém systému mám 256 kB na 64bitové platformě.

Pokud existují různé hodnoty, možná někdo sestaví server s různými možnostmi kompilace, jako je -DDEFAULT_THREAD_STACK (nebo jen upravil zdroj) ... V takovém případě bych se zeptal, odkud pochází binární soubor.

Zadruhé zkontrolujte v my.cnf výchozí hodnoty poskytnuté v samotném konfiguračním souboru. Řádek, který by explicitně nastavil hodnotu na thread_stack (a s nízkou hodnotou), by definitivně způsobil viděnou chybu.

Nakonec zkontrolujte soubor protokolu serveru, zda neobsahuje chybu, jako je tato (viz sql/mysqld.cc):

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

Kód serveru volá:

  • pthread_attr_setstacksize() pro nastavení velikosti zásobníku
  • pthread_attr_getstacksize() k ověření, kolik zásobníku vlákno skutečně má, a stížnosti v protokolu, pokud knihovna pthread používá méně.

Stručně řečeno, chyba je vidět, protože thread_stack je příliš malý ve srovnání s výchozími hodnotami dodanými se serverem. Může se stát:

  • při vytváření vlastních sestavení serveru s různými možnostmi kompilace
  • při změně výchozí hodnoty v souboru my.cnf
  • Pokud se něco pokazilo v samotné knihovně pthread (teoreticky jsem to při čtení kódu nikdy neviděl).

Doufám, že to odpoví na otázku.

S pozdravem-- Marc Alff

Aktualizace (2014-03-11), aby bylo „jak opravit“ jasnější.

Co se s největší pravděpodobností děje, je to, že výchozí hodnota pro soubor thread_stack byla změněna v souboru my.cnf.

Jak to opravit, je pak triviální, najděte, kde je v souboru my.cnf nastaven thread_stack, a buď toto nastavení odstraňte (důvěřujte kódu serveru, že poskytuje slušnou výchozí hodnotu, aby se to příště neopakovalo), nebo zvyšte zásobník velikost.

Aktualizujte (2021-04-28), zkontrolujte, odkud pochází zásobník vláken:

Použijte tabulku performance_schema.variables_info zjistit, odkud daná proměnná pochází.

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)

Zde je výchozí tovární hodnota (zkompilovaná v binárce mysqld).

Další příklad:

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)

Zde je nastaven thread_stack v souboru my.cnf hlášený.

Refman:

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



  1. SQL Server 2005 Pivot na neznámý počet sloupců

  2. Získání časového rozdílu mezi dvěma časy v PHP

  3. Hloubkový průzkum zabezpečení na úrovni řádků

  4. Escapování řídicích znaků v Oracle XDB