Saltar al contenido

Parte 09 — Almacenamiento, índices y planes

Por qué una consulta tarda: páginas, estructuras de índice, estadísticas y la lectura honesta de un plan de ejecución.

5 clases · 17 horas · 23 conceptos · 12 fuentes

Antes de esta parte

Esta parte se apoya en lo trabajado antes. Si vienes de fuera del programa, revisa al menos el vocabulario de:

De qué trata esta parte

Por qué una consulta tarda, respondido desde el disco hacia arriba. La parte empieza donde de verdad empieza el costo —la página, no la fila— y termina en la única herramienta que convierte el rendimiento en un asunto de evidencia: el plan de ejecución.

Primero páginas, factor de bloque, buffer y localidad, que explican por qué a veces recorrer la tabla entera le gana a usar el índice. Después las dos grandes familias de estructuras: B-Tree, con la regla del prefijo más a la izquierda que decide el orden de las columnas de un índice compuesto, y LSM-Tree, con su compactación y su amplificación de escritura. Luego los índices especializados, cada uno con el caso concreto en que gana y con el costo de mantenimiento que hace que un índice inútil sea una penalización permanente. Y al final leer `EXPLAIN` para refutar una hipótesis, comparando filas estimadas contra reales nodo a nodo.

La clase 052 es la que cambia la forma de trabajar: después de ella, «creo que va lento por el índice» deja de ser una frase aceptable sin un plan al lado.

Al terminar esta parte podrás

  1. Explicar por qué la unidad de costo es la página y qué consecuencias tiene para el diseño de la fila.
  2. Elegir el orden de las columnas de un índice compuesto y justificarlo con las consultas que debe servir.
  3. Comparar B-Tree y LSM-Tree en términos de amplificación de lectura y de escritura.
  4. Elegir el índice especializado adecuado y contar su costo de mantenimiento.
  5. Leer un plan de ejecución y refutar una hipótesis de rendimiento con filas estimadas frente a reales.

Las clases, una por una

#ClaseNivelHorasFuentes
048Páginas, filas y buffer: por qué la entrada y salida mandaIntermedio33
049B-Tree: estructura, orden de columnas y selectividadIntermedio43
050LSM-Tree, compactación y amplificación de escrituraAvanzado33
051Índices especializados: hash, GIN, GiST, BRIN, parciales y cubrientesAvanzado33
052Planes de ejecución: leer EXPLAIN y refutar una hipótesisAvanzado43

048 — Páginas, filas y buffer: por qué la entrada y salida manda

Intermedio · 3 h · 3 fuentes · requiere 012

Por qué la entrada y salida manda: el motor no lee filas, lee páginas. De ahí salen el factor de bloque, la localidad y la ventaja de la lectura secuencial, que explica por qué a veces recorrer la tabla entera le gana a usar el índice —y por qué el planificador lo elige a propósito.

pagina factor de bloque buffer pool localidad lectura secuencial

049 — B-Tree: estructura, orden de columnas y selectividad

Intermedio · 4 h · 3 fuentes · requiere 048

El B-Tree y las dos preguntas que responde en la práctica: en qué orden poner las columnas de un índice compuesto —la regla del prefijo más a la izquierda— y cuándo el índice no compensa, que es cuando la selectividad es baja. Introduce el índice cubriente, la optimización con mejor relación entre esfuerzo y resultado.

B-Tree prefijo más a la izquierda selectividad índice cubriente

050 — LSM-Tree, compactación y amplificación de escritura

Avanzado · 3 h · 3 fuentes · requiere 048

La otra familia de estructuras de almacenamiento: memtable, SSTable y compactación. Explica por qué un LSM absorbe mucha más escritura que un B-Tree y qué paga a cambio —amplificación de escritura y compactaciones que consumen recursos justo cuando el sistema está cargado—, con el filtro de Bloom como pieza que salva lecturas.

memtable SSTable compactación amplificación de escritura filtro de Bloom

051 — Índices especializados: hash, GIN, GiST, BRIN, parciales y cubrientes

Avanzado · 3 h · 3 fuentes · requiere 049

Los índices que no son B-Tree y el caso concreto en que cada uno gana: hash para igualdad pura, GIN para contenido de arreglos y documentos, GiST para geometría y rangos, BRIN cuando el orden físico se correlaciona con la columna, más parciales, de expresión y cubrientes. Cierra con el costo de mantenimiento, que hace que un índice inútil no sea neutro sino una penalización permanente.

índice parcial índice de expresión GIN BRIN costo de mantenimiento

052 — Planes de ejecución: leer EXPLAIN y refutar una hipótesis

Avanzado · 4 h · 3 fuentes · requiere 049, 051

Leer un plan de ejecución para refutar una hipótesis, no para confirmarla. La técnica central es comparar filas estimadas contra reales nodo a nodo: un error de estimación explica casi cualquier plan absurdo. Insiste en que el `cost` no son milisegundos y en que solo `EXPLAIN ANALYZE` mide tiempo.

optimizador por costos estadística estimación de cardinalidad costo frente a tiempo

Errores frecuentes en esta parte

Cada uno de estos es una creencia habitual y su corrección.

Vocabulario de la parte

Los 23 términos que esta parte introduce. Todos están también en el glosario del programa con sus términos relacionados.

TérminoQué significaSe trabaja en
amplificación de escrituraCuántos bytes acaba escribiendo el motor en disco por cada byte que escribió la aplicación, sumando registro y compactaciones sucesivas. Es la métrica que decide el desgaste del disco y el techo real de escritura de un motor LSM.050
B-TreeÁrbol equilibrado de páginas ordenadas, con todos los datos en hojas enlazadas entre sí. Sirve para igualdad, para rangos y para devolver ya ordenado, con un número de accesos que crece logarítmicamente. Es la estructura por defecto de casi todos los motores relacionales.049
BRINÍndice de rangos por bloque: guarda el mínimo y el máximo de cada grupo de páginas. Diminuto y utilísimo cuando el orden físico se correlaciona con la columna —una tabla de eventos por fecha—, e inútil cuando no.051
buffer poolLa memoria donde el motor mantiene las páginas leídas para no volver a pedirlas al disco. Su tasa de acierto explica la mayor parte de la diferencia entre una consulta de 2 ms y la misma consulta de 200 ms.048
compactaciónProceso de fusionar SSTables, descartar versiones antiguas y aplicar los borrados. Es lo que impide que las lecturas se degraden sin fin, y también lo que consume entrada y salida en segundo plano justo cuando el sistema está cargado.050
costo de mantenimientoLo que cada índice cobra en cada `INSERT`, `UPDATE` y `DELETE`, más el espacio que ocupa y el trabajo de reconstruirlo. Un índice que no usa ninguna consulta no es neutro: es una penalización permanente sobre todas las escrituras.051
costo frente a tiempoEl `cost` de `EXPLAIN` es una unidad interna comparativa, no milisegundos; el tiempo real solo aparece con `EXPLAIN ANALYZE`. Comparar filas estimadas contra filas reales en cada nodo es la técnica central para refutar una hipótesis de rendimiento.052
estadísticaResúmenes que el motor guarda sobre los datos: número de filas, valores distintos, histogramas, valores más comunes. Cuando están obsoletas el optimizador estima mal y elige planes ruinosos, y ese es el primer sitio donde mirar ante una consulta que «de repente» se volvió lenta.052
estimación de cardinalidadCuántas filas cree el planificador que devolverá cada paso. Es la entrada de la que depende todo lo demás, y también la parte más frágil: los errores se multiplican al reunir tablas, y una estimación de 1 fila que en realidad son 100 000 explica casi cualquier plan absurdo.052
factor de bloqueCuántas filas caben en una página. Depende del ancho de la fila, así que columnas anchas que nadie consulta encarecen todas las lecturas de esa tabla, incluidas las que no las piden.048
filtro de BloomEstructura probabilística compacta que responde «seguro que no está» o «puede que esté». Ahorra abrir SSTables que no contienen la clave; nunca produce falsos negativos, así que es seguro usarla para descartar.050
GINÍndice invertido generalizado de PostgreSQL: indexa los elementos de un valor compuesto —palabras de un texto, claves de un JSONB, elementos de un arreglo—. Es rápido buscando y caro escribiendo, y por eso admite una cola de actualizaciones diferida.051
lectura secuencialLeer páginas contiguas, que es órdenes de magnitud más barato por fila que saltar de una a otra. Por eso un recorrido completo puede ganarle a un índice cuando la consulta devuelve una fracción grande de la tabla.048
localidadQue los datos que se usan juntos estén guardados juntos. Es la propiedad que convierte muchas lecturas lógicas en pocas lecturas físicas, y el motivo por el que el orden físico de una tabla —y la clave de agrupamiento— importa tanto.048
memtableEstructura ordenada en memoria donde un motor LSM acumula las escrituras antes de volcarlas a disco. Convierte escrituras aleatorias en secuenciales, que es la razón de que los LSM absorban mucha más carga de escritura que un B-Tree.050
optimizador por costosComponente que enumera planes equivalentes y elige el de menor costo estimado a partir de estadísticas. Desde el artículo de Selinger de 1979 el principio no ha cambiado: el motor no ejecuta lo que escribiste, ejecuta lo que calculó que es más barato.052
paginaLa unidad mínima de lectura y escritura en disco, típicamente de 4 a 16 KB. El motor nunca lee «una fila»: lee la página que la contiene, y de ahí que quepan más filas por página sea una optimización real.048
prefijo más a la izquierdaUn índice sobre `(a, b, c)` solo sirve para filtros que fijan `a`, o `a` y `b`, o los tres —nunca para `b` solo—. Es la regla que decide el orden de las columnas de un índice compuesto y la que explica por qué «tengo el índice y no lo usa».049
selectividadQué fracción de la tabla devuelve un predicado. Un índice compensa cuando la selectividad es alta —pocas filas—; con predicados poco selectivos, el recorrido secuencial gana y el planificador lo elige a propósito.049
SSTableFichero ordenado e inmutable resultante de volcar una memtable. Al ser inmutable no se actualiza: los cambios posteriores viven en ficheros más nuevos, y por eso una lectura puede tener que consultar varios niveles.050
índice cubrienteÍndice que incluye todas las columnas que la consulta necesita, así que el motor responde sin volver a la tabla. En PostgreSQL se construye con `INCLUDE`; su costo es un índice más ancho y más caro de mantener en cada escritura.049
índice de expresiónÍndice sobre el resultado de una función, como `lower(correo)`. Es lo que permite que una búsqueda insensible a mayúsculas use índice, siempre que la consulta escriba la expresión exactamente igual que el índice.051
índice parcialÍndice que solo cubre las filas que cumplen un predicado (`WHERE activo`). Ocupa una fracción del total y se mantiene más barato, y encaja perfectamente cuando las consultas siempre filtran por ese mismo estado.051

Fuentes usadas en esta parte

12 obras distintas sostienen lo que se afirma en estas 5 clases.

Otras partes