【greenplum】greenplum pg_largeobject 大对象处理实践

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/you_xian/article/details/78847559

作者:lianghc

1. postgresql的大对象处理

     由于greenplum 的底层是postgresql ,所以gp的大对象存储的基本知识先看postgreql的大对象存储,先看看postgreql布道者德哥关于表的行较大的数据处理测试案例:
PostgreSQL large row|column performance tuning case :http://blog.163.com/digoal@126/blog/static/16387704020130931040444/
文中详细比对了表的行较大的数据处理三种方法
1. 既然大部分的开销是在索引上, 那么减少索引的更新是一种优化办法. 按列拆表, 创建视图. 通过创建视图触发器(用于插入,更新,删除操作).
2. 删除大字段上的索引,或者不使用全索引, 例如只索引前面32个字符. substr(col3,1,32).
3. 使用大对象存储.

     第三种方案是利用 large object(大对象) ,什么是大对象呢?
     官方文档的解释:http://www.postgres.cn/docs/9.3/largeobjects.html
     在 PostgreSQL 7.1 以前的版本里, 记录存储在数据页面里并且单个记录里的数据大小不能超过数据页面的大小。 因为数据页面大小是8192 字节(缺省,可以增加到 32768), 所以可存储数据值的上限是相当低的。为了存储更大的原子数值, PostgreSQL 提供了大对象接口。 这个接口给用户提供对存储在特殊大对象结构的用户数据的面向文件的接口。最初,PostgreSQL 4.2(PostgreSQL 的间接前身)支持三种大对象的标准实现: 作为 POSTGRES服务器外部的文件扩展, 作为由 POSTGRES 管理的外部文件, 以及作为存储在 POSTGRES 数据库里面的数据. 这样做容易导致用户的迷惑.结果是,我们只支持把大对象作为数据存储在 PostgreSQL 数据库里. 即使这样做令数据访问变得有些慢,但却保证了更严格的数据完整性. 由于历史原因,这种存储机制被称为转置大对象.。 自 PostgreSQL 7.1 开始,所由大对象都保留在一个叫pg_largeobject的系统表里.
     了解了基本概念后我们来做个实践

 2.greenplum 大对象存储实践

2.1 创建 一张测试表,其中big_col存储大字段oid.

create table  largeobject_test(
   id bigint    
   big_col oid
)  ;

largeobject_test .big_col 的类型是oid类型,这个字段将存储大字段.

2.2 参照德哥的大对象处理脚本

-- 创建写大对象的函数, 打开文件FLAG参考src/include/libpq/libpq-fs.h : 
create or replace function write_lo (i_bytea bytea) returns oid as $$
declare
  oid_new_lo oid;
  fd_new_lo int;
begin
  select lo_creat(-1) into oid_new_lo;
  select lo_open(oid_new_lo, 131072) into fd_new_lo;
  perform lowrite(fd_new_lo, i_bytea);
  perform lo_close(fd_new_lo);
  return oid_new_lo;
end;
$$ language plpgsql;


-- 创建读大对象的函数, 打开文件FLAG参考src/include/libpq/libpq-fs.h : 
create or replace function read_lo (i_lo_oid oid, i_size int4) returns bytea as $$
declare
  result bytea;
  fd_lo int;
begin
  select lo_open(i_lo_oid, 262144) into fd_lo;
  select loread(fd_lo, i_size) into result;
  perform lo_close(fd_lo);
  return result;
end;
$$ language plpgsql;

--   解释下 131071 和 262144:
--   lo_open(lobjId oid, mode integer) returns integer
--  The mode parameter to lo_open uses two constants:
--   INV_READ  = 0x20000
--   INV_WRITE = 0x40000
SELECT CAST( x'20000' AS integer);
  int4 
--------
 131072(1 row)
SELECT CAST( x'40000' AS integer);
  int4 
--------
262144 (1 row)
关于大对象存储的函数说明和编程实践可以参考:
http://blog.chinaunix.net/uid-63508-id-112665.html
http://blog.chinaunix.net/uid-63508-id-112666.html

2.3 插入数据

insert into largeobject_test (id,big_col)
select    generate_series(1,100000000),  write_lo(decode(repeat(md5(clock_timestamp()::text), 3000), 'base64'))


2.4 查看插入结果

select * from largeobject_test;
可以看到 big_col 行存的都是oid,那么实际的数据存在哪里呢?
是存在 pg_catalog.pg_largeobject

关于 pg_catalog.pg_largeobject 可以查看官方文档:https://www.postgresql.org/docs/9.4/static/catalog-pg-largeobject.html
这张表的第三个字段就是我们存储的内容,第一项是oid, 第二个字段是页数,当存储的内容一夜存不下时会分多页存储。将数据存储在pg_largeobject 中的好处是,loid 是指向文件的指针,可以很快定位到文件,原来的largeobject_test 再磁盘上占用很小,处理起来就很快了。

2.5 清除大对象

如果不需要的大对象的清除方法 : 
select lo_unlink(big_col) from test;
到这里greenplum的实践就结束了。一切顺利!!!

3.greenplum 大对象的问题

1. pg_largeobject不断增长如何清理?

     虽然lo_unlink 将oid 和大对象存储内容解绑了,但是磁盘并没释放,目前除了真空回收(VACUUM FULL ANALYZE pg_largeobject ),没有其他办法
关于释放pg_largeobject 表大小 请参考:
http://www.postgresql-archive.org/How-to-clean-truncate-VACUUM-FULL-pg-largeobject-without-much-downtime-td5848700.html
https://dba.stackexchange.com/questions/112637/can-i-do-vacuum-full-to-pg-largeobject-table/112646

2.greenplum 如何删除大对象?

在greenplum中实践后, 发现可以成功生成和读取大对象,但是greenplum是分布式的,将分布式数据转存大对象后pg_largeobject为空, 但是空间占用却不是0
select * from  pg_catalog.pg_largeobject;

select pg_size_pretty(pg_total_relation_size('pg_largeobject'));


此时想通过lo_unlink() 清除大对象是行不通的,应为拿不到oid。但是数据可以通过lorea() 读取到,那么oid究竟存在哪里呢?这个问题困扰了我一下午,后来我登陆到子节点查看子节点的pg_largeobject

#使用utility方式
PGOPTIONS="-c gp_session_role=utility" psql -h -d dbname hostname -p port
发现子节点的pg_largeobject 是有数据的。也就是说gp并不是在主节点生成全局唯一的oid 然后分配给子节点使用。回想之前我们在主节点测试largeobject_test 可以成功,通过查看segment_id 可以验证在主节点生成的大对象仅存储在主节点


我们借助gp_dist_random() 来获取每个节点上的pg_largeobject.loid,使用下面的语句就可以清楚gp的大对象了。
SELECT   lo_unlink(loid) FROM gp_dist_random('pg_largeobject') WHERE pageno=0
因为pg_largeobject  中loid和pageno  是一对多的关系,lo_unlink() 直解绑一次oid就可以了,多以加上WHERE pageno=0

以上为开发笔记,稍作整理,用以备忘和分享。


猜你喜欢

转载自blog.csdn.net/you_xian/article/details/78847559