O Mysql usa lentamente a memória até começar a usar o swap

8

Estou executando um servidor de banco de dados de rackspace de 1 GB de RAM. Por alguma razão, em cerca de 2 dias, o uso da memória passa do uso de muito pouca troca, até o uso de 100mb. Se eu não reiniciar o sql, ele continuará usando mais swap. (Meu arquivo my.cnf é mostrado abaixo e o uso de memória é mostrado abaixo)

Alguns antecedentes: Eu tenho cerca de 50 bancos de dados ativos que possuem o mesmo esquema que usa INNODB para as tabelas. Eu tenho alguns bancos de dados com pouco tráfego que usam o MyISAM.

Nas tabelas INNODB, NÃO uso conexões persistentes. Eu também tenho uma função de relatório que cria uma tabela temporária. (Isso pode consumir muitos recursos, mas NÃO acontece com frequência)

Estou usando o CENTOS 6.3 e o mysql 5.5.28-log

Mesmo usando o swap, o desempenho ainda é muito bom. Só tenho medo de que, se não reiniciar a cada poucos dias, terei um problema.

Aqui está o meu log de free -m por aproximadamente 2 dias: (O primeiro registro é logo após uma reinicialização do mysql)

12/26 2:08 PM EST
             total       used       free     shared    buffers     cached
Mem:           992        697        295          0         74        362
-/+ buffers/cache:        260        732
Swap:          976         15        961

12/26 4:10 PM EST
[root@php-pos-db ~]# free -m
             total       used       free     shared    buffers     cached
Mem:           992        791        201          0         97        405
-/+ buffers/cache:        287        705
Swap:          976         14        961

12/27 2:52 PM EST
[root@php-pos-db ~]# free -m
             total       used       free     shared    buffers     cached
Mem:           992        947         45          0         55        169
-/+ buffers/cache:        722        270
Swap:          976         34        942

12/28 1:41 PM EST
             total       used       free     shared    buffers     cached
Mem:           992        963         29          0         45        119
-/+ buffers/cache:        797        195
Swap:          976         48        927

12/28 7:24 PM EST
[root@php-pos-db ~]# free -m
             total       used       free     shared    buffers     cached
Mem:           992        957         35          0         41        141
-/+ buffers/cache:        774        218
Swap:          976         90        886

12/28 8:33 PM EST
[root@php-pos-db ~]# free -m
             total       used       free     shared    buffers     cached
Mem:           992        948         44          0         48        130
-/+ buffers/cache:        768        224
Swap:          976         96        880

my.cnf

# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
# 
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html
#
# Take care to only add/remove/change a setting if you are comfortable
# doing so! For Rackspace customers, if you have any questions or
# concerns, please contact the MySQL Database Services Team. Be aware
# that some work performed by this team can involve additional billable
# fees.
#
# This file generated for host php-pos-db please modify
# variables if the server is resized from 1016636kB

[mysqld]

### General
user                = mysql
port                = 3306
datadir                         = /var/lib/mysql
tmpdir                          = /tmp
socket                          = /var/lib/mysql/mysql.sock
skip-external-locking           = 1
log_error                       = /var/log/mysqld.log

## This prevents using host-based authentication. That means users must be
## created using an ip-address (ie 'myuser'@'192.168.100.1') or must make
## use of the % wildcard (ie 'myuser'@'%'). The benefit to not using
## host-based authentication is that DNS will not impact MySQL performance.
#skip-name-resolve

## If open-files-limit is set very low, MySQL may increase on its own. Either
## way, increase this if MySQL gives 'too many open files' errors. Setting
## this above 65535 could be unwise (MySQL may crash).
open-files-limit                = 20000

### Cache
thread-cache-size               = 16
table-open-cache                = 4096
table-definition-cache          = 512

## Generally, it is unwise to set the query cache to be larger than 64-128M 
## as the costs associated with maintaining the cache outweigh the performance
## gains. A far superior solution would be to implement memcached, though this
## required modifying the application, among other things.
query-cache-type                = 1
query-cache-size                = 32M
query-cache-limit               = 1M

### Per-thread Buffers
sort-buffer-size                = 1M
read-buffer-size                = 1M
read-rnd-buffer-size            = 2M
join-buffer-size                = 1M

### Temp Tables
tmp-table-size                  = 64M 
max-heap-table-size             = 64M

### Networking
back-log                        = 100
max-connections                 = 50
max-connect-errors              = 10000
max-allowed-packet              = 16M
interactive-timeout             = 600
wait-timeout                    = 180
net_read_timeout        = 30
net_write_timeout       = 30
# This value is the size of the listen queue for incoming TCP/IP connections.
back_log            = 128

#### Storage Engines
## Set this to force MySQL to use a particular engine / table-type
## for new tables. This setting can still be overridden by specifying
## the engine explicitly in the CREATE TABLE statement.
default-storage-engine         = InnoDB

## Makes sure MySQL does not start if InnoDB fails to start. This helps
## prevent ugly silent failures.
innodb                          = FORCE

### MyISAM
## Not sure what to set this to?
## Try running a 'du -sch /var/lib/mysql/*/*.MYI'
## This will give you a good estimate on the size of all the MyISAM indexes.
## (The buffer may not need to set that high, however)
key-buffer-size                 = 2M
## This setting controls the size of the buffer that is allocated when 
## sorting MyISAM indexes during a REPAIR TABLE or when creating indexes 
## with CREATE INDEX or ALTER TABLE.
myisam-sort-buffer-size         = 2M

### InnoDB
## Note: While most settings in MySQL can be set at run-time, many InnoDB
## variables cannot be set at runtime as require restarting MySQL
###
## These settings control how much RAM InnoDB will use. Generally, when using
## mostly InnoDB tables, the innodb-buffer-pool-size should be as large as
## is possible without swapping or starving other processes of RAM. The other 
## two settings usually do not need to be changed, but can help for very large 
## datasets.
innodb-buffer-pool-size         = 285M
innodb-log-buffer-size          = 8M

## Be careful when changing these as they require re-generating the 
## ib-logfile* files, which must be done carefully. Do not change this unless 
## you are familiar with the procedure.
innodb-log-file-size           = 128M
innodb-log-files-in-group      = 2

## This will cause each table to create its own .ibd file
innodb-file-per-table           = 1

## Setting this to 2 will decrease disk I/O but can cause up to a second of
## queries to be lost during a hard outage (i.e. power failures)
# innodb-flush-log-at-trx-commit = 2

### Replication
## Set this to the Server's instance ID in replication environments
server-id                       = 1

#log-bin                        = /var/lib/mysql/bin-log
#relay-log                      = /var/lib/mysql/relay-log
#relay-log-space-limit          = 4G
#expire-logs-days               = 5

## This should be enabled on conventional MySQL slaves
#read-only                      = 1

## This will cause replicated statements on a slave to be written to the slave's binlog
## Enable this on the middle slave of M->S->S configs
#log-slave-updates              = 1

#binlog-format                  = STATEMENT

### Logging
## This option determines the destination for general query log and slow query log output.
## The option value can be given as one or more of the words TABLE, FILE, or NONE.
## NOTE: Table logging takes away 50% of performance and thus is not recommended
##       http://bugs.mysql.com/bug.php?id=30414
## In addition, you cannot backup the contents of these tables properly
## (mysqldump skips these tables by default since they cannot be locked)
#log-output                     = FILE
slow-query-log                 = 1
slow-query-log-file            = /var/lib/mysql/slow-log
long-query-time                = 2
log-queries-not-using-indexes  = 1

[mysqld-safe]
log-error                       = /var/log/mysqld.log

[mysqldump]
max-allowed-packet      = 16M

# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/sysconfig/mysqld-config/
Chris Muench
fonte

Respostas:

5

O MySQL tem o hábito desagradável de ser feliz em trocar. Jeremy Cole abordou isso melhor em seu blog: http://blog.jcole.us/2012/04/16/a-brief-update-on-numa-and-mysql/ . Nesse blog, você aprende que há algo que pode ser feito: Adicione numactl --interleave=alldentro de /etc/init.d/mysql.

SUGESTÕES

Se o servidor se dedicar apenas ao MySQL, altere o seguinte em /etc/my.cnf:

[mysqld]
innodb_open_files=1000
innodb_flush_method=O_DIRECT
innodb_buffer_pool_size=768M
innodb_log_file_size=192M

Se o servidor tiver pelo menos núcleo duplo, adicione estes

innodb_buffer_pool_instances=2
innodb_read_io_threads=16
innodb_write_io_threads=16
innodb_io_capacity=2000

Em seguida, faça o login no mysql run SET GLOBAL innodb_fast_shutdown = 0;

Em seguida, execute o seguinte no sistema operacional

cd /var/lib/mysql
service mysql stop
mv ib_logfile0 ib_logfile0.bak
mv ib_logfile1 ib_logfile1.bak
service mysql start

De uma chance !!!

UPDATE 2012-12-31 08:30 EDT

Do seu último comentário

Parou de subir em torno de 1 GB. Removai bancos de dados não utilizados e parece que o mysql 5.5 armazena muitos dados na memória, pois isso não aconteceu no 5.0. O mysql mudou muito?

Sim, o MySQL mudou muito. De fato, existem muitos casos em que a atualização do MySQL 5.0 para o MySQL 5.5 e resultou na degradação do desempenho. O InnoDB 5.5 agora está equipado para realizar o acoplamento de hyperthreading e multicore.

Percona realmente testou isso há algum tempo .

Leia-me as postagens anteriores sobre esse assunto

Também escrevi sobre isso no ServerFault e StackOverflow

RolandoMySQLDBA
fonte
Vou tentar: Qual é a diferença entre innodb_buffer_pool_size e innodb-buffer-pool-size #
Chris Muench
innodb_buffer_pool_sizede 768M pode estar aumentando o limite em uma máquina com apenas 1 GB de RAM. Restam apenas 256 milhões para o que está acontecendo no kernel e no espaço do usuário fora do MySQL, além de tudo o que está acontecendo no MySQL fora do buffer pool do InnoDB ... Você precisa definir isso como algo, mas honestamente, eu estaria procurando obter mais memória também.
James L
FWIW, NUMA não deve ser um fator aqui: uma máquina de 1 GB na Rackspace executando o CentOS 6.3 será uma VM com apenas um nó NUMA apresentado.
James L
@ James, uma vez que é uma VM, você está certo. O ajuste multicore é desnecessário e 75% da RAM de 1 GB é muito baixa. Ele precisa de pelo menos 4 GB.
RolandoMySQLDBA
Atualizei para um servidor de rackspace de 2GB e o uso de memória ainda está aumentando. Estamos perplexos com o que está acontecendo. Isso não aconteceu no mysql 5.0.96
Chris Muench
0

Além dos conselhos muito bons dados por Rolando, você pode, no lado do sistema, ativar uma configuração sem troca usando sysctl . Eu costumo configurar vm.swappiness=10na máquina MySQL no /etc/sysctl.conf . Ele fornece acesso restrito à troca, mas permite, se necessário.

O valor padrão de vm.swappiness é 60, o que é muito permissivo.

dominix
fonte
0

Nota : Publiquei esta resposta em uma pergunta relacionada no stackoverflow. Essa solução é específica para Linux e Systemd, mas, na verdade, pode ser adaptada a qualquer sistema que suporte adequadamente as memlockchamadas e fornece a capacidade de fazê-lo para processos que não permanecem raiz.

Atualização : Esta solução pode não funcionar tão bem. Veja a nota no final.

Existe uma classe de aplicativos em que você nunca deseja que eles troquem. Uma dessas classes é um banco de dados. Os bancos de dados usarão a memória como caches e buffers para suas áreas de disco, e não faz absolutamente nenhum sentido que eles sejam trocados. A memória específica pode conter alguns dados relevantes que não são necessários por uma semana até um dia em que um cliente solicita. Sem o armazenamento em cache / troca, o banco de dados simplesmente encontraria o registro relevante no disco, o que seria bastante rápido; mas com a troca, seu serviço pode demorar um pouco para responder.

mysqldinclui código para usar a chamada do sistema operacional / memlock. No Linux, desde pelo menos 2.6.9, essa chamada de sistema funcionará para processos não raiz que possuam a CAP_IPC_LOCKcapacidade [1] . Ao usar memlock(), o processo ainda deve funcionar dentro dos limites do LimitMEMLOCKlimite. [2] . Uma das (poucas) coisas boas systemdé que você pode conceder mysqldesses recursos ao processo, sem a necessidade de um programa especial. Se também pode definir os rlimits como seria de esperar ulimit. Aqui está um overridearquivo para o mysqldqual executa as etapas necessárias, incluindo alguns outros que você pode precisar para um processo como um banco de dados:

[Service]
# Prevent mysql from swapping
CapabilityBoundingSet=CAP_IPC_LOCK

# Let mysqld lock all memory to core (don't swap)
LimitMEMLOCK=-1 

# do not kills this process if low on memory
OOMScoreAdjust=-900 

# Use higher io scheduling
IOSchedulingClass=realtime    

Type=simple    
ExecStart=
ExecStart=/usr/sbin/mysqld --memlock $MYSQLD_OPTS

Nota A comunidade padrão do mysql atualmente acompanha Type=forking e adiciona --daemonizea opção ao serviço na ExecStartlinha. Isso é inerentemente menos estável que o método acima.

Sobre Substituir arquivos no systemd : Você cria um diretório /etc/systemd/system/nomeado mysqld.service.de coloca o novo arquivo (com o conteúdo acima) nele.

ATUALIZAÇÃO Não estou 100% satisfeito com esta solução. Após vários dias de execução, notei que o processo ainda tinha enormes quantidades de troca! Examinando /proc/XXXX/smaps, observo o seguinte:

  • O maior contribuidor de swap é de um segmento de pilha! No começo, isso não parecia tão ruim, mas depois de vários dias, ficou em 437 MB e flutuava. Isso apresenta problemas óbvios de desempenho. Também indica vazamento de memória baseada em pilha.
  • Não existem páginas bloqueadas . Isso indica que a memlockopção no MySQL (ou Linux) está quebrada. Nesse caso, não importaria muito, porque o MySQL não pode bloquear a memória.
Otheus
fonte