Postgresql中的large object
1、初识postgresql large object
一位同事在对使用pg_dump备份出来的文件(使用plain格式)进行恢复时,觉得速度非常慢,让我分析一下是什么原因。
我拿到他的.bak文件,文件有1个多G。为了方便分析,我用split工具把文件给拆成了多个小文件。在.bak文件中,我发现有大量这样的SQL:

通过查询文档,我知道lo表示large object。还别说,这是我第一次接触到postgresql 中的large object 。因为受oracle中LOB概念的影响,我想当然地以为large object 也是postgresql中的一种数据类型,就像ORACLE中的BLOB或CLOB。可是翻看了参考书籍与官方文档后,发现竟然没有对应的字段类型。网上有人说,postgresql中的large object,其对应的字段类型为oid。这样的说法让我真的好费解,只到最后,待我搞明白large object是怎么回事后,我意识到这样说的人大概知道一点,但应该没有理解透。
在查询官方文档的过程中,接触到两张与large object相关的系统表:


简单点说,pg_largeobject_metadata表是large object的元数据表,记录每个large object的OID(对象标识符)、属主、访问权限。Pg_largeobject表是具体存储large object的表,其存储方式为:将large ojbect以page(2K)为单位分成多个单元,第个单元在Pg_largeobject中表示为1条记录。
可是,我没有发现有任何说明,可以解释如何以large object作为字段值。
2、如何理解postgresql large object
在oracle中,假设我们定义了表的一个字段类型为BLOB,例如
Create table t (desc blob)
然后我们可以向该字段中插入记录: insert into t values ('0Xadfe4358942c')
最后,在我们查询时,直接select desc from t 就会返回:
0Xadfe4358942c
我错就错在默认了postgresql也是这么玩儿的。然而呢?
实际上,在postgresql中(至少截止到9.6版本)没有large object字段。Large object在postgresql中是作为一个个的对象存在的,可以使用特定方法以oid来引用它们。我们来举个现实中的例子来说明一下:假如把数据库表的每条记录比喻成1个人,LOB比喻成1只狗,那么oracle的处理方式为1个人有了1只狗,数据库中才有了这只狗。而关于这只狗怎么来的,是凭空变出来的、基因克隆的、还是自己用泥巴做的,oracle不管它。postgresql large object的管理方式则不同,数据库中首先得有1只狗或多只狗,然后这个人就可以办理领养手续,但领养手续只完成领养登记,表示这只狗属于你了,但狗你不能真正带回家。因为狗并没有真正带回来,所以多个人可以登记领养同一只狗,只需要将领养编号赋予同一只狗的ID即可。
3、Postgresql large object 的使用
上面只是一个比喻,那postgresql large object如何使用的呢?使用过程中的每一步操作又对应上文的什么内容呢?
我们就以.bak中的过程来做示例。
- 创建large object (数据库中有了一只狗,但现在这只狗还只是个概念,还不是实体)

执行该过程后,会在pg_largeobject_metadata中生成1条记录,oid字段值为1000001。而pg_largeobject表不会生成相应记录,因为现在的large object还是空的。

- 打开large object (找到这只狗,准备为它赋予生命)--执行这一步前需要开启事务: begin;

- 为large object 注入实际数据 (给狗赋予生命,让它变成一个实体)--执行这一步后需要提交事务:commit;

执行该过程后,会在pg_largeobject生成记录,记录的条数与large object的大小有关(每2K大小一条记录)

- 至此,狗已经有了,但它只是孤零零地在那时。现在需要有人对它办理领养登记
我们新建一张表:

然后往该表中插入一条记录,lo字段的值为1000001

好了,我们现在领养手续办理完了。可能有人要说,不对呀,这就完了?是的,这就完了。从这里可以看出,人知道它领养了哪只狗,但狗不知道谁领养了它。在计算机术语来说,就是引用者知道被引用者,但被引用者不知道引用者。
现在领也领养了,但有什么意义呢?就这么标识一下引用,是不是就可以通过test表的lo字段来查看large object的内容呢?我们来看一下:

很遗憾,看到的只是引用值,看不到large object的具体的值。实际上,在postgresql中,对large object 的使用需要使用专门的方法,不能直接使用引用字段的方式来查看、使用。
- 办理领养登记之后的使用
办理领养登记之后,可以的方法包括:
- lo_export(oid loid, text filename) --将大对象loid的数据导出到一个服务器文件filename中,返回导出长度(整型)。
- lo_unlink(oid loid) -- 删除一个地址为loid的大对象,返回整型 1-成功 -1 -失败。
- lo_open(oid loid, integer open_mode) -- 打开一个地址为loid的大对象,为读写做准备,open_mode为打开类型: inv_write(写,值为131072)、inv_read(读,值为262144)或者inv_write|inv_read (读写,值为393216)。返回文件句柄fd(整型),若fd为负数,失败。
- loread(integer fd, integer len) -- 读句柄fd当前位置开始的len大小的数据,返回数据内容(bytea类型)。
- lowrite(integer fd, bytea buf) -- 在句柄fd当前位置开始将二进制数据buf写入大对象中,返回所写的长度(整型)。
- lo_lseek(integer fd, integer offset, integer whence) -- 改变句柄fd当前的读写位置。whence是寻址方式,seek_set(值为0)从对象头开始,seek_cur(值为1)从当前读写位置开始,seek_end(值为2)从对象尾开始,offset是偏移尺寸。返回新的读写位置(整型),-1表示错误。
注:loread、lowrite会自动改变当前读写位置,所以若顺序读写,lo_lseek这个命令就没什么用。
- lo_tell(integer fd) -- 返回句柄fd的当前位置(整型)
- lo_truncate(integer fd, integer len) -- 截取句柄fd所打开的大对象长度为len大小。若len大于原来大对象的长度,会在大对象后缀一个'\0'字符。成功返回0,失败为负数。
- lo_close(integer fd) -- 关闭句柄fd, 成功返回0,失败为负数。
以上函数涉及到df句柄的,必须在一个transaction内完成,也就是说句柄fd只在一个事务内有效,事务结束它自动关闭。
- 使用案例
例如,我们可以lo_export方法将test表lo字段所对应的large object导出到本地。

查看test.f文件

通过查询1f 8b 08文件头所对应的文件格式,可知test.f文件为gz压缩文件。通过gunzip解压test.f文件

好,现在我们看清它的明文是什么了。这实际是一种矢量瓦片的明码格式。
Postgresql中的large object的更多相关文章
- Large Object Heap内存碎片在.NET 4.5中的改进
.NET 4.5已然到来,预览了解了下Large Object Heap在.NET 4.5中的效能改进.借此和大家来探讨下.本文不讨论Loder Heap,SOH(samll object heap) ...
- 关于LOH(Large Object Heap)及内存泄漏
关于LOH(Large Object Heap)的. .NET CLR中对于大于85000字节的内存既不像引用类型那样分配到普通堆上,也不像值类型那样分配到栈上,而是分配到了一个特殊的称为LOH的内部 ...
- BLOB (binary large object)
BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的容器. 在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型. BLOB是一个大文件,典型的BL ...
- 通过arcgis在PostgreSQL中创建企业级地理数据库
部署环境: Win7 64位旗舰版 软件版本: PostgreSQL-9.1.3-2-windows-x64 Postgis-pg91x64-setup-2.0.6-1 Arcgis 10.1 SP1 ...
- 【前端】js中new和Object.create()的区别
js中new和Object.create()的区别 var Parent = function (id) { this.id = id this.classname = 'Parent' } Pare ...
- buffer cache中,各个object对象占用的buffer blocks
buffer cache中,各个object对象占用的buffer blocks: COLUMN OBJECT_NAME FORMAT A40 COLUMN NUMBER_OF_BLOCKS FORM ...
- PostgreSQL 中日期类型转换与变量使用及相关问题
PostgreSQL中日期类型与字符串类型的转换方法 示例如下: postgres=# select current_date; date ------------ 2015-08-31 (1 row ...
- PostgreSQL 中定义自己需要的数据类型
PostgreSQL解决某系数据库中的tinyint数据类型问题,创建自己需要的数据类型如下: CREATE DOMAIN tinyint AS smallint CONSTRAINT tinyint ...
- 在PostgreSQL中使用oracle_fdw访问Oracle
本文讲述如何在PostgreSQL中使用oracle_fdw访问Oracle上的数据. 1. 安装oracle_fdw 可以参照:oracle_fdw in github 编译安装oracle_fdw ...
随机推荐
- Echo团队Alpha冲刺随笔 - 第十天
项目冲刺情况 进展 对Web端和小程序端进行各项功能的测试 问题 bug无穷无尽 心得 debug使人秃头,希望明天能挑好 今日会议内容 黄少勇 今日进展 测试小程序,对发现的bug进行处理 存在问题 ...
- 最小圆覆盖(洛谷 P1742 增量法)
题意:给定N个点,求最小圆覆盖的圆心喝半径.保留10位小数点. N<1e5: 思路:因为精度要求较高,而且N比较大,所以三分套三分的复杂度耶比较高,而且容易出错. 然是写下增量法吧. 伪代码加深 ...
- LeetCode 1135. Connecting Cities With Minimum Cost
原题链接在这里:https://leetcode.com/problems/connecting-cities-with-minimum-cost/ 题目: There are N cities nu ...
- PostgreSQL 一些比较好用的字符串函数
最近刚接触到PostgreSQL数据库,发现很多功能比较强大的内置函数,特此记录下来.示例下次再补. 1.concat 字符串连接函数 2.concat_ws concat_ws函数连接可自定义分隔符 ...
- terraform v0.12.0 发布了
v0.12.0 相比以前的有好多新的特性,包括语法,以及函数增强,昨天还在折腾的一个json解码的问题,直接使用 v0.12.0 就可以解决了,同时也包含了for 操作处理同时官方文档对于v0.12. ...
- CSP2019自闭记
为什么我之前没有写呢,是因为我总是考的太lj,于是就不想写了. 这次不管考没考好都要强迫自己写,因为这是第一次参加提高组+第一次参加CSP. 当然什么初赛/复赛试题/答案什么的是不会出现的. Day ...
- (10)Go结构体struct
结构体 Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个 ...
- cnetos6.5安装Varnish
安装依赖包: tar -zxvf docutils-0.13.1.tar.gz python setup.py install unzip pcre2-10.23.zip ./configure -- ...
- D3.js的v5版本入门教程(第五章)—— 选择、插入、删除元素
D3.js的v5版本入门教程(第五章) 1.选择元素 现在我们已经知道,d3.js中选择元素的函数有select()和selectAll(),下面来详细讲解一下 假设我们的<body>中有 ...
- Java-JUC(十四):SimpleDateFormat是线程不安全的
SimpleDateFormat是Java提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个SimpleDateFormat实例对日期进行解析.格式 ...