我需要在Postgres数据库中存储大文件(从几MB到1GB)。该数据库有多个模式。看起来Postgres有2个选项来存储大型对象:LOB和BYTEA。但是,我们似乎遇到了每个选项的问题。
LOB。这几乎是理想的,可以存储高达2GB并允许流式传输,这样我们在读取LOB时不会在PHP后端中达到内存限制。但是,所有blob都存储在pg_catalog中,并且不是模式的一部分。当您尝试将pg_dump与选项-n和-b一起使用以仅使用其blob转储一个模式时,这会导致一个大问题。它正确地转储模式数据,然后它包括数据库中的所有blob,而不仅仅是属于特定模式的blob。 有没有办法使用pg_dump或其他实用程序转储单个模式及其blob?
BYTEA。这些都是按模式正确存储的,因此pg_dump -n可以正常工作,但我似乎无法找到一种流式传输数据的方法。这意味着如果数据大于内存限制,则无法从PHP访问数据。
有没有其他方法可以在Postgres中存储大数据,允许流式传输并正确使用每个数据库的多个模式?
感谢。
答案 0 :(得分:3)
尽管使用bytea不支持流式/文件式API,但您可以使用它来仅获取部分内容,因此它支持“分块”。
您需要将bytea列的存储模式设置为“external”以禁用压缩,然后您可以在bytea列上使用substring
来仅获取其中的一部分。至少根据文档,这将DTRT并有效地访问数据库端的值的必要部分:
http://www.postgresql.org/docs/current/static/storage-toast.html
所以创建一个类似这样的模式:
create table media.entity(entity_id serial primary key, content bytea not null);
alter table media.entity alter column content set storage external;
然后从内容中获取8Kb:
select substring(content, 1, 8192) from media.entity where entity_id = 1;
select substring(content, 8193, 8192) from media.entity where entity_id = 1;
不幸的是,TOAST数据的提取似乎没有计入explain (buffers on)
计数,因此很难验证数据库是否正在执行文档所说的内容。
答案 1 :(得分:1)
实际存储大对象的 pg_catalog.pg_largeobject 系统表本质上是由 pageno 排序的每对象bytea块的列表,这是一个连续的块号从0到N。
Table "pg_catalog.pg_largeobject" Column | Type | Modifiers --------+---------+----------- loid | oid | not null pageno | integer | not null data | bytea | Indexes: "pg_largeobject_loid_pn_index" UNIQUE, btree (loid, pageno)
这些块的最大大小是2048字节(可以更改,但是以服务器重新编译为代价),对于几百兆字节的blob来说这是非常小的。
因此,在您的情况下,一个选项是在您自己的模式中复制类似的结构,可能使用更大的块,并通过迭代pageno
列表来实现类似于流的访问。无论如何,具有较小的列含量通常是更好的。例如,就客户端内存需求而言,pg_dump
在单行中很好地处理大型bytea内容并不明显。