MySQL es el cuello de botella de la mayoría de aplicaciones web. Una base de datos mal configurada puede hacer que un VPS potente se arrastre.
La buena noticia: optimizar MySQL no es difícil. Unos pocos ajustes pueden mejorar el rendimiento 2-5x sin cambiar hardware.
Esta guía te enseña las optimizaciones más efectivas para MySQL y MariaDB en VPS.
Por qué MySQL suele ser el problema
La configuración por defecto es conservadora
MySQL viene configurado para funcionar en cualquier máquina, incluso con 512MB de RAM. En un VPS moderno, esa configuración desperdicia recursos.
Los datos crecen, la configuración no
Empezaste con una base de datos pequeña. Ahora tienes millones de registros y la misma configuración de hace 3 años.
Las queries no optimizadas se acumulan
Cada plugin, cada feature, cada consulta mal escrita suma. Al final, el servidor pasa más tiempo en MySQL que en cualquier otra cosa.
Diagnóstico inicial
Antes de optimizar, identifica el problema.
Ver estado actual
SHOW STATUS LIKE 'Threads%';
SHOW STATUS LIKE 'Connections';
SHOW STATUS LIKE 'Slow_queries';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
Variables importantes
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'query_cache%';
Herramienta de diagnóstico
MySQLTuner analiza tu configuración y sugiere mejoras:
wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl
perl mysqltuner.pl
Ejecuta después de que MySQL lleve funcionando al menos 24 horas.
El parámetro más importante: buffer pool
Qué es el buffer pool
El buffer pool es la memoria donde InnoDB guarda datos e índices en RAM. Cuanto más grande, menos accesos a disco.
Query → ¿Está en buffer pool?
→ Sí: respuesta instantánea (RAM)
→ No: leer de disco (lento), guardar en buffer pool
Tamaño recomendado
| RAM del VPS | Buffer pool | Otros usos |
|---|---|---|
| 2GB | 512MB-768MB | Sistema, PHP, etc. |
| 4GB | 1GB-1.5GB | Sistema, PHP, etc. |
| 8GB | 3GB-4GB | Sistema, PHP, etc. |
| 16GB | 8GB-10GB | Sistema, PHP, etc. |
Regla general: 50-70% de la RAM disponible para MySQL.
Configurar buffer pool
Edita /etc/mysql/mysql.conf.d/mysqld.cnf o /etc/mysql/mariadb.conf.d/50-server.cnf:
[mysqld]
innodb_buffer_pool_size = 2G
Reinicia MySQL:
sudo systemctl restart mysql
Verificar uso
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
Calcula el hit rate:
Hit Rate = (Innodb_buffer_pool_read_requests - Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests * 100
Un hit rate > 99% es excelente. Si es menor, aumenta el buffer pool.
Configuración completa optimizada
Para VPS de 4GB RAM
[mysqld]
# Buffer pool (el más importante)
innodb_buffer_pool_size = 1G
innodb_buffer_pool_instances = 1
# Log files
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
# Flush settings
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
# Conexiones
max_connections = 150
thread_cache_size = 50
# Tablas temporales
tmp_table_size = 64M
max_heap_table_size = 64M
# Query cache (desactivado en MySQL 8+)
query_cache_type = 0
query_cache_size = 0
# Timeouts
wait_timeout = 600
interactive_timeout = 600
# Slow query log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
Para VPS de 8GB RAM
[mysqld]
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 4
innodb_log_file_size = 512M
innodb_log_buffer_size = 32M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
max_connections = 200
thread_cache_size = 100
tmp_table_size = 128M
max_heap_table_size = 128M
query_cache_type = 0
query_cache_size = 0
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
Detectar y arreglar queries lentas
Activar slow query log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # Queries que tardan más de 2 segundos
log_queries_not_using_indexes = 1
Ver queries lentas
sudo tail -f /var/log/mysql/slow.log
Analizar con mysqldumpslow
sudo mysqldumpslow -s t /var/log/mysql/slow.log | head -20
Muestra las queries más lentas ordenadas por tiempo.
Analizar una query específica
EXPLAIN SELECT * FROM posts WHERE author_id = 5;
Busca:
- type: ALL = escaneo completo de tabla (malo)
- type: index = usa índice (bien)
- type: ref/const = búsqueda directa (excelente)
- rows = filas examinadas (menos es mejor)
Índices: la clave del rendimiento
Qué es un índice
Un índice es como el índice de un libro. En lugar de leer todas las páginas para encontrar algo, vas directo.
Sin índice:
SELECT * FROM users WHERE email = '[email protected]'
→ Escanea 1.000.000 de filas → 5 segundos
Con índice:
→ Busca en índice → Encuentra posición → Lee 1 fila → 0.001 segundos
Ver índices existentes
SHOW INDEX FROM nombre_tabla;
Crear índices
-- Índice simple
CREATE INDEX idx_email ON users(email);
-- Índice compuesto (para queries con múltiples WHERE)
CREATE INDEX idx_author_date ON posts(author_id, created_at);
-- Índice único
CREATE UNIQUE INDEX idx_email_unique ON users(email);
Qué indexar
Sí indexar:
- Columnas en WHERE frecuentes
- Columnas en JOIN
- Columnas en ORDER BY
- Foreign keys
No indexar:
- Columnas que cambian constantemente
- Columnas con pocos valores distintos (ej: género, estado)
- Tablas pequeñas (menos de 1000 filas)
Índices para WordPress
WordPress ya tiene índices básicos, pero puedes añadir:
-- Mejorar búsquedas en postmeta
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key(191), meta_value(100));
-- Mejorar queries de comentarios
ALTER TABLE wp_comments ADD INDEX idx_comment_approved_date (comment_approved, comment_date_gmt);
Optimizar tablas
ANALYZE TABLE
Actualiza estadísticas que MySQL usa para elegir índices:
ANALYZE TABLE nombre_tabla;
OPTIMIZE TABLE
Reorganiza datos físicamente y recupera espacio:
OPTIMIZE TABLE nombre_tabla;
Cuidado: Bloquea la tabla durante la operación. Hazlo en horario de bajo tráfico.
Automatizar mantenimiento
Script /root/scripts/mysql-maintenance.sh:
#!/bin/bash
mysql -u root -p'contraseña' -e "
SELECT CONCAT('ANALYZE TABLE ', table_name, ';')
FROM information_schema.tables
WHERE table_schema = 'tu_base_datos';" | mysql -u root -p'contraseña'
Programa semanal:
0 4 * * 0 /root/scripts/mysql-maintenance.sh
Query cache (deprecated)
MySQL 8.0+
Query cache fue eliminado en MySQL 8. No intentes activarlo.
Alternativa: Usa Redis para cachear resultados a nivel de aplicación.
MySQL 5.7 / MariaDB
Si usas versiones anteriores, el query cache puede ayudar para lecturas repetitivas:
query_cache_type = 1
query_cache_size = 128M
query_cache_limit = 2M
Pero generalmente Redis es mejor opción.
Conexiones y timeouts
max_connections
Por defecto es 151. Si tienes muchos usuarios concurrentes:
max_connections = 200
Pero más conexiones = más RAM. Cada conexión consume ~10MB.
Verificar conexiones usadas:
SHOW STATUS LIKE 'Max_used_connections';
Si está cerca del límite, aumenta. Si está muy por debajo, no desperdicies RAM.
thread_cache_size
Reutiliza threads en lugar de crear nuevos:
thread_cache_size = 50
wait_timeout
Cierra conexiones inactivas:
wait_timeout = 600
interactive_timeout = 600
600 segundos (10 minutos) es razonable. Evita conexiones zombies.
InnoDB vs MyISAM
Usa siempre InnoDB
| Característica | InnoDB | MyISAM |
|---|---|---|
| Transacciones | Sí | No |
| Foreign keys | Sí | No |
| Row-level locking | Sí | Table-level |
| Crash recovery | Sí | Limitado |
| Performance en escrituras | Mejor | Peor |
MyISAM es legacy. InnoDB es el estándar desde hace años.
Convertir tablas MyISAM a InnoDB
ALTER TABLE nombre_tabla ENGINE=InnoDB;
Verificar engine de tablas
SELECT table_name, engine
FROM information_schema.tables
WHERE table_schema = 'tu_base_datos';
Monitorización continua
Comandos útiles
-- Procesos activos
SHOW PROCESSLIST;
-- Matar proceso lento
KILL process_id;
-- Estado general
SHOW STATUS;
-- Variables
SHOW VARIABLES;
pt-query-digest (Percona)
Análisis avanzado de queries:
sudo apt install percona-toolkit
pt-query-digest /var/log/mysql/slow.log
Grafana + Prometheus
Para monitorización visual, usa el exporter de MySQL con Prometheus y dashboards de Grafana.
Errores comunes
Error 1: Buffer pool muy pequeño
El error más común. Si MySQL está lento, probablemente el buffer pool es muy pequeño.
Error 2: Demasiadas conexiones max_connections
Más conexiones = más RAM. Si tienes 500 max_connections pero nunca usas más de 50, estás desperdiciando recursos.
Error 3: No usar índices
Una query sin índice en tabla grande puede tardar minutos. Con índice, milisegundos.
Error 4: innodb_flush_log_at_trx_commit = 1
Es lo más seguro pero lo más lento. Para la mayoría de webs, = 2 es suficiente con riesgo mínimo.
Error 5: No revisar slow query log
Si no sabes qué queries son lentas, no puedes arreglarlas. Activa el log.
Checklist de optimización
Básico (obligatorio)
| Tarea | Comando verificación | ✓ |
|---|---|---|
| Buffer pool configurado | SHOW VARIABLES LIKE 'innodb_buffer_pool_size' | ☐ |
| Slow query log activo | SHOW VARIABLES LIKE 'slow_query_log' | ☐ |
| InnoDB en todas las tablas | Query information_schema | ☐ |
Intermedio
| Tarea | Comando verificación | ✓ |
|---|---|---|
| Índices en WHERE frecuentes | EXPLAIN en queries | ☐ |
| Buffer pool hit rate >99% | SHOW STATUS | ☐ |
| max_connections ajustado | SHOW STATUS | ☐ |
Avanzado
| Tarea | Comando verificación | ✓ |
|---|---|---|
| pt-query-digest analizado | Percona toolkit | ☐ |
| Monitorización activa | Grafana/similar | ☐ |
| Mantenimiento automatizado | Cron | ☐ |
MySQL vs MariaDB
MariaDB es un fork de MySQL, compatible y generalmente más rápido:
| Aspecto | MySQL 8 | MariaDB 10.11 |
|---|---|---|
| Rendimiento | Excelente | Excelente+ |
| Compatibilidad | Base | Compatible |
| Licencia | GPL (Oracle) | GPL (comunidad) |
| Innovación | Lenta | Más rápida |
Para VPS, ambos funcionan bien. MariaDB suele ser la opción por defecto en Linux.
Preguntas frecuentes
¿Cuánta RAM necesita MySQL?
Mínimo 512MB para funcionar. Recomendado: 50-70% de la RAM del VPS para buffer pool, más algo para conexiones y sistema.
¿Cada cuánto debo hacer OPTIMIZE TABLE?
Semanal o mensual para tablas con muchas escrituras/borrados. Tablas estáticas no lo necesitan.
¿El query cache sigue siendo útil?
En MySQL 8 no existe. En versiones anteriores, Redis es mejor alternativa.
¿Cómo sé si necesito más RAM para MySQL?
Si el buffer pool hit rate es menor de 99%, necesitas más RAM o reducir datos.
Nuestra recomendación
Cambios de mayor impacto:
- Buffer pool al 50-70% de RAM
- Slow query log activo
- Índices en columnas frecuentes
- innodb_flush_log_at_trx_commit = 2
Estos 4 cambios mejoran el rendimiento dramáticamente para la mayoría de sitios.
¿No quieres complicarte? La administración gestionada incluye optimización de MySQL como parte del servicio.
Conclusión
MySQL bien configurado puede manejar millones de queries al día en un VPS modesto. Mal configurado, se arrastra con unos pocos miles.
El buffer pool es el rey. Configúralo primero. Después activa slow query log, añade índices donde falten, y ajusta conexiones.
Con estas optimizaciones, tu base de datos volará.
¿Quieres un VPS con MySQL optimizado? Explora los VPS de Avantys con configuración profesional.
Guías relacionadas
- Optimizar el rendimiento de tu VPS
- Caché en VPS: Redis, Memcached y OPcache
- Monitorizar tu VPS con Netdata
- Escalar tu VPS: vertical vs horizontal
¿Quieres la guía completa con todos los casos de uso?
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.