O MySQL cria tabelas temporárias no disco. Como eu paro isso?

27

Estamos executando um site (Moodle) que os usuários acham lento no momento. Acho que localizei o problema no MySQL criando tabelas temporárias no disco. Observo a variável created_tmp_disk_tablesna administração do servidor Mysql Workbench e o número aumenta em aproximadamente 50 tabelas / s. Após um dia de uso, created_tmp_disk_tablesé> 100k. Além disso, a memória não parece ser liberada. O uso continua aumentando até que o sistema se torne praticamente inutilizável e precisamos reiniciar o MySQL. Preciso reiniciá-lo quase todos os dias e começa com o uso de 30 a 35% da memória disponível e o final do dia com 80%.

Não tenho blobs no banco de dados e não tenho controle sobre as consultas, portanto não posso tentar otimizá-las. Também usei o Assistente de confirmação da Percona para gerar um arquivo de configuração, mas o my.ini também não resolveu o meu problema.

Questões

  1. O que devo mudar para impedir que o MySQL crie tabelas temporárias em disco? Existem configurações que preciso alterar? Devo jogar mais memória nele?

  2. Como posso parar o MySQL de consumir minha memória?

Editar

Ativei o slow_querieslog e descobri que a consulta SELECT GET_LOCK()estava registrada como lenta. Uma pesquisa rápida revelou que eu havia permitido conexões persistentes na configuração do PHP ( mysqli.allow_persistent = ON). Eu desliguei isso. Isso reduziu a taxa na qual o MySQL consome memória, embora ainda esteja criando tabelas temporárias.

Eu também verifiquei se o key_buffer sizetamanho é grande o suficiente. Eu olhei para a variável key_writes. Isso deve ser zero. Caso contrário, aumente o key_buffer_size. Eu tenho zero key_readse zero, key_writesentão suponho que o número key_buffer_sizeseja grande o suficiente.

Aumentei o tmp_table_sizee max-heap-table-sizepara 1024M, pois um aumento no created_tmp_disk_tables pode indicar que as tabelas não podem caber na memória. Isso não resolveu.

Ref: http://www.mysqlperformanceblog.com/2007/08/16/how-much-overhead-is-caused-by-on-disk-temporary-tables/

Editar 2

Se você vir muitos sort_merge_passespor segundo na saída SHOW GLOBAL STATUS, considere aumentar o sort_buffer_sizevalor. Eu tinha 2 sort_merge_passesem uma hora, então eu considero o sort_buffer_sizesuficiente para ser grande.

Ref: Manual do Mysql sobre sort_buffer_size

Editar 3

Modifiquei a classificação e juntei os buffers, conforme sugerido por @RolandoMySQLDBA. O resultado é exibido na tabela abaixo, mas acho que created_tmp_tables_on_diskainda é alto. Reiniciei o servidor mysql depois de alterar o valor, verifiquei created_tmp_tables_on_diskapós um dia (8h) e calculei a média. Alguma outra sugestão? Parece-me que há algo que não cabe dentro de algum tipo de recipiente, mas não consigo descobrir o que é.

+---------------------+-------------+-------------+--------------------+
| Tmp_table_size,     | Sort_buffer | Join_buffer | No of created      |
| max_heap_table_size |             |             | tmp_tables on disk |
+---------------------+-------------+-------------+--------------------+
| 125M                | 256K        | 256K        |  100k/h            |
+---------------------+-------------+-------------+--------------------+
| 125M                | 512K        | 512K        |  100k/h            |
+---------------------+-------------+-------------+--------------------+
| 125M                | 1M          | 1M          |  100k/h            |
+---------------------+-------------+-------------+--------------------+
| 125M                | 4M          | 4M          |  100k/h            |
+---------------------+-------------+-------------+--------------------+   



Esta é a minha configuração:

+-----------------------+-----------------------+
|DATABASE SERVER        |WEB SERVER             |
+-----------------------+-----------------------+
|Windows Server 2008 R2 |Windows Server 2008 R2 |
+-----------------------+-----------------------+
|MySQL 5.1.48           |IIS 7.5                |
+-----------------------+-----------------------+
|4 Core CPU             |4 Core CPU             |
+-----------------------+-----------------------+
|4GB RAM                |8GB RAM                |
+-----------------------+-----------------------+

Informação adicional

+--------------------+---------+
|PARAM               |VALUE    |
+--------------------+---------+
|Num of tables in Db |361      |
+--------------------+---------+
|Size of database    |2.5G     |
+--------------------+---------+
|Database engine     |InnoDB   |
+--------------------+---------+
|Read/write ratio    |3.5      |
|(Innodb_data_read/  |         |
|innodb_data_written)|         |
+--------------------+---------+
|Avg table size      |15k rows |
+--------------------+---------+
|Max table size      |744k rows|
+--------------------+---------+

Essa configuração foi dada a mim, então eu tenho controle limitado sobre ela. O servidor da Web está usando muito pouca CPU e RAM, então excluí essa máquina como gargalo. A maioria das configurações do MySQL se origina de uma ferramenta de geração automática de configuração.

Monitorei o sistema usando o PerfMon por alguns dias representativos. A partir disso, concluo que não é o SO que está trocando para o disco.

My.ini

[client]
port=3306
[mysql]
default-character-set=utf8

[mysqld]
port=3306
basedir="C:/Program Files/MySQL/MySQL Server 5.1/"
datadir="D:/DBs/Data/"
default-character-set=utf8
default-storage-engine=INNODB
sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
max_connections=125
query_cache_size=350M
table_cache=1520
tmp_table_size=125M
table-definition-cache= 1024
max-heap-table-size= 32M
thread_cache_size=38

MyISAM Specific options
myisam_max_sort_file_size=100G
myisam_sort_buffer_size=125M
key_buffer_size=55M
read_buffer_size=1024K
read_rnd_buffer_size=256K
sort_buffer_size=1024K
join_buffer_size=1024K


INNODB Specific options
innodb_data_home_dir="D:/DBs/"
innodb_additional_mem_pool_size=32M
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=16M
innodb_buffer_pool_size=2G
innodb_log_file_size=407M
innodb_thread_concurrency=8
user30431
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Paul White diz GoFundMonica

Respostas:

16

Olhando para o my.ini, eu tenho duas sugestões

SUGESTÃO # 1

Eu aumentaria as seguintes configurações no seu my.ini

sort_buffer_size=4M
join_buffer_size=4M

Isso fará com que algumas junções e a classificação fiquem na memória. Obviamente, uma vez que um JOINou ORDER BYmais precise mais 4M, ele paginará em disco como uma tabela MyISAM.

Se você não pode logar como root@localhost, reinicie o mysql com

C:\> net stop mysql
C:\> net start mysql

Se você pode fazer login como root @ localhost, não precisa reiniciar o mysql para usar essas configurações.

Basta executar isso no cliente MySQL:

SET @FourMegs = 1024 * 1024 * 4;
SET GLOBAL sort_buffer_size = @FourMegs;
SET GLOBAL join_buffer_size = @FourMegs;

SUGESTÃO # 2

Como seus dados estão na unidade D:, você pode ter E / S de disco na unidade C:.

Por favor, execute esta consulta:

mysql> show variables like 'tmpdir';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tmpdir        | C:\Windows\TEMP |
+---------------+-----------------+
1 row in set (0.00 sec)

Como eu executo o mysql na minha área de trabalho com os padrões, minhas tabelas temporárias estão sendo gravadas no Drive C:. Se a Unidade D for um disco melhor que a Unidade C:, talvez você possa mapear tabelas temporárias para a Unidade D:configurando tmpdir da my.iniseguinte maneira:

tmpdir="D:/DBs/"

Você terá que reiniciar o mysql, pois tmpdir não é uma variável dinâmica.

De uma chance !!!

ATUALIZAÇÃO 29-11-2013 10:09 EST

SUGESTÃO # 3

Dado o fato de o MySQL estar em execução no Windows e você não poder tocar nas consultas do pacote principal, tenho duas idéias que devem ser feitas juntas.

IDÉIA # 1: Mover o banco de dados para uma máquina Linux

Você deve ser capaz de

  • Configurar uma máquina Linux
  • Instale o MySQL na máquina Linux
  • Habilitar o log binário para MySQL no Windows
  • mysqldump o banco de dados em um arquivo SQL de texto
  • Carregar arquivo SQL no MySQL em execução no Linux
  • Replicação da instalação do MySQL / Windows para MySQL / Linux

IDÉIA # 2: Reconfigure o Moodle para apontar para a máquina Linux

O Moodle foi projetado para o LAMP em primeiro lugar. Apenas mude os arquivos de configuração para apontar para a máquina Linux em vez de localhost.

Aqui está um link para um documento antigo do Moodle 2.3 sobre como configurar o MySQL: http://docs.moodle.org/23/en/Installing_Moodle#Create_an_empty_database

Tenho certeza de que os documentos mais recentes também estão disponíveis.

Qual é o ponto de mover o banco de dados para o Linux ???

Como isso ajuda a situação da tabela temporária ???

Sugiro configurar um disco RAM como a pasta de destino para suas tabelas temporárias

A criação da tabela temporária ainda ocorrerá, mas será gravada na RAM e não no disco. reduzindo E / S de disco.

ATUALIZAÇÃO 29/11/2013 11:24 EST

SUGESTÃO # 4

Eu sugeriria revisitar a SUGESTÃO # 2 com um disco RAID-0 rápido (32+ GB), configurando-o como Drive T: (T for Temp). Depois de instalar esse disco, adicione-o a my.ini:

[mysqld]
tmpdir="T:\"

Seria necessário reiniciar o MySQL, usando

net stop mysql
net start mysql

BTW, eu disse RAID-0 de propósito, para que você possa obter um bom desempenho de gravação em um RAID-1, RAID-10. Um disco de tabela tmp não é algo que eu tornaria redundante.

Sem otimizar as consultas como @RaymondNijland comentou, você não pode reduzir a contagem de criação da tabela temporária de forma alguma. SUGGESTION #3e SUGGESTION #4oferecer acelerar a criação da tabela temporária e a E / S da tabela temporária como a única alternativa.

RolandoMySQLDBA
fonte
13

Eu respondo minha própria pergunta aqui por completude

Selecionarei @RolandoMySQLDBA como a resposta preferida, pois ele me deu mais dicas, embora não tenha realmente resolvido o meu problema.

Abaixo estão os resultados da minha investigação

Conclusão

O MySQL no Windows apenas cria muitas tabelas temporárias e o ajuste do MySQL ao modificar o conteúdo dos arquivos de configuração não ajudou.

Detalhes

A tabela detalha os parâmetros que eu modifiquei no my.ini, respectivamente, antes de executar qualquer consulta. O MySQL foi reiniciado entre cada teste.

Usei o my.ini encontrado na pergunta original como modelo e, em seguida, alterei o valor dos parâmetros um por um, de acordo com a tabela abaixo.

Eu usei o JMeter para gerar 100 solicitações da Web simultâneas (como isso representava nosso uso) repetidas 10 dez vezes. Cada um Testconsistia em 1000 pedidos no total. Isso resultou em chamadas subseqüentes ao banco de dados. Isso mostrou que o MySQL criaria muitas tabelas temporárias, independentemente de quais parâmetros de configuração foram alterados.

+----+------------+-------+---------------+------------+
|Test|Parameter   |Value  |NumOfTempTables|Db Max Conn |
+----+------------+-------+---------------+------------+
| 1  |key_buffer_ | 25M   | 30682         | 29         |
|    |size        |       |               |            | 
+----+------------+-------+---------------+------------+
| 2  |key_buffer_ | 55M   | 30793         | 29         |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 3  |key_buffer_ | 100M  | 30666         | 28         |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 4  |key_buffer_ | 125M  | 30593         | 24         |
|    |size        |       |               |            | 
+----+------------+-------+---------------+------------+
| 5  |query_cache_| 100M  | 30627         | 32         |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 6  |query_cache_| 250M  | 30761         | 26         |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 7  |query_cache_| 500M  | 30864         | 83*        |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 8  |query_cache_| 1G    | 30706         | 75*        |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 9  |tmp_table_  | 125M  | 30724         | 31         |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 10 |tmp_table_  | 250M  | 30689         | 90*        |
|    |size        |       |               |            |
+----+------------+-------+---------------+------------+
| 11 |tmp_table_  | 500M  | 30792         | 28         |
|    |size        |       |               |            |  
+----+------------+-------+---------------+------------+
| 12 |Sort_buffer&| 256K  | 30754         | 28         |
|    |Join_buffer |       |               |            |
+----+------------+-------+---------------+------------+
| 13 |Sort_buffer&| 512K  | 30788         | 30         |
|    |Join_buffer |       |               |            | 
+----+------------+-------+---------------+------------+
| 14 |Sort_buffer&| 1M    | 30788         | 28         |
|    |Join_buffer |       |               |            | 
+----+------------+-------+---------------+------------+
| 15 |Sort_buffer&| 4M    | 30642         | 35         |
|    |Join_buffer |       |               |            |
+----+------------+-------+---------------+------------+
| 16 |innodb-     | 1G    | 30695         | 33         |
|    |buffer-     |       |               |            |
|    |pool-size   |       |               |            |
+----+------------+-------+---------------+------------+
| 17 |innodb-     | 2G    | 30791         | 28         |
|    |buffer-     |       |               |            |
|    |pool-size   |       |               |            | 
+----+------------+-------+---------------+------------+
| 18 |innodb-     | 3G    | 30719         | 34         |
|    |buffer-     |       |               |            |
|    |pool-size   |       |               |            |  
+----+------------+-------+---------------+------------+

* Média de três execuções

As imagens abaixo mostram a quantidade de memória e CPU necessárias para o servidor de banco de dados para as diferentes configurações. As linhas pretas indicam os valores mínimo e máximo e as barras azuis indicam os valores inicial e final. A memória máxima foi a 4096Mindicada na pergunta.

Uso de memória Utilização do CPU

user30431
fonte
Qual mecanismo de armazenamento você está usando? MyiSAM? Se você não estiver usando tabelas MyISAM, não é significativo confiar em key_buffer_size. se você estiver usando o mecanismo de armazenamento innodb, qual é o tamanho de innodb_buffer_pool_size. Você está usando query_cache?
314: