PG中的blob cleanup

简介: PG中的blob cleanup

PG中的blob cleanup


PG提供了一个很好的BLOB接口,得到了广泛应用。然而最近我们遇到了各种客户遇到的问题,有必要对PG如何处理blob进行一次思考,尤其是BLOB清理。


PG的BLOB接口


PG中可以使用各种方式存储二进制数据,最简单的方式是定义一个“bytea”(=byte array)数据类型。也就是定义一个bytea列:

test=# CREATE TABLE t_image (id int, name text, image bytea);

CREATE TABLE

test=# \d t_image

Table "public.t_image"

Column | Type    | Collation | Nullable | Default

-------+---------+-----------+----------+---------

id     | integer |           |          |

name   | text    |           |          |

image  | bytea   |           |          |

如你所见,这是一个普通列,可以像普通列一样使用。唯一值得一提的是必须在SQL级别使用的编码。PG使用一个变量来配置此行为:

test=# SHOW bytea_output;

bytea_output

--------------

hex

(1 row)

bytea_output有两个值,hex:表示PG以16进制格式发送数据,escape:表示以8进制发送。除了每个字段最大大小限制1GB外,应用程序在这里不需要担心太多。

PG还有第二种接口处理二进制数据:BLOB接口。例子:

test=# SELECT lo_import('/etc/hosts');

lo_import

-----------

80343

(1 row)

这个例子中,“/etc/hosts”内容存储到了数据库。PG有这个数据的副本而不是到文件系统的链接。需要注意,数据库将返回新条目的OID(存储到了系统表),为了跟踪这些OID,一些开发人员执行以下操作:

test=# CREATE TABLE t_file (

id int,

name text,

object_id oid

);

CREATE TABLE

test=# INSERT INTO t_file

VALUES (1, 'some_name', lo_import('/etc/hosts'))

RETURNING *;

id  | name          | object_id

----+---------------+-----------

1   | some_name     | 80350

(1 row)

1

INSERT 0 1

除了做下面动作,其他方面都还好:

test=# DELETE FROM t_file WHERE id = 1;

DELETE 1

问题是对象的OID被遗忘了,虽然删除了t_file里的一个记录,但是二进制数据仍然存在。PG通过pg_largeobject系统表存储二进制数据。所有的lo_functions为了处理下面事情会和该系统表交互:

test=# \x

Expanded display is on.

test=# SELECT * FROM pg_largeobject WHERE loid = 80350;

-[ RECORD 1 ]------------------------------------------

loid   | 80350

pageno | 0

data   | ##\012# Host Database\012#\012# localhost ...

问题是什么?原因很简单,数据库会膨胀,因此删除BLOB条目正确的方法是:

test=# \x

Expanded display is off.

test=#

test=# SELECT lo_unlink(80350);

lo_unlink

-----------

1

(1 row)

 

test=# SELECT * FROM pg_largeobject WHERE loid = 80350;

loid  | pageno | data

------+--------+------

(0 rows)

如果忘记删除object,我们经常会遇到这样的事情:你将遭受膨胀带来的痛苦。如果使用BLOB接口,这是一个主要问题。


vacuumlo:清理死大对象


然而,一旦积累了成千上万的死blob,如何解决这个问题呢?可以通过vacuumlo工具:

test=# SELECT lo_import('/etc/hosts');

lo_import

-----------

80351

(1 row)

iMac:~ hs$ vacuumlo -h localhost -v test

Connected to database "test"

Checking object_id in public.t_file

Successfully removed 2 large objects from database "test".

可以看到,死大对象被清理了。


额外函数


PG提供了不止lo_import和lo_unlink函数:

test=# \df lo_*

                               List of functions

  Schema   |   Name        | Result data type | Argument data types       | Type

------------+---------------+------------------+---------------------------+------

pg_catalog | lo_close      | integer          | integer                   | func

pg_catalog | lo_creat      | oid              | integer                   | func

pg_catalog | lo_create     | oid              | oid                       | func

pg_catalog | lo_export     | integer          | oid, text                 | func

pg_catalog | lo_from_bytea | oid              | oid, bytea                | func

pg_catalog | lo_get        | bytea            | oid                       | func

pg_catalog | lo_get        | bytea            | oid, bigint, integer      | func

pg_catalog | lo_import     | oid              | text                      | func

pg_catalog | lo_import     | oid              | text, oid                 | func

pg_catalog | lo_lseek      | integer          | integer, integer, integer | func

pg_catalog | lo_lseek64    | bigint           | integer, bigint, integer  | func

pg_catalog | lo_open       | integer          | oid, integer              | func

pg_catalog | lo_put        | void             | oid, bigint, bytea        | func

pg_catalog | lo_tell       | integer          | integer                   | func

pg_catalog | lo_tell64     | bigint           | integer                   | func

pg_catalog | lo_truncate   | integer          | integer, integer          | func

pg_catalog | lo_truncate64 | integer          | integer, bigint           | func

pg_catalog | lo_unlink     | integer          | oid                       | func

(18 rows)

由于历史原因,还有2个函数没有遵循命名约定:lowrite和loread:

pg_catalog | loread  | bytea   | integer, integer | func

pg_catalog | lowrite | integer | integer, bytea   | func


最后


PG的BLOB接口非常有用。美妙之处在于它完全是事务性的,因此二进制内容和元数据sync后不会丢失。


原文


https://www.cybertec-postgresql.com/en/blob-cleanup-in-postgresql/

目录
相关文章
|
SQL 消息中间件 关系型数据库
iceberg实践
iceberg实践
|
4月前
|
关系型数据库 分布式数据库 数据库
|
物联网 调度 异构计算
使用GaLore在本地GPU进行高效的LLM调优
GaLore是一种新的优化策略,它通过梯度低秩投影减少VRAM需求,使得大型语言模型(如70亿参数的模型)能在消费级GPU上进行微调,而不减少参数数量。与LoRA相比,GaLore内存效率更高,且性能相当或更优。它在反向传播期间逐层更新参数,降低了计算负荷。虽然GaLore训练时间较长,但它为个人爱好者提供了在有限资源下训练大模型的可能性。相关代码示例和性能对比显示了其优势。
462 0
|
弹性计算 运维 Serverless
揭秘ADB Serverless架构:如何实现无服务器计算的神奇魔法?
【8月更文挑战第27天】随着云计算的演进,Serverless架构作为一种新兴计算模式崭露头角。阿里云函数计算(简称ADB)提供了一种无需管理服务器即可运行代码的服务。本文通过案例探讨ADB Serverless模式的实现方式。首先介绍Serverless架构的基础概念,即事件驱动模型,使开发者仅需关注业务逻辑,减轻了基础设施构建与维护的负担。ADB采用分布式、无状态的设计,确保高可用性和弹性伸缩能力。以一个简单的Web应用为例,展示了如何利用ADB快速实现根据用户输入返回问候语的功能,突显出其简化开发流程、提高性能与可扩展性的优势。
191 0
|
缓存 Linux Shell
Docker资源(CPU/内存/磁盘IO/GPU)限制与分配指南
什么是cgroup? cgroups其名称源自控制组群(control groups)的简写,是Linux内核的一个功能,用来限制、控制与分离一个进程组(如CPU、内存、磁盘输入输出等)。 什么是Docker资源限制?
|
C语言 C++
高质量C++ C编程指南及编码规范.pdf
高质量C++ C编程指南及编码规范.pdf
920 0
|
运维 监控 数据可视化
一个会“说话”的软件项目管理工具
“软件开发是一个需要高智力、频繁沟通和密切协作的过程。”
|
存储 分布式计算 Cloud Native
首次揭秘云原生Hologres存储引擎
本文将会首次对外公开介绍Hologres的存储引擎,深度剖析其实现原理和核心技术优势。
29349 2
首次揭秘云原生Hologres存储引擎