El dilema: ¿una consulta gigante o varias pequeñas?
Cuando usas .Include()
en EF Core, por defecto se genera una sola consulta SQL con múltiples JOIN
. Este enfoque, llamado SingleQuery, funciona bien en estructuras simples. Pero en relaciones complejas, puede provocar una explosión cartesiana que arrastra millones de filas duplicadas.
Para solucionarlo, EF Core introdujo SplitQuery. Este modo divide la carga en múltiples consultas SQL, una por cada relación. ¿El objetivo? Menos duplicación, mejor uso de memoria… pero a costa de más accesos a la base de datos.
Benchmark real con BenchmarkDotNet
Ejecutamos múltiples pruebas con modelos tipo Blog -> Posts -> Comments -> Owners
. Comparamos SingleQuery
, SplitQuery
y una versión optimizada con Raw SQL
.
Escenario | Método | Tiempo Medio | Memoria | GC Gen0 | GC Gen1 |
---|---|---|---|---|---|
1 blog, 1 post, 10 comentarios | SingleQuery | 885 µs | 113 KB | 7.8 | 0 |
SplitQuery | 1,991 µs | 120 KB | 7.8 | 0 | |
Raw SQL | 663 µs | 18 KB | 0.9 | 0 | |
100 blogs, 100 posts, 10 comentarios | SingleQuery | 22,5 s | 283 MB | 22k | 11k |
SplitQuery | 25,1 s | 243 MB | 17k | 11k | |
Raw SQL | 4,4 s | 124 MB | 11k | 6k |
Resultado clave:
-
Raw SQL fue el más eficiente en todos los escenarios.
-
SplitQuery redujo el uso de memoria en escenarios grandes (~15% menos que SingleQuery).
-
SingleQuery fue más rápido en relaciones pequeñas o poco profundas.
Análisis: ¿Cuándo usar cada uno?
Escenario | Recomendación |
---|---|
Relaciones simples (1:1 o pocos Include ) |
SingleQuery |
Muchas entidades relacionadas (1:N:N) | SplitQuery |
Alta concurrencia / consistencia estricta | SingleQuery |
Poca RAM o GC bajo presión | SplitQuery |
Red local / baja latencia | SplitQuery |
Datos estables y consulta intensiva | Raw SQL |
Consideraciones adicionales
-
Transacciones:
SplitQuery
no es atómico. Usa transacciones si necesitas consistencia fuerte. -
Timeouts: más consultas = más riesgo de timeout si la red o BD está lenta.
-
Ordenamientos con Skip/Take: asegúrate de ordenar por campos únicos para evitar resultados incorrectos con
SplitQuery
.
Conclusión
No hay un ganador absoluto.
La elección entre SingleQuery
y SplitQuery
debe basarse en el contexto real de tus datos, el diseño de tus entidades y las necesidades de rendimiento o consistencia.
Sin embargo, algo que veo con demasiada frecuencia —especialmente en proyectos donde se copia y pega código sin cuestionar— es que se elige siempre uno u otro sin entender realmente las implicaciones. Rara vez se hace el ejercicio de parar, analizar y pensar críticamente:
¿Qué estoy cargando? ¿Cuál es la estructura? ¿Qué opción se adapta mejor a este caso concreto?
Los datos del benchmark muestran que, en muchos escenarios, un enfoque híbrido (usar SplitQuery
cuando hay muchas colecciones y SingleQuery
cuando hay pocas) sería óptimo, pero casi nunca se aplica.
Reflexiona antes de codificar. Las decisiones por inercia pueden costarte caro en rendimiento y escalabilidad.