No guardes fotos en MySQL (y por qué S3 existe, con MinIO como plan B)
Hay un momento en la vida de todo proyecto donde alguien dice: "y los ficheros dónde los guardamos?". Y la respuesta fácil es: en la base de datos, en un BLOB. Funciona. Es transaccional. Es consistente.
Y es una terrible idea. Vamos a ver por qué.
El problema de guardar binarios en MySQL
MySQL guarda BLOBs en páginas de 16KB (InnoDB). Cuando metes un PDF de 5MB, MySQL lo parte en trozos y los esconde en páginas internas que nadie ve. Esto tiene consecuencias:
El buffer pool se satura. InnoDB cachea páginas de datos en RAM. Si la mitad de tu buffer pool está lleno de pedazos de PDFs, las páginas que realmente importan (índices, rows de usuarios, queries frecuentes) se evaporan de la cache. Tu base de datos se vuelve lenta por culpa de un contrato que alguien subió hace 3 semanas.
Backups más lentos.
mysqldumpde 2GB de datos limpios tarda segundos.mysqldumpde 2GB de datos + 50GB de BLOBs tarda horas. Y la mayoría de esos BLOBs no cambian nunca. Estás respaldando lo mismo una y otra vez.Replicación pesada. Los binlogs de MySQL replican todos los cambios. Si alguien sube un archivo de 20MB, ese archivo viaja por el binlog a cada replica. Ancho de banda desperdiciado en algo que no necesita consistencia transaccional.
Queries que antes eran rápidas ahora no lo son. Un
SELECT *que antes devolvía 100 filas ligeras ahora devuelve 100 filas con BLOBs de 5MB cada una. Si olvidas excluir la columna del BLOB, la query se arrastra. Y olvidarás. Todos olvidan.
Qué es S3 (y por qué es diferente)
Amazon S3 es un object store. No es una base de datos. No
tiene esquemas. No tiene JOINs. Es un diccionario gigante: le das una
clave (uploads/contrato-2026.pdf) y te
devuelve los bytes. Punto.
Las ventajas:
Separación de responsabilidades. Tu MySQL guarda datos relacionales. Tu S3 guarda ficheros. Cada uno hace lo que sabe hacer. MySQL no se satura con binarios. S3 no se preocupa por transacciones.
Escalabilidad sin pensar. S3 no tiene límites prácticos de almacenamiento. Subes lo que quieras. No planificas capacidad. No añades discos. Simplemente funciona.
Backups baratos. Cada objeto en S3 tiene versionado automático. Puedes configurar lifecycle rules que muevan objetos antiguos a almacenamiento más barato (Glacier). Un backup de BLOB en MySQL no tiene nada de esto.
Servir ficheros directamente. S3 puede generar URLs firmadas con expiración. Tu backend no actúa de proxy: el cliente descarga directamente de S3. Menos carga en tu servidor, menos ancho de banda consumido.
CDN integrado. CloudFront (la CDN de AWS) se integra con S3 nativamente. Ficheros estáticos servidos desde el edge en milisegundos. Con BLOBs en MySQL, cada descarga pasa por tu aplicación.
Las desventajas:
No es transaccional. Si subes un fichero a S3 y luego falla la inserción en MySQL, tienes un fichero huérfano. Necesitas lógica de compensación o limpieza periódica.
Costos inesperados. AWS cobra por GB almacenado, por petición GET, por petición PUT, por transferencia de datos entre regiones. Un proyecto pequeño puede ser gratis. Un proyecto con mucho tráfico puede ser caro.
Latencia de primera petición. S3 no es un disco. La primera petición a un objeto tiene latencia de red (típicamente 50-200ms). Si estás accediendo al mismo fichero constantemente, necesitas una cache o CDN delante.
Vendor lock-in. Tu código depende de la API de AWS. Si mañana quieres moverte, necesitas adaptar tu capa de almacenamiento.
MinIO: S3 sin depender de AWS
Aquí es donde entra MinIO.
MinIO es un servidor de object storage compatible con la API S3. Esto significa que tu código que habla S3 puede hablar MinIO sin cambiar una línea. Solo cambias el endpoint:
# AWS S3
ENDPOINT=https://s3.eu-west-1.amazonaws.com
# MinIO (self-hosted)
ENDPOINT=http://minio.tu-servidor.local:9000Las ventajas de MinIO:
Self-hosted. Lo instalas en tu servidor, en tu VPS, en tu Raspberry Pi. Tus datos no salen de tu infraestructura. Para proyectos con requisitos de soberanía de datos o presupuestos ajustados, es ideal.
100% compatible S3. La API es idéntica. Si usas el SDK de AWS (
aws-sdk-php,boto3,@aws-sdk/client-s3), solo cambias el endpoint y funciona. Sin adaptadores, sin wrappers, sin dolores de cabeza.Distribuido o standalone. Puedes correr una sola instancia para un proyecto pequeño, o un cluster distribuido con erasure coding para tolerancia a fallos. Crece contigo.
UI web incluida. MinIO trae una consola web donde puedes explorar buckets, subir ficheros, gestionar policies. No necesitas herramientas extra.
Gratis. MinIO es open source (AGPLv3). La versión enterprise tiene features adicionales, pero la versión community cubre el 99% de los casos de uso.
Docker-ready. Un solo comando y tienes S3 en local:
docker run -p 9000:9000 -p 9001:9001 \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio server /data --console-address ":9001"Desventajas de MinIO:
Tú eres el sysadmin. Si se rompe el disco, tú lo arreglas. Si se queda sin espacio, tú lo amplías. No hay un equipo de AWS monitorizando tu instancia a las 3AM.
Sin CDN integrada. Para servir ficheros rápido a nivel global, necesitas poner una CDN delante (Cloudflare, Caddy con cache, etc.). No viene integrado como con CloudFront.
Erasure coding consume más disco. Para tolerancia a fallos, MinIO usa erasure coding que necesita más almacenamiento que los datos originales. En un VPS de 50GB, esto importa.
Cuándo usar qué
| Situación | Recomendación |
|---|---|
| Proyecto personal, pocos ficheros | MinIO en un VPS |
| Startup escalando rápido | AWS S3 + CloudFront |
| Requisitos de soberanía de datos | MinIO self-hosted |
| Prototipo / desarrollo local | MinIO en Docker |
| Mucho tráfico global | S3 + CDN (o MinIO + CDN) |
| Datos sensibles / compliance | MinIO en infra propia |
Resumen
No guardes ficheros en MySQL. Es tentador, es fácil, y a los 6 meses te arrepientes. Usa un object store: S3 si quieres que otro lo gestione, MinIO si quieres controlarlo tú. La API es la misma. El esfuerzo de integración es el mismo. La diferencia es dónde duermen tus bytes y quién paga la factura del disco.
Tu MySQL te lo agradecerá. Tu buffer pool también.
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario