Una base de datos mal configurada es el cuello de botella más común en aplicaciones web. MySQL correctamente optimizado puede hacer que tu sitio vuele.
Esta guía te enseña a configurar y optimizar MySQL/MariaDB en tu VPS.
MySQL vs MariaDB
| Aspecto | MySQL | MariaDB |
|---|---|---|
| Origen | Oracle | Fork de MySQL |
| Compatibilidad | - | 100% con MySQL |
| Rendimiento | Bueno | Ligeramente mejor |
| Licencia | GPL + Comercial | GPL |
| Recomendación | Empresas Oracle | Mayoría de casos |
Para la mayoría de VPS, MariaDB es la mejor opción. Los comandos son idénticos.
Instalación
Ubuntu/Debian
# MariaDB (recomendado)
sudo apt update
sudo apt install mariadb-server mariadb-client -y
# MySQL
sudo apt install mysql-server mysql-client -y
# Verificar
mysql --version
systemctl status mariadb # o mysql
Asegurar instalación
sudo mysql_secure_installation
# Respuestas recomendadas:
# Set root password? → Y (establecer contraseña fuerte)
# Remove anonymous users? → Y
# Disallow root login remotely? → Y
# Remove test database? → Y
# Reload privilege tables? → Y
Configuración básica
Archivo de configuración
# MariaDB
/etc/mysql/mariadb.conf.d/50-server.cnf
# MySQL
/etc/mysql/mysql.conf.d/mysqld.cnf
Configuración inicial recomendada
[mysqld]
# Red
bind-address = 127.0.0.1
port = 3306
# Charset
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# InnoDB (motor principal)
default-storage-engine = InnoDB
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
# Conexiones
max_connections = 150
wait_timeout = 600
interactive_timeout = 600
# Caché de queries (solo MySQL 5.7, deshabilitado en 8.0)
# query_cache_type = 1
# query_cache_size = 64M
# Logs
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
# Temporal
tmp_table_size = 64M
max_heap_table_size = 64M
# Reiniciar para aplicar
sudo systemctl restart mariadb
Gestión de usuarios
Conectar como root
# Método 1: Socket (más seguro)
sudo mysql
# Método 2: Con contraseña
mysql -u root -p
Crear usuario y base de datos
-- Crear base de datos
CREATE DATABASE mi_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Crear usuario
CREATE USER 'mi_usuario'@'localhost' IDENTIFIED BY 'contraseña_segura';
-- Dar permisos
GRANT ALL PRIVILEGES ON mi_app.* TO 'mi_usuario'@'localhost';
-- Aplicar cambios
FLUSH PRIVILEGES;
Permisos específicos
-- Solo lectura
GRANT SELECT ON mi_app.* TO 'usuario_lectura'@'localhost';
-- Lectura y escritura
GRANT SELECT, INSERT, UPDATE, DELETE ON mi_app.* TO 'usuario_app'@'localhost';
-- Ver permisos
SHOW GRANTS FOR 'mi_usuario'@'localhost';
-- Revocar permisos
REVOKE ALL PRIVILEGES ON mi_app.* FROM 'mi_usuario'@'localhost';
-- Eliminar usuario
DROP USER 'mi_usuario'@'localhost';
Acceso remoto (si es necesario)
-- Crear usuario para acceso remoto
CREATE USER 'mi_usuario'@'%' IDENTIFIED BY 'contraseña_segura';
GRANT ALL PRIVILEGES ON mi_app.* TO 'mi_usuario'@'%';
FLUSH PRIVILEGES;
# En configuración, cambiar bind-address
bind-address = 0.0.0.0
# Abrir firewall
sudo ufw allow from IP_PERMITIDA to any port 3306
Optimización de rendimiento
Calcular innodb_buffer_pool_size
La variable más importante. Debe ser ~70-80% de la RAM disponible para servidores dedicados a MySQL.
# Ver RAM disponible
free -h
# Fórmula para VPS compartido (MySQL + web)
# innodb_buffer_pool_size = RAM * 0.25 a 0.40
# 2GB RAM → 512M a 800M
# 4GB RAM → 1G a 1.5G
# 8GB RAM → 2G a 3G
Configuración por tamaño de VPS
VPS 2GB RAM:
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M
max_connections = 100
tmp_table_size = 32M
max_heap_table_size = 32M
VPS 4GB RAM:
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
max_connections = 150
tmp_table_size = 64M
max_heap_table_size = 64M
VPS 8GB RAM:
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
max_connections = 200
tmp_table_size = 128M
max_heap_table_size = 128M
Verificar uso actual
-- Ver variables actuales
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'max_connections';
-- Ver estado
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Innodb_buffer_pool%';
-- Conexiones usadas vs máximas
SHOW STATUS LIKE 'Max_used_connections';
Queries lentas
Habilitar log de queries lentas
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
Analizar queries lentas
# Ver queries lentas
sudo tail -100 /var/log/mysql/slow.log
# Usar mysqldumpslow
sudo mysqldumpslow -s t /var/log/mysql/slow.log | head -20
Optimizar queries
-- Analizar query específica
EXPLAIN SELECT * FROM usuarios WHERE email = '[email protected]';
-- Ver índices de tabla
SHOW INDEX FROM usuarios;
-- Crear índice
CREATE INDEX idx_email ON usuarios(email);
-- Ver tablas sin índices usados
SELECT * FROM sys.schema_tables_with_full_table_scans;
Índices
Cuándo crear índices
- Columnas en WHERE frecuentes
- Columnas en JOIN
- Columnas en ORDER BY
- Columnas en GROUP BY
Crear índices
-- Índice simple
CREATE INDEX idx_nombre ON tabla(columna);
-- Índice compuesto (orden importa)
CREATE INDEX idx_compuesto ON tabla(columna1, columna2);
-- Índice único
CREATE UNIQUE INDEX idx_email ON usuarios(email);
-- Ver índices
SHOW INDEX FROM tabla;
-- Eliminar índice
DROP INDEX idx_nombre ON tabla;
Analizar uso de índices
-- Estadísticas de índices
SELECT
table_name,
index_name,
stat_value
FROM mysql.innodb_index_stats
WHERE database_name = 'mi_app';
Backups
mysqldump (básico)
# Backup de una base de datos
mysqldump -u root -p mi_app > backup.sql
# Con compresión
mysqldump -u root -p mi_app | gzip > backup.sql.gz
# Todas las bases de datos
mysqldump -u root -p --all-databases > all_backup.sql
# Opciones recomendadas
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
--events \
mi_app | gzip > backup_$(date +%Y%m%d).sql.gz
Restaurar backup
# Restaurar
mysql -u root -p mi_app < backup.sql
# Desde comprimido
gunzip < backup.sql.gz | mysql -u root -p mi_app
# Crear base de datos primero si no existe
mysql -u root -p -e "CREATE DATABASE mi_app"
mysql -u root -p mi_app < backup.sql
Script de backup automatizado
#!/bin/bash
# /root/scripts/mysql-backup.sh
FECHA=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/mysql"
RETENTION_DAYS=7
mkdir -p $BACKUP_DIR
# Backup de todas las bases de datos
mysqldump --all-databases \
--single-transaction \
--routines \
--triggers \
| gzip > $BACKUP_DIR/all_databases_$FECHA.sql.gz
# Eliminar backups antiguos
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completado: $BACKUP_DIR/all_databases_$FECHA.sql.gz"
chmod +x /root/scripts/mysql-backup.sh
# Cron diario a las 3 AM
0 3 * * * /root/scripts/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1
Monitorización
Comandos útiles
-- Conexiones actuales
SHOW PROCESSLIST;
-- Conexiones completas
SHOW FULL PROCESSLIST;
-- Estado general
SHOW STATUS;
-- Variables de configuración
SHOW VARIABLES;
-- Estado de InnoDB
SHOW ENGINE INNODB STATUS\G
-- Tamaño de bases de datos
SELECT
table_schema AS 'Base de datos',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Tamaño (MB)'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;
Script de monitorización
#!/bin/bash
# /root/scripts/mysql-status.sh
echo "=== MySQL Status ==="
echo ""
echo "=== Conexiones ==="
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -e "SHOW STATUS LIKE 'Max_used_connections';"
echo ""
echo "=== Buffer Pool ==="
mysql -e "SHOW STATUS LIKE 'Innodb_buffer_pool_read%';"
echo ""
echo "=== Queries ==="
mysql -e "SHOW STATUS LIKE 'Queries';"
mysql -e "SHOW STATUS LIKE 'Slow_queries';"
Problemas comunes
”Too many connections"
-- Ver conexiones actuales
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';
-- Aumentar temporalmente
SET GLOBAL max_connections = 200;
-- Permanente en configuración
max_connections = 200
"Lock wait timeout exceeded”
-- Ver locks actuales
SHOW ENGINE INNODB STATUS\G
-- Ver transacciones
SELECT * FROM information_schema.innodb_trx;
-- Matar proceso bloqueado
KILL proceso_id;
MySQL consume mucha RAM
# Reducir buffer pool
innodb_buffer_pool_size = 256M
# Reducir conexiones
max_connections = 50
# Reducir buffers por conexión
sort_buffer_size = 256K
read_buffer_size = 256K
join_buffer_size = 256K
Queries muy lentas
-- Identificar queries lentas
SELECT * FROM sys.statements_with_full_table_scans
ORDER BY no_index_used_count DESC LIMIT 10;
-- Verificar índices
EXPLAIN SELECT ... ;
-- Optimizar tablas
OPTIMIZE TABLE nombre_tabla;
-- Analizar tablas
ANALYZE TABLE nombre_tabla;
Seguridad
Checklist de seguridad
# 1. Ejecutar mysql_secure_installation
sudo mysql_secure_installation
# 2. Verificar usuarios
mysql -e "SELECT user, host FROM mysql.user;"
# 3. Eliminar usuarios innecesarios
mysql -e "DROP USER ''@'localhost';"
# 4. Solo acceso local (bind-address = 127.0.0.1)
# 5. Firewall si acceso remoto necesario
sudo ufw allow from IP_ESPECIFICA to any port 3306
Contraseñas seguras
-- Cambiar contraseña
ALTER USER 'usuario'@'localhost' IDENTIFIED BY 'nueva_contraseña_segura';
-- Política de contraseñas (MySQL 8.0+)
SHOW VARIABLES LIKE 'validate_password%';
Preguntas frecuentes
¿Cuánta RAM debo asignar a innodb_buffer_pool_size?
Para VPS con MySQL + web server, usa 25-40% de la RAM. Para servidor dedicado a MySQL, usa 70-80%. En un VPS de 4GB compartido, 1GB es un buen punto de partida.
¿MySQL o MariaDB para mi VPS?
MariaDB para la mayoría de casos. Es compatible 100% con MySQL, ligeramente más rápido, y totalmente open source. Solo elige MySQL si tienes requisitos específicos de Oracle.
¿Cómo sé si necesito más conexiones?
Ejecuta 'SHOW STATUS LIKE Max_used_connections'. Si está cerca del max_connections configurado, aumenta. Pero primero optimiza queries y cierra conexiones que no se usan.
¿Cada cuánto debo hacer backup de MySQL?
Mínimo diario para producción. Para datos críticos, considera backups cada pocas horas o replicación en tiempo real.
¿Por qué mis queries son lentas aunque tengo índices?
Usa EXPLAIN para verificar que el índice se está usando. A veces MySQL elige no usar el índice si la selectividad es baja. También verifica que las estadísticas de tabla estén actualizadas con ANALYZE TABLE.
Nuestra recomendación
Configuración inicial:
- Ejecutar mysql_secure_installation
- Ajustar innodb_buffer_pool_size según RAM
- Habilitar slow query log
- Configurar backups automáticos
Para optimizar:
- Analiza queries lentas regularmente
- Crea índices en columnas frecuentes en WHERE
- Monitoriza conexiones y memoria
¿Necesitas bases de datos gestionadas? La administración gestionada de Avantys incluye optimización y backups de MySQL.
Conclusión
MySQL bien configurado es la diferencia entre un sitio lento y uno rápido. Dedica tiempo a ajustar innodb_buffer_pool_size, crear índices adecuados y monitorizar queries lentas.
Recuerda: backups automáticos son obligatorios, no opcionales.
¿Necesitas un VPS optimizado para bases de datos? Explora los VPS de Avantys con almacenamiento NVMe para máximo rendimiento.
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.