BRIN colapsa tras pocas actualizaciones: cuándo el índice ligero de Postgres deja de podar

Fuentes: BRIN is 1/4570th the size of a B-tree, until 5% of rows are updated

PostgreSQL presume de BRIN como un índice minúsculo —48 kB frente a los 214 MB de un B-tree sobre 10 millones de filas ordenadas—, pero esa ventaja se evapora con apenas un 5% de filas actualizadas. Una prueba controlada en Postgres 17.9, con una tabla de 976 MB sobre la columna created_at y consultas de rango de un día, muestra que el índice pasa de leer 1.536 páginas a 51.268, multiplicando por 28 la E/S y por 23 el tiempo de ejecución, mientras que la correlación de pg_stats apenas baja de 1.000 a 0.921, una cifra que la mayoría interpretaría como 'bien correlacionada'.

La causa es estructural: BRIN resume cada rango de páginas con sus valores mínimo y máximo. Una UPDATE en Postgres escribe una nueva versión de la tupla que suele aterrizar lejos de su posición original; basta con que unas pocas filas mal ubicadas ensanchen los extremos de un rango para que todo el bloque de 128 páginas coincida con cualquier predicado futuro. El fenómeno es un precipicio, no una pendiente: la poda cae de golpe.

El artículo, firmado por Venkat Sakamuri —ex del equipo de motor de consultas de Oracle y coinventor de los zonemaps de Oracle—, recomienda no fiarse de pg_stats.correlation como señal de salud y vigilar en su lugar el número de bloques 'lossy' del plan. La solución, CLUSTER sobre la tabla reorganizada, recupera la poda por completo, pero exige un bloqueo ACCESS EXCLUSIVE y, en producción, herramientas como pg_repack con el coste de duplicar almacenamiento y generar WAL equivalente. Para tablas genuinamente append-only —logs de eventos, métricas o auditorías— BRIN sigue siendo la mejor relación coste-rendimiento del catálogo de Postgres; en tablas con actualizaciones in situ pierde su ventaja sin avisar.