Ingeniería de base de datos
Ingeniería de base de datos y Eloquent
El trabajo de consultas que hay detrás de una aplicación Laravel que ha superado su esquema - eliminar N+1, índices a partir de planes reales, y migraciones que no bloquean una tabla en producción.
Casi toda aplicación Laravel que se vuelve lenta se vuelve lenta en la base de datos, y casi todas ellas iban bien en desarrollo. Una página que ejecuta once consultas contra cuarenta filas ejecuta once consultas contra cuatro millones de filas de la misma manera, y solo una de las dos cosas es sobrevivible.
Adónde se va el tiempo en realidad
A lo largo de los encargos que hacemos, las causas se agrupan en una lista corta.
Un N+1 que solo aparece en producción. La página de listado carga su relación por adelantado. La de detalle no, porque solo carga un registro, y entonces alguien reutiliza el componente de detalle dentro de un bucle. El número de consultas pasa de dos a doscientas y nada en el diff parece mal.
Una relación cargada dentro de un accessor.
getFullAddressAttribute() toca $this->country, el accessor se llama durante
la serialización, y la aplicación emite una consulta por fila mientras aparenta
estar formateando una cadena.
Un índice que existe y no se usa. Una columna envuelta en una función, una conversión implícita de tipo entre una columna de texto y un parámetro entero, un comodín inicial en un LIKE. El índice está ahí, EXPLAIN dice que no se está leyendo, y todo el mundo confía en el esquema en lugar del plan de la consulta.
Agregación hecha en PHP. La tubería de colecciones es lo bastante expresiva
como para que ->get()->groupBy()->map() se lea mejor que el SQL, así que cien
mil filas se convierten en modelos para producir seis números.
Una migración que bloqueó una tabla en producción. Añadir un índice, añadir una columna con valor por defecto, o cambiar un tipo de columna, sobre una tabla lo bastante grande como para que el bloqueo dure más que el tiempo de espera de la petición. El despliegue tuvo éxito; el sitio estaba caído.
Cómo trabajamos
Medir primero, desde el sistema real. Registros de consultas lentas, el número de consultas por endpoint, y EXPLAIN sobre las consultas que importan. No un profiler en un portátil con una base de datos sembrada, que le informa de forma fiable sobre un problema que no tiene.
Arreglar la causa y no el síntoma. Una caché delante de una consulta mala es una forma de pagar la consulta mala con menos frecuencia. A veces esa es la decisión correcta y lo diremos; con más frecuencia la consulta quiere un índice, un join, o no emitirse en tiempo de ejecución en absoluto.
Hacer permanente el arreglo. Carga perezosa desactivada fuera de producción, para que el N+1 que acaba de eliminar no pueda reintroducirse en silencio. Una aserción sobre el número de consultas en los endpoints que importan, para que una carga anticipada eliminada en el futuro haga fallar una prueba y no un ticket de soporte. Ambas son unas pocas líneas y son la diferencia entre un arreglo y un arreglo que se queda.
Trabajo de esquema en un sistema que no puede parar
La mayor parte de lo que nos piden arreglar no es una consulta sino una forma: una columna de estado que debería ser una tabla de estados, una relación polimórfica que hizo imposible restringir dos filas, un registro de auditoría que crece sin política de retención, una tabla que hace tres trabajos porque dividirla parecía caro hace dos años.
Ese trabajo se hace en pasos individualmente seguros:
- Añadir la nueva estructura junto a la vieja, sin que nada la lea.
- Rellenar por lotes, en una cola, con un progreso que sobreviva a un despliegue.
- Escribir en ambas, leer de la vieja, y conciliar hasta que la diferencia sea cero.
- Cambiar las lecturas. Esperar. Luego dejar de escribir en la vieja.
- Eliminarla, en una entrega aparte, una vez que nada la haya tocado en una semana.
Son más pasos que una sola migración y son la diferencia entre un cambio de esquema y una interrupción programada.
Qué recibe
Un documento de hallazgos con cada problema trazado hasta la consulta o la migración que lo causa y dimensionado por esfuerzo, los arreglos como pull requests revisables, los cambios de índices como migraciones seguras de ejecutar con su volumen de datos, y las aserciones en CI que impiden que las regresiones vuelvan.
Cuando la respuesta es estructural y no una reescritura de consulta, los hallazgos lo dicen y ponen ambas cifras una al lado de la otra: qué cuesta cambiarlo y qué cuesta dejarlo durante el próximo año. Eso debería leerlo en la primera semana y no oírlo en una reunión de cierre.
Antes de empezar, la gente suele querer leer dos cosas: lo que cuesta un cambio de esquema que bloquea una tabla en producción y en qué se diferencian de verdad MySQL y Postgres para una aplicación Laravel. Si lo lento resulta ser la ruta de la petición y no la consulta, rendimiento es el encargo vecino.
