
La lentitud en el manejo de datos no se resuelve con más hardware, sino con una arquitectura y configuración más inteligentes de su infraestructura actual.
- Los cuellos de botella a menudo provienen de configuraciones por defecto, mala estructuración de datos y código ineficiente, no de la falta de potencia.
- Optimizar tipos de datos, particionar tablas y ajustar parámetros del servidor puede liberar hasta un 50% de recursos de memoria y CPU.
Recomendación: Inicie un diagnóstico proactivo para identificar si el problema es de disco, CPU o configuración, en lugar de asumir que necesita una migración costosa.
Para cualquier administrador de sistemas o analista de datos, la frustración es familiar: informes que tardan una eternidad en generarse, aplicaciones que se arrastran y una latencia que paraliza la operativa. La primera reacción suele ser culpar al hardware, soñando con servidores más potentes y costosas migraciones a la nube. Se habla de añadir más RAM, de cambiar a discos SSD más rápidos o de escalar verticalmente la instancia. Pero estas soluciones, aunque a veces necesarias, a menudo actúan como un parche costoso sobre un problema mucho más profundo.
¿Y si el verdadero culpable no fuera la falta de recursos, sino la forma en que los utilizamos? La verdadera optimización del rendimiento no reside en la fuerza bruta del hardware, sino en la inteligencia con la que configuramos, estructuramos y consultamos los datos. Un servidor modesto, afinado con precisión, puede superar a una bestia de hardware mal configurada. Este es el principio del cuello de botella sistémico: un fallo en una parte del sistema (como una mala normalización) puede degradar el rendimiento global, sin importar cuánta potencia de cálculo se le añada.
Este artículo adopta una perspectiva diferente. En lugar de mirar hacia afuera en busca de nuevo hardware, miraremos hacia adentro, a las causas raíz de la lentitud que se esconden en su infraestructura actual. Exploraremos cómo una inteligencia de configuración, un diseño de datos previsor y un código optimizado pueden transformar el rendimiento de sus sistemas. Analizaremos desde el clásico síndrome del «lunes por la mañana» hasta la deuda técnica de datos que se acumula silenciosamente durante años.
A lo largo de las siguientes secciones, desglosaremos estrategias concretas para diagnosticar y resolver estos problemas, demostrando que es posible recuperar velocidad, reducir costes en la nube y, en definitiva, trabajar de forma más eficiente con los recursos que ya posee.
Para navegar por estas soluciones avanzadas, hemos estructurado el contenido en áreas clave de optimización. A continuación, encontrará un resumen de los temas que abordaremos para transformar el rendimiento de sus sistemas de datos.
Sumario: Estrategias para la optimización de datos sin cambiar de hardware
- ¿Por qué sus consultas SQL tardan el doble en ejecutarse los lunes por la mañana?
- Cómo depurar bases de datos antiguas para recuperar un 30% de velocidad de lectura
- Procesamiento en el borde (Edge) o en la Nube: ¿cuál elegir para aplicaciones de tiempo real?
- La configuración por defecto que estrangula el rendimiento de su servidor de datos
- Cómo diagnosticar si el cuello de botella es el disco o el procesador en 5 minutos
- Cómo elegir la estructura de datos correcta para reducir el uso de memoria en un 50%
- ¿Por qué una mala normalización inicial hace que sus consultas sean lentas dos años después?
- Cómo optimizar código para reducir el consumo de CPU en la nube y bajar la factura
¿Por qué sus consultas SQL tardan el doble en ejecutarse los lunes por la mañana?
El escenario es un clásico en cualquier departamento de TI: llega el lunes, el equipo comercial empieza a generar sus informes semanales y el sistema de base de datos, que funcionaba con normalidad el viernes, de repente se arrastra. Las consultas tardan el doble de tiempo y la frustración se dispara. Este fenómeno no es casualidad; se debe principalmente al concepto de «caché frío». Durante el fin de semana, con una actividad reducida o a causa de reinicios programados y procesos de backup, el caché de la base de datos (el área de memoria RAM donde se guardan los datos y los planes de ejecución más frecuentes) se vacía o se «enfría».
Cuando llegan las primeras consultas masivas del lunes, el motor de la base de datos no encuentra los datos en la rápida memoria RAM. En su lugar, debe ir a buscarlos al lento disco duro, lo que provoca un cuello de botella masivo de entrada/salida (I/O). Es como si un bibliotecario, en lugar de tener los libros más pedidos a mano, tuviera que ir a buscarlos al almacén más lejano cada vez. Para sistemas críticos, un ratio de aciertos de caché del 99% es el mínimo para evitar estos problemas. El lunes por la mañana, ese ratio puede desplomarse por debajo del 50%.
Además, los trabajos pesados de ETL (Extracción, Transformación y Carga) o las tareas de mantenimiento que se ejecutan durante el fin de semana pueden dejar bloqueos (locks) residuales en tablas clave, o haber consumido tantos recursos que el sistema operativo sigue recuperándose. Identificar la causa exacta requiere un diagnóstico proactivo para no empezar cada semana apagando fuegos.
Plan de acción: diagnóstico del rendimiento del lunes
- Verificar procesos del fin de semana: Revise los logs de los procesos ETL y backups. ¿Alguno falló, se extendió más de lo previsto o dejó bloqueos en tablas críticas?
- Analizar el ratio de aciertos de caché: Utilice las herramientas de monitoreo de su base de datos (pg_stat_database en PostgreSQL, por ejemplo) para comparar el «cache hit ratio» del lunes a las 9 a.m. con el del viernes a la misma hora.
- Identificar consultas bloqueadas: Ejecute `pg_stat_activity` (PostgreSQL) o `SHOW FULL PROCESSLIST` (MySQL) para buscar consultas en estado «lock wait» o con tiempos de ejecución anormalmente altos.
- Revisar prioridades de procesos: Confirme que los procesos de negocio críticos tienen mayor prioridad a nivel de sistema operativo (usando `nice` o `cgroups` en Linux) que las tareas de fondo.
- Implementar calentamiento de caché: Programe scripts que ejecuten las consultas más comunes y críticas 30 minutos antes del inicio de la jornada laboral para «calentar» el caché y tener los datos listos en RAM.
Cómo depurar bases de datos antiguas para recuperar un 30% de velocidad de lectura
Con el paso de los años, las bases de datos acumulan una cantidad ingente de datos históricos. Aunque valiosos para análisis a largo plazo, estos registros antiguos se convierten en un lastre para las operaciones diarias. Cada consulta sobre una tabla de ventas, por ejemplo, debe escanear millones de filas de hace cinco años para encontrar las de la semana pasada. Este es un cuello de botella sistémico de diseño, donde el tamaño de los datos degrada linealmente el rendimiento de lectura.
La solución no es borrar los datos, sino estructurarlos de forma inteligente. Las dos estrategias principales son el archivado en frío y el particionamiento de tablas. El archivado consiste en mover los datos que ya no se usan con frecuencia (por ejemplo, registros con más de dos años de antigüedad) a una tabla separada o incluso a un sistema de almacenamiento más barato y lento. Esto reduce drásticamente el tamaño de las tablas «calientes» u operativas, acelerando las consultas diarias.
El particionamiento es una técnica más avanzada donde una tabla grande se divide lógicamente en segmentos más pequeños (particiones), generalmente por un criterio de fecha (mes, año) o región. Cuando se realiza una consulta con un filtro de fecha, el motor de la base de datos es lo suficientemente inteligente como para escanear únicamente la partición relevante, ignorando el 99% de los datos restantes. Esta técnica es extremadamente eficaz para mejorar el rendimiento.
Estudio de caso: Particionamiento de tablas en Google BigQuery
La propia documentación de Google BigQuery demuestra que el particionamiento de tablas masivas por fecha puede mejorar el rendimiento de las consultas en más de un 30%. Al estructurar los datos en particiones diarias o mensuales, el motor puede descartar automáticamente los segmentos de tiempo que no son relevantes para la cláusula `WHERE` de una consulta. Esto no solo acelera la ejecución, sino que también reduce los costes en sistemas de pago por uso, ya que se procesan menos datos.

Como se visualiza, separar los datos «fríos» de los «calientes» permite que las operaciones del día a día se centren en un conjunto de datos mucho más pequeño y manejable, resultando en una mejora inmediata de la velocidad de lectura sin necesidad de cambiar el hardware subyacente.
Procesamiento en el borde (Edge) o en la Nube: ¿cuál elegir para aplicaciones de tiempo real?
Cuando se diseñan aplicaciones que dependen de una respuesta instantánea, como sistemas de monitoreo industrial, análisis de vídeo en tiempo real o aplicaciones de IoT, la latencia es el enemigo número uno. En este contexto, surge una decisión arquitectónica crucial: ¿dónde procesar los datos? ¿En un servidor centralizado en la nube (Cloud Computing) o localmente, cerca de la fuente de datos (Edge Computing)? La elección tiene implicaciones profundas en el rendimiento, el coste y la soberanía de los datos.
El Cloud Computing ofrece una escalabilidad casi infinita y un modelo de pago por uso muy flexible. Es ideal para análisis masivos de datos (big data) que no son sensibles al tiempo, donde se pueden agregar datos de múltiples fuentes para entrenar modelos de machine learning o generar informes complejos. Sin embargo, su principal desventaja es la latencia de red. Enviar datos a un data center, procesarlos y recibir una respuesta puede tardar entre 20 y 100 milisegundos o más, un tiempo inaceptable para muchas aplicaciones de tiempo real.
El Edge Computing, por otro lado, realiza el procesamiento en o cerca del dispositivo que genera los datos. Esto reduce la latencia a menos de 10 milisegundos, ya que se elimina el viaje de ida y vuelta a la nube. Es la solución perfecta para decisiones críticas que deben tomarse en una fracción de segundo. No obstante, tiene un coste inicial de infraestructura más alto y una escalabilidad limitada al hardware local. A menudo, la mejor arquitectura es un modelo híbrido: pre-procesamiento crítico en el borde y envío de datos agregados o menos urgentes a la nube para su almacenamiento y análisis a largo plazo.
La siguiente tabla resume los criterios clave para tomar esta decisión fundamental, que impactará directamente en la capacidad de respuesta de su aplicación.
| Criterio | Edge Computing | Cloud Computing |
|---|---|---|
| Latencia | <10ms (local) | 20-100ms (red) |
| Escalabilidad | Limitada por hardware local | Virtualmente ilimitada |
| Costo inicial | Alto (infraestructura) | Bajo (pago por uso) |
| Procesamiento crítico | Ideal para tiempo real | Mejor para análisis batch |
| Soberanía de datos | Control total local | Depende del proveedor |
Esta comparación, basada en las prácticas recomendadas por proveedores como Google Cloud en sus análisis de arquitecturas de bases de datos, deja claro que no hay una solución única. La elección depende estrictamente de los requisitos de latencia y escalabilidad de su aplicación.
La configuración por defecto que estrangula el rendimiento de su servidor de datos
Uno de los errores más comunes y costosos en la administración de bases de datos es asumir que la configuración por defecto es adecuada para un entorno de producción. Nada más lejos de la realidad. Esta idea queda perfectamente resumida por un experto del sector.
Los defaults de bases de datos como PostgreSQL o MySQL están diseñados para funcionar en un portátil con 1GB de RAM, y no para un servidor de producción.
– Ed Pollack, SQL Server Query Optimization Techniques
Esta filosofía de «seguridad primero» significa que parámetros críticos como la asignación de memoria, el paralelismo o los niveles de sincronización están configurados en sus valores más bajos y conservadores. El resultado es un servidor de producción con 64 GB de RAM que se comporta como si solo tuviera 2 GB. Es una forma de auto-sabotaje del rendimiento que pasa desapercibida para muchos administradores hasta que los problemas de lentitud se vuelven insostenibles. La inteligencia de configuración consiste en adaptar estos parámetros a los recursos reales de su máquina y a la carga de trabajo específica que soporta.

Por ejemplo, el parámetro `shared_buffers` en PostgreSQL, que define el caché de datos principal, suele venir por defecto en unos míseros 128 MB. En un servidor con 32 GB de RAM, este valor debería estar cerca de los 8 GB (25% del total). Ignorar este ajuste es el equivalente a comprar un coche deportivo y nunca sacarlo de la primera marcha. Ajustar estos valores no es opcional; es el primer paso para desbloquear el verdadero potencial del hardware que ya posee.
Revisar y ajustar la configuración no es una tarea de una sola vez. A medida que la aplicación crece y la carga de trabajo cambia, estos parámetros deben ser monitoreados y afinados. Aquí hay una lista de los ajustes más críticos que, según análisis de optimización para servidores SQL, suelen ofrecer las mayores ganancias de rendimiento:
- work_mem (PostgreSQL) o sort_buffer_size (MySQL): Aumentar de un valor por defecto bajo (ej. 4MB) a 64-256MB puede acelerar drásticamente las ordenaciones y agrupaciones complejas al permitir que se hagan en memoria en lugar de en disco.
- max_parallel_workers_per_gather (PostgreSQL): Por defecto suele estar desactivado. Activarlo con un valor igual a la mitad de los núcleos de CPU disponibles permite paralelizar consultas grandes, reduciendo su tiempo de ejecución.
- synchronous_commit (PostgreSQL): Para cargas de datos masivas (ETL) donde una pequeña pérdida de datos en caso de fallo es aceptable, configurarlo en ‘off’ puede multiplicar la velocidad de escritura.
- shared_buffers (PostgreSQL): La regla general es configurarlo al 25% de la RAM total del servidor para maximizar el caché de datos.
- effective_cache_size (PostgreSQL): Debe informar al planificador de consultas sobre cuánta memoria está realmente disponible para el caché, típicamente un 75% de la RAM total.
Cómo diagnosticar si el cuello de botella es el disco o el procesador en 5 minutos
Cuando una aplicación se vuelve lenta, las sospechas suelen recaer sobre dos componentes principales: la CPU o el sistema de almacenamiento (discos duros o SSD). Saber cuál de los dos es el verdadero culpable es el primer paso para aplicar la solución correcta y no malgastar recursos. Afortunadamente, un diagnóstico proactivo con herramientas estándar de sistemas operativos tipo Unix (como Linux) puede revelar la causa en menos de cinco minutos.
El primer sospechoso suele ser el disco, especialmente si no se utilizan SSD de alto rendimiento. El indicador clave a observar es el `%iowait`. Esta métrica representa el porcentaje de tiempo que la CPU estuvo inactiva esperando a que se completara una operación de entrada/salida (I/O) en el disco. Es la señal más clara de un sistema de almacenamiento sobrecargado. De hecho, las métricas de optimización de WebHosting.de son claras: si el `%iowait` supera el 10% de forma sostenida mientras el uso general de la CPU es bajo, el cuello de botella es, sin duda, el disco.
Si el `%iowait` es bajo (menos del 5%), pero el uso total de la CPU (la suma de `%user` y `%system`) se mantiene por encima del 80-90%, entonces el problema es el procesador. Esto indica que las consultas son demasiado complejas computacionalmente, que hay falta de índices adecuados (lo que obliga a realizar «full table scans») o que el código de la aplicación es ineficiente (como en el problema N+1, que veremos más adelante). En este caso, la solución pasa por optimizar las consultas y el código, no por cambiar los discos.
Para realizar este diagnóstico rápido, se pueden seguir estos pasos con herramientas que ya vienen instaladas en la mayoría de los servidores Linux:
- Ejecute el comando `iostat -x 1 5` en la terminal. Esto le dará 5 mediciones del estado de I/O, una cada segundo.
- Observe la columna `%iowait`. Si su valor promedio está consistentemente por encima del 10%, ha encontrado un cuello de botella de disco.
- A continuación, ejecute `vmstat 1 5`. Esto muestra un resumen del uso de CPU, memoria y otros recursos.
- Si la suma de las columnas `us` (user) y `sy` (system) supera el 80%, mientras que la columna `wa` (iowait) es baja, el cuello de botella es la CPU.
- Para un análisis más granular, utilice `EXPLAIN ANALYZE BUFFERS` en una consulta específica para ver exactamente dónde invierte el tiempo y si está utilizando los índices correctamente.
Cómo elegir la estructura de datos correcta para reducir el uso de memoria en un 50%
La optimización no solo se trata de velocidad de CPU, sino también de eficiencia de memoria. En bases de datos con cientos de millones de registros, una mala elección en los tipos de datos de las columnas puede desperdiciar gigabytes de RAM y espacio en disco. Cada byte ahorrado por fila se multiplica por millones, liberando recursos valiosos que el sistema puede usar para caché o para procesar consultas más complejas. Esta es una de las palancas de optimización más efectivas y a menudo ignoradas.
El error más común es usar tipos de datos genéricos y sobredimensionados. Por ejemplo, definir una columna para almacenar la edad de una persona como `INT` (4 bytes) cuando un `TINYINT` (1 byte, rango 0-255) es más que suficiente. Esto parece un ahorro trivial, pero en una tabla con 10 millones de usuarios, supone un desperdicio de 30 MB de memoria por cada consulta que involucre esa columna. Lo mismo ocurre con el texto: usar `CHAR(255)` para una columna que rara vez supera los 50 caracteres, o `NVARCHAR` (que soporta Unicode) cuando solo se almacenarán caracteres ASCII, duplica innecesariamente el uso de espacio.
Caso práctico: Optimización de tipos de datos para ahorro de espacio
Un análisis de optimización frecuente en entornos de producción demuestra que simplemente cambiar las columnas de texto de longitud fija (`CHAR`) a longitud variable (`VARCHAR`) y de `NVARCHAR` a `VARCHAR` cuando no se requiere soporte Unicode puede reducir el espacio en disco y el uso de memoria hasta en un 50%. Además, la estrategia de usar el tipo de dato entero más pequeño posible (por ejemplo, `TINYINT` en lugar de `INT` para columnas de estado o tipo que solo tienen unos pocos valores posibles) tiene un impacto masivo. Un `TINYINT` consume solo 1 byte por registro, frente a los 4 bytes de un `INT`, un ahorro del 75% para esa columna específica.
La elección de la clave primaria (ID) también es crítica. Usar un `UUID` (16 bytes) por defecto en todas las tablas puede ser un error costoso si un `BIGINT` (8 bytes) o incluso un `INT` (4 bytes) es suficiente. La siguiente tabla compara el coste de memoria de los tipos de datos más comunes, ayudando a tomar una decisión informada.
| Tipo de Dato | Bytes | Uso Recomendado |
|---|---|---|
| UUID | 16 | Solo cuando es absolutamente necesario (sistemas distribuidos) |
| BIGINT | 8 | IDs con más de 2.1 mil millones de registros |
| INT | 4 | IDs estándar (hasta 2.1 mil millones) |
| SMALLINT | 2 | Valores hasta 32,767 (ej. IDs de tablas de catálogo) |
| TINYINT | 1 | Valores hasta 255 (estados, tipos, flags booleanos) |
¿Por qué una mala normalización inicial hace que sus consultas sean lentas dos años después?
La normalización es un pilar del diseño de bases de datos relacionales. Su objetivo es reducir la redundancia y mejorar la integridad de los datos. Sin embargo, tanto la normalización insuficiente como la excesiva pueden crear una deuda técnica de datos que se manifiesta en forma de consultas lentas años después de la implementación inicial. Este problema es sutil, ya que el diseño puede parecer correcto al principio, pero se degrada a medida que los datos y la complejidad de las consultas crecen.
Una normalización insuficiente (por debajo de la Tercera Forma Normal o 3FN) lleva a la duplicación de datos. Por ejemplo, almacenar el nombre del cliente en cada factura en lugar de un ID de cliente. Esto no solo malgasta espacio, sino que hace que las actualizaciones sean una pesadilla y propensas a errores. Por otro lado, una normalización excesiva (ir más allá de la 3FN sin una buena razón) fragmenta los datos en demasiadas tablas pequeñas. Para reconstruir una entidad de negocio completa, como un «pedido con detalles del cliente y productos», se necesitan múltiples y costosos `JOINs`.
Este dilema lo captura perfectamente una experta en optimización de bases de datos.
La normalización excesiva (más de 3FN) o insuficiente genera ‘intereses’ en forma de JOINs cada vez más costosos a medida que los datos crecen, un interés que se paga con latencia.
– Maria Paula Vizcaíno, Optimización de Consultas SQL: Bases de Datos Relacionales – Pragma
El equilibrio correcto generalmente se encuentra en la 3FN, pero en entornos con un alto volumen de lecturas y requisitos de bajo tiempo de respuesta (como en la mayoría de las aplicaciones web), una desnormalización controlada puede ser una estrategia de optimización poderosa. Consiste en duplicar intencionadamente ciertos datos para evitar `JOINs` costosos. Por ejemplo, añadir el nombre del producto directamente en la tabla de «líneas de pedido» para no tener que unirla con la tabla de «productos» cada vez que se muestra un carrito de la compra. Esta decisión debe tomarse con cuidado, ya que introduce redundancia, pero el beneficio en rendimiento puede ser enorme.
Puntos clave a recordar
- La optimización del rendimiento es un problema de software y arquitectura, no solo de hardware.
- Un diagnóstico proactivo para identificar cuellos de botella (I/O, CPU, memoria) es el primer paso antes de cualquier cambio.
- La configuración por defecto de las bases de datos está diseñada para ser conservadora y debe ser ajustada a los recursos del servidor de producción.
- La estructura de datos, incluyendo el particionamiento y la elección de tipos de datos, tiene un impacto masivo en el uso de memoria y la velocidad de las consultas.
Cómo optimizar código para reducir el consumo de CPU en la nube y bajar la factura
A menudo, el cuello de botella no está en la base de datos en sí, sino en la forma en que la aplicación interactúa con ella. Un código ineficiente puede inundar el servidor con miles de consultas triviales, consumiendo ciclos de CPU y disparando la factura de la nube. El culpable más notorio de este comportamiento es el problema N+1, un anti-patrón de acceso a datos extremadamente común en aplicaciones que usan ORM (Object-Relational Mapping).
Imagine que necesita mostrar una lista de 100 artículos de un blog, cada uno con el nombre de su autor. Un código con el problema N+1 primero haría una consulta para obtener los 100 artículos (`1` consulta). Luego, dentro de un bucle, haría una consulta separada para obtener el autor de cada uno de esos 100 artículos (`N` consultas). El resultado: 101 consultas a la base de datos para una operación que podría haberse resuelto con solo dos. Un análisis de optimización de CódigoFacilito explica que una consulta N+1 puede ejecutar más de 100 queries donde bastaría con una sola operación `JOIN`.
Este patrón es devastador para el rendimiento. No solo multiplica la latencia de red, sino que también somete a la base de datos a un estrés innecesario, consumiendo conexiones y ciclos de CPU para planificar y ejecutar cientos de micro-consultas. En un entorno de nube donde se paga por uso de CPU y por transferencia de datos, el problema N+1 puede aumentar la factura de forma exponencial sin que los desarrolladores se den cuenta.
La solución es utilizar técnicas como «eager loading» (carga ansiosa). La mayoría de los ORM modernos permiten especificar qué relaciones deben cargarse de antemano. Al solicitar los 100 artículos, se le indica al ORM que también traiga a todos los autores correspondientes en una segunda consulta optimizada (usando una cláusula `WHERE autor_id IN (…)`). De esta forma, el problema N+1 (101 consultas) se convierte en un problema 1+1 (solo 2 consultas), reduciendo drásticamente la carga sobre la base de datos y el consumo de CPU.
Aplicar estas estrategias de optimización de código, configuración y arquitectura de datos es el siguiente paso lógico para cualquier equipo que busque mejorar el rendimiento. Evalúe sus sistemas a la luz de estos principios para identificar las oportunidades de mejora más impactantes y empezar a liberar el verdadero potencial de su infraestructura actual.
Preguntas frecuentes sobre Cómo reducir los tiempos de carga de datos masivos sin cambiar de servidor
¿Cuándo es aceptable desnormalizar una base de datos?
La desnormalización es aceptable, e incluso recomendable, en escenarios donde las consultas de lectura superan ampliamente a las de escritura (por ejemplo, una proporción 80/20). Si los `JOINs` complejos para reconstruir datos son la principal causa de lentitud en una operación crítica para el negocio, introducir redundancia controlada para eliminarlos es una estrategia de optimización válida.
¿Qué es el anti-patrón EAV y por qué evitarlo?
El modelo EAV (Entity-Attribute-Value) consiste en almacenar datos en tripletas (Entidad, Atributo, Valor) en lugar de en columnas fijas. Aunque parece extremadamente flexible al principio, se convierte en una pesadilla de rendimiento a medida que los datos crecen. Reconstruir una sola entidad requiere múltiples `auto-JOINs` (self-joins), haciendo que las consultas eficientes sean prácticamente imposibles y que los planes de ejecución se disparen en complejidad.
¿Cómo detectar problemas de normalización?
Las señales de alerta de un mal diseño de normalización son claras. Si una consulta común y recurrente en su aplicación necesita más de 4 o 5 `JOINs` para obtener la información necesaria, es un fuerte indicio de sobre-normalización. Del mismo modo, si al analizar el plan de ejecución de una consulta (`EXPLAIN`) se observan bucles anidados excesivos («Nested Loops») sobre tablas grandes, es muy probable que haya un problema fundamental en el diseño del esquema.