Mysql usa lentamente la memoria hasta que comienza a usar el intercambio

8

Estoy ejecutando un servidor de base de datos de 1 gb de RAM en rack. Por alguna razón, en aproximadamente 2 días, el uso de la memoria pasa de usar muy poco intercambio a 100mb. Si no reinicio sql, seguirá usando más intercambio. (Mi archivo my.cnf se muestra a continuación y el uso de memoria se muestra a continuación)

Algunos antecedentes: tengo alrededor de 50 bases de datos activas que tienen el mismo esquema que usan INNODB para sus tablas. Tengo un par de bases de datos con poco tráfico que usan MyISAM.

En las tablas INNODB NO uso conexiones persistentes. También tengo una función de informes que crea una tabla temporal. (Esto puede requerir muchos recursos, pero NO sucede con frecuencia)

Estoy usando CENTOS 6.3 y mysql 5.5.28-log

Aunque estoy usando el intercambio, el rendimiento sigue siendo bastante bueno. Solo tengo miedo de que si no reinicio cada pocos días tendré un problema.

Aquí está mi registro de free -m durante aproximadamente 2 días: (El primer registro es justo después de un reinicio de 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
fuente

Respuestas:

5

MySQL tiene el desagradable hábito de ser swap-happy. Jeremy Cole ha abordado mejor esto en su blog: http://blog.jcole.us/2012/04/16/a-brief-update-on-numa-and-mysql/ . Desde ese blog, aprendes que hay algo que puedes hacer: agregar numactl --interleave=alldentro de /etc/init.d/mysql.

SUGERENCIAS

Si el servidor está dedicado a hacer solo MySQL, cambie lo siguiente en /etc/my.cnf:

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

Si el servidor es al menos de doble núcleo, agregue estos

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

A continuación, inicie sesión en mysql run SET GLOBAL innodb_fast_shutdown = 0;

A continuación, ejecute lo siguiente en el sistema operativo

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

Darle una oportunidad !!!

ACTUALIZACIÓN 2012-12-31 08:30 EDT

De tu último comentario

Dejó de subir alrededor de 1 gb. Eliminé las bases de datos no utilizadas y parece que mysql 5.5 almacena muchos datos en la memoria, ya que esto no sucedió en 5.0. ¿MySQL cambió mucho?

Sí, MySQL ha cambiado mucho. De hecho, hay muchos casos en los que la actualización de MySQL 5.0 a MySQL 5.5 produce una degradación del rendimiento. InnoDB 5.5 ahora está equipado para hacer hyperthreading y participación multinúcleo.

Percona realmente probó esto hace un tiempo .

Por favor, léeme publicaciones anteriores sobre este tema

También escribí sobre esto en ServerFault y StackOverflow

RolandoMySQLDBA
fuente
Voy a intentar esto: ¿Cuál es la diferencia entre innodb_buffer_pool_size y innodb-buffer-pool-size
Chris Muench
innodb_buffer_pool_sizede 768M podría estar empujando el límite en una máquina con solo 1 GB de RAM. Solo quedan 256M para lo que sea que esté sucediendo en el kernel y el espacio de usuario fuera de MySQL, además de todo lo que está sucediendo en MySQL fuera del grupo de búferes InnoDB ... Necesitas configurar eso en algo pero honestamente estaría buscando obtener más memoria también.
James L
FWIW, NUMA no debería ser un factor aquí: una máquina de 1 GB en Rackspace con CentOS 6.3 será una VM con solo un nodo NUMA presentado.
James L
@ James, ya que es una VM, tienes razón. El ajuste multinúcleo es innecesario y el 75% de la RAM en un 1 GB es demasiado bajo. Necesita al menos 4 GB.
RolandoMySQLDBA
Actualicé a un servidor de espacio en bastidor de 2 gb y el uso de memoria todavía está aumentando. Estamos perplejos con lo que está sucediendo. Esto NO sucedió en mysql 5.0.96
Chris Muench
0

Además de los muy buenos consejos dados por Rolando, puede, en el lado del sistema, activar una configuración sin intercambio utilizando sysctl . Normalmente configuro vm.swappiness=10en la máquina MySQL en /etc/sysctl.conf . Da acceso restringido al intercambio, pero lo permite si es necesario.

El valor predeterminado de vm.swappiness es 60, que es muy permisivo.

dominix
fuente
0

Nota : publiqué esta respuesta a una pregunta relacionada en stackoverflow. Esta solución es específica de Linux y Systemd, pero de hecho, se puede adaptar a cualquier sistema que admita memlockllamadas de manera adecuada y brinde la capacidad de hacerlo para procesos que no permanecen en la raíz.

Actualización : esta solución podría, de hecho, no funcionar tan bien. Ver nota al final.

Existe una clase de aplicaciones en las que nunca desea que se intercambien. Una de esas clases es una base de datos. Las bases de datos utilizarán la memoria como cachés y memorias intermedias para sus áreas de disco, y no tiene absolutamente ningún sentido que se intercambien. La memoria particular puede contener algunos datos relevantes que no son necesarios durante una semana hasta un día cuando un cliente lo solicite. Sin el almacenamiento en caché / intercambio, la base de datos simplemente encontraría el registro relevante en el disco, lo que sería bastante rápido; pero con el intercambio, su servicio puede tardar mucho tiempo en responder.

mysqldincluye código para usar la llamada del sistema operativo / sistema operativo memlock. En Linux, desde al menos 2.6.9, esta llamada al sistema funcionará para procesos no root que tienen la CAP_IPC_LOCKcapacidad [1] . Cuando se usa memlock(), el proceso aún debe funcionar dentro de los límites del LimitMEMLOCKlímite. [2] . Una de las (pocas) cosas buenas systemdes que puede otorgarle al mysqldproceso estas capacidades, sin requerir un programa especial. Si también puede establecer los límites como es de esperar con ulimit. Aquí hay un overridearchivo mysqldque realiza los pasos necesarios, incluidos algunos otros que podría necesitar para un proceso como una base de datos:

[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 La comunidad estándar mysql actualmente se envía con Type=forking y agrega --daemonizela opción al servicio en ExecStartlínea. Esto es inherentemente menos estable que el método anterior.

Acerca de la anulación de archivos en systemd : crea un directorio con /etc/systemd/system/nombre mysqld.service.dy coloca el nuevo archivo (con el contenido anterior) en él.

ACTUALIZACIÓN No estoy 100% satisfecho con esta solución. ¡Después de varios días de tiempo de ejecución, noté que el proceso todavía tenía enormes cantidades de intercambio! Al examinarlo /proc/XXXX/smaps, observo lo siguiente:

  • ¡El mayor contribuyente de intercambio es de un segmento de pila! Al principio, esto no parecía tan malo, pero después de varios días, se situó en 437 MB y fluctuante. Esto presenta problemas obvios de rendimiento. También indica pérdida de memoria basada en pila.
  • Hay cero páginas bloqueadas . Esto indica que la memlockopción en MySQL (o Linux) está rota. En este caso, no importaría mucho porque MySQL no puede apilar memlock.
Oteo
fuente