Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Błąd MySQL 1436:Przepełnienie stosu wątków za pomocą prostego zapytania

1436 — Przekroczenie stosu wątków:6136 bajtów używane ze stosu 131072 bajtów i potrzebne 128000 bajtów.

Błąd 1436 odpowiada ER_STACK_OVERRUN_NEED_MORE w kodzie 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

Kod wyświetlający zauważony błąd znajduje się w 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));

Z widocznych wartości margines wynosi 128000, a rozmiar_wątka_stosu to 131072.

Jedyne wywołanie check_stack_overrun(), które próbuje zarezerwować 128000 bajtów, pochodzi 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);

Wartość STACK_MIN_SIZE to 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.

Jak dotąd wszystko działa zgodnie z oczekiwaniami na serwerze:

  • kod wykonuje wyzwalacz, który jest zaimplementowany za pomocą sp_head::execute.
  • środowisko wykonawcze MySQL sprawdza, czy na stosie jest co najmniej 128000 bajtów
  • to sprawdzenie kończy się niepowodzeniem (słusznie), a wykonanie wyzwalacza kończy się błędem.

Ilość stosu potrzebnego do wykonania wyzwalacza MySQL nie zależy od złożoności samego wyzwalacza ani od zawartości/struktury zaangażowanych tabel.

Co prawdziwe Pytanie brzmi, jak sądzę, dlaczego thread_stack ma tylko 128K (131072).

Zmienna serwera o nazwie 'thread_stack' jest zaimplementowana w C jako 'my_thread_stack_size' w 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 to minimalna wartość tego parametru. Wartość domyślna to DEFAULT_THREAD_STACK, która jest zdefiniowana w 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

Tak więc domyślnie rozmiar stosu powinien wynosić 192K (32-bitowe) lub 256K (architektury 64-bitowe).

Najpierw sprawdź, jak skompilowano plik binarny mysqld, aby zobaczyć, jaka jest wartość domyślna:

[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

W moim systemie mam 256K na platformie 64-bitowej.

Jeśli są różne wartości, może ktoś zbuduje serwer z różnymi opcjami kompilacji, takimi jak -DDEFAULT_THREAD_STACK (lub po prostu zmodyfikuje źródło) ... w takim przypadku zadałbym sobie pytanie, skąd pochodzi plik binarny.

Po drugie, sprawdź my.cnf pod kątem domyślnych wartości podanych w samym pliku konfiguracyjnym. Wiersz określający jawnie wartość thread_stack (i niską wartością) ostatecznie spowodowałby zauważony błąd.

Na koniec sprawdź plik dziennika serwera pod kątem błędu takiego jak ten (zobacz sql/mysqld.cc):

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

Kod serwera wywołuje:

  • pthread_attr_setstacksize(), aby ustawić rozmiar stosu
  • pthread_attr_getstacksize(), aby sprawdzić, ile stosu naprawdę ma wątek i narzeka w dzienniku, jeśli biblioteka pthread zużyła mniej.

Krótko mówiąc, błąd jest widoczny, ponieważ thread_stack jest za mały w porównaniu z wartościami domyślnymi dostarczanymi z serwerem. Może się to zdarzyć:

  • podczas tworzenia niestandardowych kompilacji serwera z różnymi opcjami kompilacji
  • przy zmianie wartości domyślnej w pliku my.cnf
  • jeśli coś poszło nie tak w samej bibliotece pthread (teoretycznie od czytania kodu, sam tego nigdy nie widziałem).

Mam nadzieję, że to odpowie na pytanie.

Pozdrawiam,-- Marc Alff

Aktualizacja (11.03.2014), aby „jak naprawić” bardziej oczywiste.

To, co się dzieje, najprawdopodobniej jest takie, że domyślna wartość pliku thread_stack została zmieniona w pliku my.cnf.

Jak to naprawić, jest więc trywialne, znajdź, gdzie w pliku my.cnf jest ustawiony thread_stack i albo usuń to ustawienie (ufając, że kod serwera zapewni przyzwoitą wartość domyślną, więc nie powtórzy się to następnym razem) lub zwiększ stos rozmiar.

Aktualizacja (2021-04-28), sprawdź, skąd pochodzi thread_stack:

Użyj tabeli performance_schema.variables_info aby dowiedzieć się, skąd pochodzi dana zmienna.

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)

Tutaj wartością domyślną jest wartość fabryczna (skompilowana w binarnym mysqld).

Inny przykład:

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)

Tutaj stos_wątków jest ustawiony w zgłoszonym pliku my.cnf.

Referent:

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. Funkcja rankingowa w MySQL

  2. Wstaw MySQL do Wybierz

  3. JSON_STORAGE_SIZE() – Znajdź rozmiar przechowywania dokumentu JSON w MySQL

  4. Jak filtrować wyniki SQL w relacji ma wiele przejść?

  5. Jak ustawić zestaw znaków i sortowanie tabeli w MySQL?