PostgreSQL和PHP:在多个模式中存储大文件:BLOB或BYTEA

时间:2012-10-10 10:23:09

标签: php postgresql pdo

我需要在Postgres数据库中存储大文件(从几MB到1GB)。该数据库有多个模式。看起来Postgres有2个选项来存储大型对象:LOB和BYTEA。但是,我们似乎遇到了每个选项的问题。

  1. LOB。这几乎是理想的,可以存储高达2GB并允许流式传输,这样我们在读取LOB时不会在PHP后端中达到内存限制。但是,所有blob都存储在pg_catalog中,并且不是模式的一部分。当您尝试将pg_dump与选项-n和-b一起使用以仅使用其blob转储一个模式时,这会导致一个大问题。它正确地转储模式数据,然后它包括数据库中的所有blob,而不仅仅是属于特定模式的blob。 有没有办法使用pg_dump或其他实用程序转储单个模式及其blob?

  2. BYTEA。这些都是按模式正确存储的,因此pg_dump -n可以正常工作,但我似乎无法找到一种流式传输数据的方法。这意味着如果数据大于内存限制,则无法从PHP访问数据。

  3. 有没有其他方法可以在Postgres中存储大数据,允许流式传输并正确使用每个数据库的多个模式?

    感谢。

2 个答案:

答案 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内容并不明显。