阿里云RDS PostgreSQL OSS 外部表实践 - (dblink异步调用封装并行) 从OSS并行导入数据

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

PostgreSQL , oss对象存储 , 阿里云RDS PG , 并行写 , dblink , 异步调用 , 异步任务监控 , OSS外部表 , 数据传输


背景

《阿里云RDS PostgreSQL OSS 外部表实践 - (dblink异步调用封装并行) 数据并行导出到OSS》

本文为从OSS并行导入数据到数据库中。

请先阅读:

RDS PG OSS 外部表文档1

RDS PG OSS 外部表文档2

原文

https://www.atatech.org/articles/98990

一.准备工作

首先,创建我们要用到的插件。

create extension dblink;  
create extension oss_fdw;  

二.创建异步存储过程

异步数据装载的准备工作,获取oss文件列表

CREATE OR REPLACE FUNCTION rds_oss_fdw_load_data_prepare(t_from text, t_to text)  
  RETURNS bool AS  
$BODY$  
DECLARE  
    t_exist  int;  
    curs1 refcursor;  
    r   record;  
    filepath text;  
    fileindex int8;  
    s1 text;  
    s2 text;  
    s3 text;  
    c int = 0;  
    s4 text;  
    s5 text;  
    ss4 text;  
    ss5 text;  
    sql text;  
BEGIN  
    create table if not exists oss_fdw_load_status(id BIGSERIAL primary key, filename text, size int8, rows int8 default 0, status int default 0);  
  
    select count(*) into t_exist from oss_fdw_load_status;  
  
    if t_exist != 0 then  
        RAISE NOTICE 'oss_fdw_load_status not empty';  
        return false;  
    end if;  
  
    -- 通过 oss_fdw_list_file 函数,把外部表 t_from 匹配的 OSS 中的文件列到表中  
    insert into oss_fdw_load_status (filename, size) select name,size from oss_fdw_list_file(t_from) order by size desc;  
  
    select count(*) into t_exist from oss_fdw_load_status;  
    if t_exist = 0 then  
        RAISE NOTICE 'oss_fdw_load_status empty,not task found';  
        return false;  
    end if;  
  
    return true;  
END;  
$BODY$  
    LANGUAGE plpgsql;  

数据装载的工作函数

CREATE OR REPLACE FUNCTION rds_oss_fdw_load_data_execute(t_from text, t_to text, num_work int, pass text)  
  RETURNS bool AS  
$BODY$  
DECLARE  
    t_exist  int;  
    curs1 refcursor;  
    r   record;  
    filepath text;  
    fileindex int8;  
    s1 text;  
    s2 text;  
    s3 text;  
    c int = 0;  
    s4 text;  
    s5 text;  
    ss4 text;  
    ss5 text;  
    sql text;  
    db text;  
    user text;  
BEGIN  
    select count(*) into t_exist from oss_fdw_load_status;  
    if t_exist = 0 then  
        RAISE NOTICE 'oss_fdw_load_status empty';  
        return false;  
    end if;  
  
    s4 = 'oss_loader';  
    s5 = 'idle';  
    ss4 = '''' || s4 ||'''';  
    ss5 = '''' || s5 ||'''';  
    sql = 'select count(*) from pg_stat_activity where application_name = ' || ss4 || ' and state != ' || ss5;  
  
    select current_database() into db;  
    select current_user into user;  
  
    -- 通过游标,不断获取单个任务  
    OPEN curs1 FOR SELECT id, filename FROM oss_fdw_load_status order by id;  
    loop  
        fetch curs1 into r;  
        if not found then  
            exit;  
        end if;  
        fileindex = r.id;  
        filepath = r.filename;  
  
        s1 = '''' || t_from ||'''';  
        s2 = '''' || t_to ||'''';  
        s3 = '''' || filepath ||'''';  
  
        LOOP  
            -- 查看当前正在工作的任务数,过达到并发数就在这里等待  
            select a into c from dblink('dbname='||db ||' user='||user || ' password='||pass ,sql)as t(a int);  
            IF c < num_work THEN  
                EXIT;  
            END IF;  
            RAISE NOTICE 'current runing % loader', c;  
            perform pg_sleep(1);  
        END LOOP;  
  
        -- 通过 DBLINK 创建异步任务  
        perform dis_conn('oss_loader_'||fileindex);  
        perform dblink_connect('oss_loader_'||fileindex, 'dbname='||db ||' user='||user || ' application_name=oss_loader' || ' password='||pass);  
        perform dblink_send_query('oss_loader_'||fileindex, format('  
            begin;  
            select rds_oss_fdw_load_single_file(%s,%s,%s,%s);  
            end;'  
            , fileindex, s1, s2, s3)  
        );  
        RAISE NOTICE 'runing loader task % filename %',fileindex, filepath;  
    end loop;  
    close curs1;  
  
    -- 任务分配完成,等待所有任务完成  
    LOOP  
        select a into c from dblink('dbname='||db ||' user='||user || ' password='||pass ,sql)as t(a int);  
        IF c = 0 THEN  
            EXIT;  
        END IF;  
        RAISE NOTICE 'current runing % loader', c;  
        perform pg_sleep(1);  
    END LOOP;  
  
    return true;  
END;  
$BODY$  
    LANGUAGE plpgsql;  

单个文件的数据装载函数(设置不同的会话参数oss_fdw.rds_read_one_file,可以读取不同的OSS文件,用完重置)

CREATE OR REPLACE FUNCTION rds_oss_fdw_load_single_file(taskid int8, t_from text, t_to text, filepath text)  
  RETURNS void AS  
$BODY$  
DECLARE  
    rowscount int8 = 0;  
    current text;  
    sql text;  
BEGIN  
    -- 配置 GUC 参数,指定要导入的 OSS 上的文件  
    perform set_config('oss_fdw.rds_read_one_file',filepath,true);  
    select current_setting('oss_fdw.rds_read_one_file') into current;  
    RAISE NOTICE 'begin load %', current;  
  
    -- 通过动态 SQL 导入数据  
    EXECUTE 'insert into '|| t_to || ' select * from ' || t_from;  
    GET DIAGNOSTICS rowscount = ROW_COUNT;  
  
    -- 导入完成后,把结果保存到状态表中  
    RAISE NOTICE 'end load id % % to % % rows', taskid, filepath, t_to, rowscount;  
    update oss_fdw_load_status set rows = rowscount,status = 1 where id = taskid;  
    return;  
  
EXCEPTION  
    when others then  
    RAISE 'run rds_oss_fdw_load_single_file with error';  
END;  
$BODY$  
    LANGUAGE plpgsql;  

关闭连接不报错函数

create or replace function dis_conn(name) returns void as $$    
declare    
begin    
  perform dblink_disconnect($1);    
  return;    
exception when others then    
  return;    
end;    
$$ language plpgsql strict;    

三.使用函数装载数据

1、 创建本地表(目标表)结构

2、 将包含数据的文件写入OSS

3、 在RDS PG中创建OSS外部表

4、 准备需要并行导入的列表

select rds_oss_fdw_load_data_prepare('oss_table','lineitem');  

执行后,会看到表 oss_fdw_load_status 中,保存了准备导入的所有文件列表,用户可以做适当的删减定制。

4、 数据装载

 select rds_oss_fdw_load_data_execute('oss_table','lineitem',10,'mypassword');  

函数 rds_oss_fdw_load_data_execute 会等待数据导入的完成才返回。

5、 查询状态
期间,我们可以通过下列 SQL 查看正在工作的异步会话状态

 select application_name, state, pid,query, now() - xact_start as xact  from pg_stat_activity where state != 'idle' and application_name='oss_loader' order by xact desc;  

6、 管理状态

同时,我们也可以随时中断数据导入工作

select pg_terminate_backend(pid),application_name, state ,query from pg_stat_activity where state != 'idle' and pid != pg_backend_pid() and application_name='oss_loader';  

7、 查看进度

我们也很容易看到整个数据装载的进度(单位 MB)

select  
(  
select sum(size)/1024/1024 as complete from oss_fdw_load_status where status = 1  
)a,  
(  
select sum(size)/1024/1024 as full from oss_fdw_load_status  
)b;  

8、 性能

使用 TPCC 100GB的数据进行装载测试,耗时 10 分钟,平均 170MB/S

select rds_oss_fdw_load_data_prepare('t_oss2','lineitem');  
  
select rds_oss_fdw_load_data_execute('t_oss2','lineitem',10,'123456Zwj');  
  
select sum(size)/1024/1024 from oss_fdw_load_status;  
      ?column?        
--------------------  
 22561.919849395752  
(1 row)  
  
select pg_size_pretty(pg_relation_size(oid)) from pg_class where relname = 'lineitem';  
 pg_size_pretty   
----------------  
 101 GB  
(1 row)  

性能极限扩展

为了提高本地加载速度,用户可以这么做:

1、目标表选择UNLOGGED TABLE,注意如果选择unlogged table,那么数据库崩溃后unlogged table的数据会被清除,并且请记住备库看不到unlogged table的数据。

除非你的数据是定期全量覆盖的,否则不建议用unlogged table来加速。

create unlogged table xxx (xx xx);  

2、选择多个目标表

由于单个目标表,在INDEX LOCK,在EXTEND BLOCK方面都有一定的局限性,为了达到极限,可以使用多个目标表。例如每一批OSS文件对应一个本地表分区。

-- 并行  
insert into tbl1 select * from oss_tbl1;  
insert into tbl2 select * from oss_tbl2;  
.....  
insert into tblx select * from oss_tblx;  

3、导入前关闭目标表的表级autovacuum

autovacuum会影响导入性能,因为它要消耗一定的IO。

alter table tbl_dict set (autovacuum_enabled =off);  
  
alter table tbl_dict set (toast.autovacuum_enabled =off);  

4、导入后再开启目标表的autovacuum,收集统计信息

alter table tbl_dict set (autovacuum_enabled =on);  
  
alter table tbl_dict set (toast.autovacuum_enabled =on);  

5、后创建索引(可以并行)

索引可以同时创建(单个表的多个索引可以同时创建,不会相互锁等待。多个表的多个索引也可以同时创建),如果创建索引过程中不需要执行DML,建议不要开启concurrently选项,否则建议开启。

同时创建,可以提高资源利用率,达到快速完成数据导入加索引创建的目标。

云端相关产品

阿里云 RDS PostgreSQL

阿里云 HybridDB for PostgreSQL

RDS PG OSS 外部表文档1

RDS PG OSS 外部表文档2

HDB PG OSS 外部表文档

《阿里云RDS PostgreSQL OSS 外部表实践 - (dblink异步调用封装并行) 数据并行导出到OSS》

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
存储 安全 对象存储
手把手教你搭建阿里云图床(PicGo+Typora+阿里云OSS),新手小白一看就会
本文详细介绍了怎样帮助新手小白从注册,购买阿里云OSS,到一步一步配置OSS做为图床,和PicGo、Typora软件连接,配置好关联之后,在使用Typora写文章时,如果需要插入图片,只需要将图片复制粘贴到Typora的编辑区域,就会自动通过PicGo上传到指定图床,自动复制外网能访问的URL并展示,简直不要太方便,极大的解决了编辑文章时复制处理图片链接的痛点。
250 2
手把手教你搭建阿里云图床(PicGo+Typora+阿里云OSS),新手小白一看就会
|
2月前
|
弹性计算 前端开发 小程序
微信小程序上传文件至阿里云OSS直传(java后端签名+前端直传)
当前的通用文件上传方式是通过前端上传到服务器,再由服务器转存至对象存储。这种方式在处理小文件时效率尚可,但大文件上传因受限于服务器带宽,速度较慢。例如,一个100MB的文件在5Mbps带宽的阿里云ECS上上传至服务器需160秒。为解决此问题,可以采用后端签名的方式,使微信小程序直接上传文件到阿里云OSS,绕过服务器中转。具体操作包括在JAVA后端引入相关依赖,生成签名,并在微信小程序前端使用这个签名进行文件上传,注意设置正确的请求头和formData参数。这样能提高大文件上传的速度。
|
10天前
|
分布式计算 大数据 MaxCompute
MaxCompute产品使用合集之使用pyodps读取OSS(阿里云对象存储)中的文件的步骤是什么
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
2月前
|
弹性计算 网络协议 关系型数据库
ECS域名问题之国内实例能不能导入阿里云新加坡的ECS和RDS如何解决
ECS(Elastic Compute Service,弹性计算服务)是云计算服务提供商提供的一种基础云服务,允许用户在云端获取和配置虚拟服务器。以下是ECS服务使用中的一些常见问题及其解答的合集:
|
2月前
|
机器学习/深度学习 存储 分布式计算
机器学习PAI常见问题之DLC的数据写入到另外一个阿里云主账号的OSS中如何解决
PAI(平台为智能,Platform for Artificial Intelligence)是阿里云提供的一个全面的人工智能开发平台,旨在为开发者提供机器学习、深度学习等人工智能技术的模型训练、优化和部署服务。以下是PAI平台使用中的一些常见问题及其答案汇总,帮助用户解决在使用过程中遇到的问题。
|
10天前
|
运维 监控 关系型数据库
Serverless 应用引擎产品使用之在阿里云函数计算(FC)中,要访问另一个账号的rds配置rds的白名单如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
28 0
|
15天前
|
网络协议 对象存储
阿里云oss配置自有域名
阿里云oss配置自有域名
22 1
|
17天前
|
存储 JSON 前端开发
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
15 0
|
17天前
|
存储 开发工具 对象存储
Javaweb之SpringBootWeb案例之阿里云OSS服务入门的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务入门的详细解析
17 0
|
17天前
|
存储 文字识别 安全
Javaweb之SpringBootWeb案例之阿里云OSS服务的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务的详细解析
19 0

相关产品

  • 云数据库 RDS MySQL 版
  • 云原生数据库 PolarDB