Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Erro 1436 do MySQL:saturação da pilha de threads, com consulta simples

1436 - Saturação da pilha de threads:6.136 bytes usados ​​de uma pilha de 131.072 bytes e 128.000 bytes necessários.


O erro 1436 corresponde a ER_STACK_OVERRUN_NEED_MORE no código 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

O código que imprime o erro visto está em 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));

Dos valores vistos, margin é 128000 e my_thread_stack_size é 131072.

A única chamada para check_stack_overrun() que tenta reservar 128.000 bytes é de:
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);

O valor de STACK_MIN_SIZE é 16.000:
[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.

Até agora, tudo funciona como esperado para o servidor:
  • o código executa um gatilho, que é implementado com sp_head::execute.
  • o tempo de execução do MySQL verifica se há pelo menos 128.000 bytes na pilha
  • esta verificação falha (com razão) e a execução do gatilho termina com um erro.

A quantidade de pilha necessária para a execução do gatilho do MySQL não depende da complexidade do gatilho em si, ou do conteúdo/estrutura das tabelas envolvidas.

O que real A questão é, eu acho, por que o thread_stack está apenas em 128K (131072).

A variável do servidor chamada 'thread_stack' é implementada em C como 'my_thread_stack_size' em 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 é o valor mínimo para este parâmetro. O valor padrão é DEFAULT_THREAD_STACK, que é definido em 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

Portanto, por padrão, o tamanho da pilha deve ser 192 K (32 bits) ou 256 K (arquiteturas de 64 bits).

Primeiro, verifique como o binário mysqld foi compilado, para ver qual é o valor padrão:
[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

No meu sistema, consegui 256K em uma plataforma de 64 bits.

Se houver valores diferentes, talvez alguém construa o servidor com diferentes opções de compilação, como -DDEFAULT_THREAD_STACK (ou apenas modifiquei a fonte) ... Eu questionaria de onde vem o binário nesse caso.

Em segundo lugar, verifique my.cnf para valores padrão fornecidos no próprio arquivo de configuração. Uma linha definindo um valor para thread_stack explicitamente (e com um valor baixo) definitivamente causaria o erro visto.

Por último, verifique o arquivo de log do servidor para um erro como este (consulte sql/mysqld.cc):
sql_print_warning("Asked for %lu thread stack, but got %ld",
                  my_thread_stack_size, (long) stack_size);

O código do servidor chama:
  • pthread_attr_setstacksize() para definir o tamanho da pilha
  • pthread_attr_getstacksize() para verificar quanta pilha um thread realmente tem e reclama no log se a biblioteca pthread usou menos.

Para encurtar a história, o erro é visto porque o thread_stack é muito pequeno em comparação com os valores padrão enviados com o servidor. Isso pode acontecer:
  • ao fazer compilações personalizadas do servidor, com diferentes opções de compilação
  • ao alterar o valor padrão no arquivo my.cnf
  • se algo deu errado na própria biblioteca pthread (em teoria, ao ler o código, eu mesmo nunca vi).

Espero que isso responda a pergunta.

Atenciosamente, -- Marc Alff

Atualização (2014-03-11), para tornar o "como consertar" mais óbvio.

O que está acontecendo, muito provavelmente, é que o valor padrão para o arquivo thread_stack foi alterado no arquivo my.cnf.

Como corrigi-lo é trivial, então, encontre onde thread_stack está definido no arquivo my.cnf e remova a configuração (confiando no código do servidor para fornecer um valor padrão decente, para que isso não aconteça novamente na próxima vez) ou aumente a pilha Tamanho.

Update (2021-04-28), verifique de onde vem o thread_stack:

Use a tabela performance_schema.variables_info para descobrir de onde uma determinada variável vem.
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)

Aqui o padrão é o valor de fábrica (compilado no binário mysqld).

Outro exemplo:
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)

Aqui o thread_stack é definido no arquivo my.cnf relatado.

Refman:

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