PostgreSQL: no más VACUUM ni bloat
(orioledata.com)- OrioleDB, un nuevo motor de almacenamiento para PostgreSQL, elimina el proceso de VACUUM de alto consumo de recursos y resuelve el problema del bloat en las tablas
- PostgreSQL tradicional requiere vacuum manual y autovacuum debido a su arquitectura MVCC, y ambos métodos consumen una cantidad considerable de recursos del sistema
- OrioleDB procesa los cambios de datos de forma eficiente y consolida los datos fragmentados mediante undo log a nivel de fila y bloque y fusión automática de páginas
- En benchmarks sintéticos, al actualizar solo un índice, fusionar automáticamente páginas dispersas y aplicar WAL a nivel de fila, logró 5 veces más TPS y un menor uso de recursos
- Resuelve el antiguo problema de VACUUM, reduce la carga de mantenimiento y mejora el rendimiento y la experiencia de usuario de PostgreSQL
Historia y límites de VACUUM en PostgreSQL
- El proceso de VACUUM es un legado histórico derivado del concepto de infinite time-travel del proyecto Berkley Postgres, concepto que luego fue descartado por la comunidad
- Sin embargo, esto llevó a una implementación del sistema MVCC (Multi-Version Concurrency Control) vulnerable al bloat en las tablas
- MVCC es ventajoso para manejar transacciones concurrentes, pero introduce la necesidad de vacuum manual para limpiar datos antiguos e innecesarios
- El vacuum manual es una tarea intensiva en trabajo y una posible fuente de ineficiencia del sistema
- Más adelante se introdujo autovacuum para reducir la carga del trabajo manual
- Fue un gran avance, pero no una solución perfecta, y aunque es automático sigue consumiendo bastantes recursos del sistema
- Se menciona como una de las razones por las que Uber migró de PostgreSQL a MySQL, y como una de las 10 quejas sobre PostgreSQL señaladas por Richard Branson
- La introducción de las actualizaciones Heap-Only Tuples (HOT) y microvacuum redujo la necesidad de hacer vacuum de tablas completas
- Aun así, VACUUM siguió siendo una tarea intensiva en recursos y las tablas continuaron siendo vulnerables al bloat
- Esto se menciona como la parte de PostgreSQL que el equipo de OtterTune más detesta
- A pesar de estas limitaciones, muchas organizaciones y desarrolladores siguieron usando PostgreSQL por su solidez, escalabilidad y fuerte comunidad, entre otras razones
- OtterTune decidió seguir usando PostgreSQL aun reconociendo el problema
Funciones clave de OrioleDB
- OrioleDB es un nuevo motor para PostgreSQL desarrollado principalmente para proteger las tablas del bloat y eliminar la necesidad de mantenimiento periódico como VACUUM
- Lo logra mediante undo log a nivel de fila y bloque y fusión automática de páginas
- El undo log a nivel de fila y bloque ofrece un control más fino y permite procesar los cambios de datos de forma eficiente
- La fusión automática de páginas consolida continuamente en segundo plano los datos fragmentados
- Cómo funciona cada técnica
- El undo log a nivel de fila permite actualizaciones in-place
- El undo log a nivel de bloque elimina del almacenamiento principal las tuplas que fueron borradas pero siguen siendo visibles para algunas transacciones, liberando espacio para nuevas tuplas
- La fusión automática de páginas dispersas protege tablas e índices del bloat incluso después de muchas eliminaciones
- Como resultado, se reduce la intervención manual, baja el consumo de recursos y disminuye la vulnerabilidad al bloat en las tablas
Benchmark
- Se configuró un benchmark sintético con un script de inicialización que crea 1 tabla y 5 índices
- Crea la tabla
testcon clave primariaidy columnasvalue1~value4(float8) yts(timestamp) - Crea índices sobre
value1,value2,value3,value4yts
- Crea la tabla
- El script de pgbench usa una forma de upsert que hace una actualización dispersa de un índice cuando ocurre una colisión
- Ejecuta
INSERTcon unidaleatorio en el rango de 1 a 10,000,000, y si hay colisión actualiza solots - Esta actualización dispersa provoca bloat en los índices en las tablas heap normales de PostgreSQL
- Ejecuta
- Lo que este benchmark muestra sobre las ventajas del diseño de OrioleDB
- Gracias al undo log y a las actualizaciones in-place, OrioleDB actualiza solo el índice cuyo valor cambió, mientras que el motor heap de PostgreSQL hace que una actualización de un solo campo indexado desactive HOT, por lo que se actualizan todos los índices
- La fusión automática de páginas protege los índices dispersos del bloat, y las páginas dispersas se fusionan automáticamente
- El WAL a nivel de fila ocupa mucho menos espacio que el WAL a nivel de bloque, lo que permite reducir IOPS al registrar WAL
- Resultados acumulados de estas mejoras en OrioleDB
- 5 veces más TPS por transacción
- 2.3 veces menos carga de CPU por transacción
- 22 veces menos IOPS por transacción
- Sin bloat en tablas ni índices
Importancia de OrioleDB
- Con la adopción de OrioleDB, la comunidad de PostgreSQL entra en una nueva era en la que VACUUM puede quedar en el pasado
- Presenta una solución a uno de los problemas más antiguos de PostgreSQL y ofrece a los usuarios mayor eficiencia y menor carga de mantenimiento
1 comentarios
Opiniones de Hacker News
Aun así, hay varias cosas que me hacen ruido. El procesamiento por fila genera problemas de alineación de escrituras y de fsync, y las páginas son más fáciles de alinear que las filas de tamaño arbitrario.
PostgreSQL es muy, quizá extremadamente, conservador con la seguridad de los datos, y eso se logra principalmente con fsync en los momentos adecuados; su impacto se propaga por toda la pila de I/O, incluido el firmware de los SSD, y la vuelve más lenta.
MVCC es muy bueno para el acceso concurrente, pero el documento de Oriole no dice bajo qué condiciones de concurrencia se obtuvo el gráfico.
El título y la introducción del documento de Oriole se centran en resolver VACUUM, pero no parece que hayan demostrado que el gráfico de “onda cuadrada” que se ve en PostgreSQL se deba realmente, en su mayor parte, a VACUUM. Otro benchmark de Percona (https://www.percona.com/blog/evaluating-checkpointing-in-postgresql/) no muestra un patrón de onda cuadrada tan marcado.
Los autores seguramente conocen estos problemas, así que podrían escribir un resumen de cómo los abordaron.
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
Dado que PostgreSQL es conservador con la seguridad de los datos, el primer objetivo es que sea una extensión pura. Para que forme parte de PostgreSQL, necesita pasar la prueba del tiempo.
Es cierto que faltaba la información de concurrencia, y agregamos al post del blog el tipo de VM y la información de concurrencia.
El patrón de onda cuadrada sí se debe a los checkpoints. En este caso, la causa de la mejora en realidad no es VACUUM, sino el enfoque de modificar solo los índices relacionados y el WAL por fila, que reduce el I/O total.
Se ve genial, pero me pregunto cómo planean seguir el ciclo de lanzamientos del proyecto padre.
Si hicieron un fork de un gran proyecto open source y le agregaron cambios significativos, y no creen que esos cambios vayan a ser aceptados upstream, no sé si sea una buena idea.
Deberían dejar claro si es un juguete hecho por diversión o un proyecto serio que van a mantener a futuro.
Si es un proyecto serio, deberían explicitar el plan para que no se convierta en software abandonado en el futuro, el plan para absorber futuros lanzamientos del proyecto original, o bien el plan para separarse como un proyecto completamente independiente.
Como es una extensión, parece que podrían recibir cambios de PostgreSQL upstream, pero si eso fuera fácil, ya habría formado parte del proyecto PostgreSQL desde el principio, así que no parece tan sencillo.
“Correcto. Pero todavía falta mucho camino. En este momento OrioleDB es una extensión que se distribuye junto con parches al core de PostgreSQL. El objetivo a mediano plazo es convertir OrioleDB en una extensión pura, y el objetivo a largo plazo es hacer que OrioleDB forme parte del core de PostgreSQL”.
La próxima vez sería mejor no entrar tan fuerte desde el principio.
https://news.ycombinator.com/item?id=36742001
Ver un ejemplo de que se puede arreglar definitivamente ayuda. Si una empresa tiene recursos para mantener un fork de Postgres, perfecto; si no, puede evaluar si la dificultad de usar este fork compensa frente a la ganancia de rendimiento.
Ya basta con lo de “Uber se pasó de Postgres a MySQL”. Uber pasó de usar Postgres como base de datos relacional a lo que en la práctica es su propia base de datos no relacional, usando MySQL como almacén distribuido de clave-valor, así que no es una situación que aplique tal cual a la mayoría de los usuarios de Postgres.
En cualquier caso, este diseño MVCC que mueve los datos antiguos a logs/segmentos de deshacer también se usa en Oracle DB, así que el funcionamiento en sí está validado.
La dificultad común es que leer versiones antiguas de los datos se vuelve más lento. Hay que buscarlas en el log y, a veces, los datos se eliminan del log antes de que termine la transacción, lo que puede producir el infame error Snapshot Too Old.
En el artículo no vi cuándo se eliminan las filas del log de deshacer. Si se eliminan cuando ya no son necesarias, haría falta una cantidad similar de trabajo de administración, así que no me queda claro de dónde sale la mejora; y si es un búfer circular que puede quedarse sin espacio, como en Oracle, entonces bajo una alta carga de escritura las transacciones largas empiezan a fallar, lo cual es bastante desagradable.
Depende de qué se entienda exactamente por “trabajo de administración”. Si hablamos de volumen de trabajo, la mejora viene de que los registros de deshacer antiguos pueden borrarse por lotes de forma muy barata, por ejemplo haciendo
unlinkde archivos. No hace falta un escaneo de VACUUM.Si hablamos de uso de espacio, es cierto que la misma cantidad de versiones ocupa la misma cantidad de espacio. Pero poner las versiones antiguas de filas en un almacenamiento separado evita la degradación a largo plazo del almacenamiento principal. OrioleDB también implementa la fusión automática de páginas dispersas.
OrioleDB implementa un búfer circular en memoria para el log de deshacer. Si el búfer circular no puede contener todos los registros de deshacer, expulsa los registros más antiguos al almacenamiento. Actualmente no pone límite al tamaño del log de deshacer y mantiene los registros mientras cualquier transacción pueda necesitarlos, así que no hay errores “Snapshot Too Old”.
Dicho eso, se podría considerar implementar como opción un error al estilo de Oracle para poder limitar el tamaño del área de deshacer. También vale la pena revisar el documento de arquitectura en GitHub.
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
Claro que MySQL evita VACUUM a costa de ceder bastante en consideraciones de concurrencia.
El título me generó expectativa, pero la primera impresión fue frustrante. El README principal en GitHub[1] se siente como frases de marketing corporativo.
Hasta ahora lo que me queda claro es que OrioleDB es un nuevo motor de almacenamiento para PostgreSQL, que PostgreSQL es la base de datos “más querida”, que OrioleDB es una extensión construida sobre otras extensiones y que OrioleDB abre la puerta a la nube.
Después de las estafas de criptomonedas y Web 3.0, pensaría que al presentar un proyecto que amplía y mejora algo tan importante como PostgreSQL se evitaría este enfoque.
[1] https://github.com/orioledb/orioledb
No sé si se pueda escribir mucho más claro que eso. Los métodos de acceso a tablas son una funcionalidad introducida en PostgreSQL para admitir esquemas de almacenamiento alternativos como zheap o almacenamiento orientado a columnas.
Mencionar esto es importante. Hay bastantes forks de PostgreSQL con sistemas alternativos de almacenamiento de datos encima; OrioleDB, en cambio, está diseñado para funcionar como extensión en PostgreSQL sin hacer fork. Todavía no está ahí, pero esa es la dirección.
Si estás familiarizado con PostgreSQL, el README parece bastante claro.
El diseño de Oriole parece requerir índices conscientes de las transacciones y eliminación puntual de entradas, y eso tiene un costo propio.
Por ejemplo, algo equivalente a GiST para índices espaciales parece que sería difícil de mantener por la naturaleza de no poder conocer con precisión la ubicación de cada tupla del índice; y GIN para indexación de búsqueda de texto completo podría crecer muchísimo porque los árboles de postings comprimirían peor.
Además, en una situación donde se usan tablas organizadas por índice, no me imagino bien cómo se implementaría un equivalente de BRIN. BRIN permite excluir rápidamente grandes rangos de una tabla física de los resultados de una consulta cuando no contienen datos interesantes. Se podría particionar por rangos de clave primaria, pero la densidad de valores de la clave primaria puede variar mucho con el tiempo y según el rango de valores.
Me da curiosidad cómo planean implementar estos métodos de índice más complejos, pero muy útiles.
También está el problema que puede surgir cuando cambian las reglas de ordenamiento, es decir, la colación. Como el heap y VACUUM de Postgres no conocen el orden de clasificación, muchas veces se puede reparar el daño después de un cambio de colación borrando e insertando de nuevo las filas que quedaron en posiciones incorrectas, y dejando que VACUUM finalmente limpie las tuplas rotas.
En Oriole eso parece más difícil, porque no podría encontrar la tupla original que hay que eliminar mediante una búsqueda puntual, y para arreglar casos conocidos de corrupción de índices probablemente haría falta reconstruir el índice completo, lo que parece una carga de mantenimiento considerable.
En una funcionalidad similar a GIN, la compresión de listas de postings sigue siendo posible. Una opción viable es vincular el registro de deshacer a toda la lista de postings, no a un único elemento de esa lista.
Para BRIN, como se usan tablas organizadas por índice, no parece que podamos crear un análogo directo. Aun así, se pueden hacer cosas interesantes con las union keys en las páginas internas de la clave primaria.
El problema de las colaciones es real y serio. Antes de llegar a GA, habrá que fijar todos los índices que sean conscientes de la colación a una versión específica de colación de libicu.
La lógica del artículo también es convincente y los benchmarks parecen respaldar las afirmaciones de rendimiento, pero no se entiende bien la distinción entre el nuevo motor de almacenamiento propuesto, es decir, OrioleDB, y el propio PostgreSQL.
Más allá de la motivación comercial y de querer monetizar las innovaciones tratadas en el artículo, ¿hay alguna razón por la que esta mejora deba comercializarse como una nueva base de datos llamada OrioleDB, en vez de contribuirla upstream?
Pero los cambios de OrioleDB son demasiado grandes como para incorporarlos gradualmente. Por eso se está comparando OrioleDB con el motor actual de PostgreSQL, incluyendo no solo el heap, sino también varios subsistemas.
Para más detalles, se pueden ver en particular las diapositivas 9 a 11 de https://www.socallinuxexpo.org/sites/default/files/presentations/solving-postgres-wicked-problems.pdf
Si es una extensión de motor, me pregunto si tiene impacto al usarla junto con otras extensiones. Por ejemplo, timescaledb[0] funciona sobre tablas hijas.
Me pregunto qué efecto tendría hacerlo así:
create table xyz(...) using orioledb;select create_hypertable(xyz, ts);[0] https://github.com/timescale/timescaledb
¿OrioleDB tendría interés en prometer un formato on-disk estable para que no haga falta un procedimiento de actualización entre versiones mayores de Postgres? Parece una oportunidad para resolver ese problema.
Cosas como agregar nuevos tipos y sus funciones de soporte tienen que insertarse mediante algún procedimiento de actualización. También hay otros cambios de catálogo que modifican la disposición de columnas de las tablas del catálogo, y en esos casos también hace falta un procedimiento para actualizar los datos almacenados entre versiones.
Sin un procedimiento de actualización, no se podría cambiar el catálogo. Por eso solo las actualizaciones de versiones menores de PostgreSQL pueden hacerse reemplazando binarios, y revertirse de forma segura sin problemas.
Si las actualizaciones se limitaran solo a cambios en APIs internas, el planner y el ejecutor, el desarrollo quedaría seriamente restringido. Parece difícil que OrioleDB elimine la necesidad de este procedimiento de actualización.
La mayor parte del trabajo de actualización de un clúster proviene de reescribir las tablas del catálogo. Además, para la mayoría de las cargas de trabajo pg_upgrade es muy rápido, así que tampoco veo dónde estaría la gran ganancia.
Me incomoda la frase “reducción de 2.3 veces en la sobrecarga de CPU por transacción”. Postgres oscila entre 5% y 65% de uso de CPU, mientras que Oriole se mantiene siempre en 90%
La previsibilidad está bien, pero subir los tramos bajos en un 85% parece algo bastante preocupante, ¿no?
La buena noticia es que eso significa que, si se escala verticalmente con una CPU más potente, se puede obtener más rendimiento de Oriole. En cambio, Postgres no seguirá aumentando su rendimiento de la misma manera
Desde la perspectiva de un sistema operativo de escritorio, uno podría usar el tiempo ocioso del servidor Postgres para otra cosa, pero en servidores normalmente se hace una sola tarea y se quiere una máquina optimizada para esa tarea
Si se baja el TPS, la CPU también bajará proporcionalmente, y aquí parece que quieren mostrar hasta dónde puede llegar
Si limitas el sistema a 60% de CPU, los números totales pueden cambiar, pero si al mismo uso obtiene 1.8 veces más TPS, de todos modos es una victoria. Más que un truco de marketing, parecen números bastante buenos
Si una CPU de servidor cara cuesta X dólares por unidad y solo puedes usar el 60%, y en la práctica solo puedes usar eso, estás quemando 0.4X dólares por unidad
Si puedes llevar la carga de trabajo verticalmente hasta saturar el 90% de una máquina, en general es fácil aplicar QoS y técnicas de aislamiento para mantener una saturación más baja y un rendimiento proporcional. Lo contrario no se cumple. Si solo puedes usar el 60% de la saturación total de la máquina y tienes que escalar horizontalmente, para pasar del 90% necesitas rediseñar, y aquí justamente ocurrió ese rediseño
Dicen que, como resultado acumulado de las mejoras, OrioleDB ofrece 5 veces más TPS, 2.3 veces menos carga de CPU por transacción, 22 veces menos IOPS por transacción y nada de bloat en tablas e índices
Que la carga de CPU suba y baje no significa que Postgres esté “escalando”, sino que periódicamente se topa con un cuello de botella de rendimiento. Probablemente por la necesidad de ejecutar VACUUM, que es muy sensible al I/O
Por eso Postgres usa I/O para tareas de limpieza en vez de usarlo para procesar consultas, y el TPS y el uso de CPU caen bruscamente al mismo tiempo
En cambio, Oriole maneja un throughput mucho más alto de forma mucho más consistente. Si pisas el acelerador, ¿qué auto preferirías: uno que va estable a 100 mph, o uno que, aunque llevas el pedal hasta el fondo, oscila violentamente entre 40 y 70 mph?
En el artículo hay un enlace con un título bastante llamativo: “10 cosas que Richard Branson odia de PostgreSQL”. Resulta que quien escribió ese blog no era Richard, sino Rick Branson
Ah, no era esa persona