sql >> Base de Datos >  >> RDS >> PostgreSQL

Esos malditos objetos grandes

Introducción

PostgreSQL brinda a los desarrolladores la oportunidad de elegir entre dos posibles instalaciones de almacenamiento para grandes datos binarios:Bytea y LargeObjects.

Los objetos grandes existen desde hace mucho tiempo y PostgreSQL tiene una forma inteligente de almacenar datos binarios de gran tamaño. Lo hace dividiéndolo en trozos de LOBLKSIZE (una cuarta parte de BLCKSZ). De esa forma, las tuplas de pg_largeobject no se derraman sobre la mesa de tostadas.

Por otro lado bytea almacena los datos binarios directamente en la tupla, lo que puede generar un rendimiento deficiente según el aspecto de su esquema.

Esto suena muy bien si tiene una interfaz inteligente para manejar la manipulación de estos archivos binarios, especialmente si la actualización modifica solo una pequeña parte de todo el archivo binario.

Pero normalmente no nos molestamos en escribir código que se aproveche de esto y, en su lugar, escribimos de nuevo todos los datos binarios.

Una de las cosas que creo que hace que la gente adopte objetos grandes son las funciones disponibles para importar y exportar archivos directamente desde el servidor de la base de datos a su sistema de archivos. Hay una desventaja en esto:si la aplicación está en un servidor diferente, necesitará más código para mover el archivo a la ubicación donde se necesita.

Un problema que podría enfrentar

Los últimos días tuve que examinar una base de datos utilizada para almacenar información de sesiones de usuario de un sistema Java CAS. Descubrí que había casi 100 millones de objetos grandes en la base de datos, no muy grandes.

Revisé las tablas de usuarios revisando los campos que tenían un oid y luego cotejo los valores de esos campos con los pg_largeobject_metadata mesa. Encontré que el 96% de esos objetos grandes eran huérfanos. Esos son objetos grandes a los que no hizo referencia ninguna tupla de las tablas de usuario.

Investigaciones posteriores concluyeron que Hibernate no se ocupó de purgar los objetos grandes que creaba al eliminar o actualizar tuplas con campos oid. Por lo tanto, estaba generando una gran cantidad de hinchazón que no podía limpiarse con la aspiradora, sino que tenía que eliminarse manualmente de la tabla pg_largeobjects.

En el caso particular de la base de datos CAS, esta consulta sirvió para identificar los objetos grandes aún en uso:

SELECT unnest(array[expiration_policy,
                    authentication,
                    services_granted_access_to])
       FROM public.ticketgrantingticket
UNION
SELECT unnest(array[expiration_policy, 
                    service])
       FROM public.serviceticket

La consulta se puede utilizar para excluir de la lista de objetos grandes cuáles eliminar. Algo como esto:

SELECT lo_unlink(pg_largeobject_metadata.oid)
       FROM pg_largeobject_metadata
       WHERE pg_largeobject_metadata.oid NOT IN (
             SELECT unnest(array[expiration_policy,
                                 authentication,
                                 services_granted_access_to])
             FROM public.ticketgrantingticket
             UNION
             SELECT unnest(array[expiration_policy, 
                                 service])
             FROM public.serviceticket
)

Conclusión

Los objetos grandes tienen sus problemas, al igual que otros tipos de datos (especialmente cuando se usan tipos para almacenar datos binarios grandes). Depende de los desarrolladores y administradores de bases de datos aprovechar las ventajas y mitigar las desventajas.

Dimos una posible consulta para realizar la limpieza, pero también hay una buena extensión que limpia los objetos grandes huérfanos con disparadores:Administrador de objetos grandes

Algunas personas pueden preferir ejecutar una consulta de purga durante las horas de tranquilidad en lugar de ejecutar un disparador en cada ACTUALIZACIÓN y ELIMINAR . En sistemas con muy, muy bajo UPDATE y/o ELIMINAR tasa, un disparador sobre cada mesa que tiene un oid campo, parece una solución más elegante. Y cualquier pérdida de rendimiento por tener que ejecutar la función de activación sería superflua.

En cualquier caso, los objetos grandes todavía tienen grandes admiradores, muy probablemente debido a las funciones internas provistas para importar y exportar los datos binarios directamente al sistema de archivos local. Con bytea, normalmente usaría más memoria en el nivel de la aplicación. Es un procedimiento muy común leer el campo binario completamente en una variable y luego procesarlo.

Podría escribir algo sobre el uso de bytea que usé en uno de mis desarrollos anteriores en una futura publicación de blog.