Aurora Postgre SQL호환에서 Oracle UTL_FILE 기능 설정 - 권장 가이드

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Aurora Postgre SQL호환에서 Oracle UTL_FILE 기능 설정

작성자: Rakesh Raghav(AWS) 및 anuradha chintha(AWS)

요약

Oracle에서 Amazon Web Services(AWS) 클라우드의 Amazon Aurora Postgre SQL호환 에디션으로 마이그레이션하는 과정에서 여러 가지 문제가 발생할 수 있습니다. 예를 들어, Oracle UTL_FILE 유틸리티를 사용하는 코드를 마이그레이션하는 것은 항상 어려운 일입니다. Oracle PL/에서SQL UTL_FILE 패키지는 기본 운영 체제와 함께 읽기 및 쓰기와 같은 파일 작업에 사용됩니다. 이 UTL_FILE 유틸리티는 서버 및 클라이언트 기기 시스템 모두에서 작동합니다. 

Amazon Aurora PostgreSQL-Compatible은 관리형 데이터베이스 제품입니다. 이 때문에 데이터베이스 서버의 파일에 액세스할 수 없습니다. 이 패턴은 Amazon Simple Storage Service(Amazon S3)와 Amazon Aurora Postgre SQL호환을 통합하여 UTL_FILE 기능 하위 집합을 달성하는 방법을 안내합니다. 이 통합을 사용하면 타사 추출, 변환 및 로드(ETL) 도구 또는 서비스를 사용하지 않고도 파일을 생성하고 사용할 수 있습니다.

선택적으로 Amazon CloudWatch 모니터링 및 Amazon SNS 알림을 설정할 수 있습니다.

프로덕션 환경에 구현하기 전에 이 솔루션을 철저하게 테스트하는 것이 좋습니다.

사전 조건 및 제한 사항

사전 조건 

  • 활성 AWS 계정

  • AWS 데이터베이스 마이그레이션 서비스(AWS DMS) 전문 지식

  • PL/pgSQL 코딩에 대한 전문성

  • Amazon Aurora Postgre SQL호환 클러스터

  • S3 버킷

제한 사항

이 패턴은 Oracle UTL_FILE 유틸리티를 대체하는 기능을 제공하지 않습니다. 하지만 데이터베이스 현대화 목표를 달성하기 위해 단계와 샘플 코드를 더욱 개선할 수 있습니다.

제품 버전

  • Amazon Aurora Postgre SQL호환 버전 11.9

아키텍처

대상 기술 스택

  • Amazon Aurora Postgre SQL호환

  • Amazon CloudWatch

  • Amazon Simple Notification Service(AmazonSNS)

  • Amazon S3

대상 아키텍처 

다음은 솔루션의 고급 표현을 보이는 다이어그램입니다.

데이터 파일은 S3 버킷에 업로드되고, aws_s3 확장자를 사용하여 처리되어, Aurora 인스턴스로 전송됩니다.
  1. 파일은 애플리케이션에서 S3 버킷으로 업로드됩니다.

  2. aws_s3 확장은 PL/pg를 사용하여 데이터에 액세스SQL하고 Aurora PostgreSQL-Compatible에 데이터를 업로드합니다.

도구

  • Amazon Aurora Postgre 호환 SQL- Amazon Aurora Postgre SQL호환 버전은 완전 관리형, Postgre SQL호환 및 ACID규정 준수 관계형 데이터베이스 엔진입니다. 이는 고급 상용 데이터베이스의 속도와 신뢰성을 오픈 소스 데이터베이스의 비용 효율성과 결합합니다.

  • AWS CLI – AWS 명령줄 인터페이스(AWS CLI)는 AWS 서비스를 관리하는 통합 도구입니다. 다운로드하고 구성할 수 있는 도구는 하나뿐이므로 명령줄에서 여러 AWS 서비스를 제어하고 스크립트를 통해 자동화할 수 있습니다.

  • Amazon CloudWatch – Amazon은 Amazon S3 리소스 및 사용을 CloudWatch 모니터링합니다.

  • Amazon S3 – Amazon Simple Storage Service(S3)는 인터넷에 대한 스토리지입니다. 이 패턴에서 Amazon S3는 Aurora Postgre SQL호환 클러스터와의 소비 및 전송을 위해 파일을 수신하고 저장할 스토리지 계층을 제공합니다.

  • aws_s3aws_s3 확장은 Amazon S3와 Aurora Postgre SQL호환을 통합합니다.

  • Amazon SNS – Amazon Simple Notification Service(Amazon SNS)는 게시자와 클라이언트 간의 메시지 전송 또는 전송을 조정하고 관리합니다. 이 패턴에서 AmazonSNS은 알림을 보내는 데 사용됩니다.

  • pgAdmin – pgAdmin Postgres. pgAdmin 4용 오픈 소스 관리 도구로, 데이터베이스 객체를 생성, 유지 관리 및 사용하기 위한 그래픽 인터페이스를 제공합니다.

코드

필요한 기능을 구현하기 위해 패턴은 UTL_FILE과 비슷한 이름을 가진 여러 함수를 생성합니다. 추가 정보 섹션에는 이러한 함수의 코드 베이스가 포함되어 있습니다.

코드에서 testaurorabucket을 테스트 S3 버킷의 이름으로 바꿉니다. 를 테스트 S3 버킷이 위치한 AWS 리전us-east-1으로 바꿉니다.

에픽

작업설명필요한 기술
IAM 정책을 설정합니다.

S3 버킷 및 그 안에 있는 객체에 대한 액세스 권한을 부여하는 AWS Identity and Access Management(IAM) 정책을 생성합니다. 코드에 대한 내용은 추가 정보 섹션을 참조하세요.

AWS 관리자, DBA
Amazon S3 액세스 역할을 Aurora Postgre에 추가합니다SQL.

읽기에 대한 역할 하나와 Amazon S3에 대한 쓰기 액세스에 대한 역할 하나라는 두 가지 IAM 역할을 생성합니다. 두 역할을 Aurora Postgre SQL호환 클러스터에 연결합니다. 

  • S3 내보내기 기능을 위한 역할 하나

  • S3 불러오기 기능의 역할 하나

자세한 내용은 Amazon S3로 데이터를 가져오내보내는 방법에 대한 Aurora Postgre SQL호환 설명서를 참조하세요.

AWS 관리자, DBA
작업설명필요한 기술
aws_commons 확장을 생성합니다.

aws_commons 확장은 aws_s3 확장의 종속성입니다.

DBA, 개발자
aws_s3 확장을 생성합니다.

aws_s3 확장은 Amazon S3와 상호 작용합니다.

DBA, 개발자
작업설명필요한 기술
Amazon S3에서 Aurora Postgre로 파일 가져오기를 테스트합니다SQL.

Aurora PostgreSQL-Compatible로 파일 가져오기를 테스트하려면 샘플 CSV 파일을 생성하여 S3 버킷에 업로드합니다. CSV 파일을 기반으로 테이블 정의를 생성하고 aws_s3.table_import_from_s3 함수를 사용하여 파일을 테이블에 로드합니다.

DBA, 개발자
Aurora PostgreSQL에서 Amazon S3로 파일 내보내기를 테스트합니다.

Aurora PostgreSQL-Compatible에서 파일 내보내기를 테스트하려면 테스트 테이블을 생성하고 데이터로 채운 다음 aws_s3.query_export_to_s3 함수를 사용하여 데이터를 내보냅니다.

DBA, 개발자
작업설명필요한 기술
utl_file_utility 스키마를 생성합니다.

스키마는 래퍼 함수를 함께 유지합니다. 스키마를 생성하려면 다음의 명령을 실행하세요.

CREATE SCHEMA utl_file_utility;
DBA, 개발자
file_type 유형을 생성합니다.

file_type 유형을 생성하려면, 다음의 코드를 사용하세요.

CREATE TYPE utl_file_utility.file_type AS (     p_path character varying(30),     p_file_name character varying );
DBA/개발자
init 함수를 생성합니다.

init 함수는 bucket 또는 region과 같은 공통 변수를 초기화합니다. 코드에 대한 내용은 추가 정보 섹션을 참조하세요.

DBA/개발자
래퍼 함수를 생성합니다.

래퍼 함수 fopen, put_linefclose를 생성합니다. 코드에 대한 내용은 추가 정보 섹션을 참조하세요.

DBA, 개발자
작업설명필요한 기술
쓰기 모드에서 래퍼 함수를 테스트합니다.

쓰기 모드에서 래퍼 함수를 테스트하려면 추가 정보 섹션에 제공된 코드를 사용하세요.

DBA, 개발자
추가 모드에서 래퍼 함수를 테스트합니다.

추가 모드에서 래퍼 함수를 테스트하려면 추가 정보 섹션에 제공된 코드를 사용하세요.

DBA, 개발자

관련 리소스

추가 정보

IAM 정책 설정

다음의 정책을 생성합니다.

정책 이름

JSON

S3IntRead

{     "Version": "2012-10-17",     "Statement": [         {             "Sid": "S3integrationtest",             "Effect": "Allow",             "Action": [                 "s3:GetObject",                 "s3:ListBucket"             ],             "Resource": [          "arn:aws:s3:::testaurorabucket/*",          "arn:aws:s3:::testaurorabucket"             ]         }     ] }

S3IntWrite

{     "Version": "2012-10-17",     "Statement": [         {             "Sid": "S3integrationtest",             "Effect": "Allow",             "Action": [                 "s3:PutObject",                                 "s3:ListBucket"             ],             "Resource": [                "arn:aws:s3:::testaurorabucket/*",                "arn:aws:s3:::testaurorabucket"             ]         }     ] }

init 함수 생성

bucket 또는 region과 같은 일반 변수를 초기화하려면 다음 코드를 사용하여 init 함수를 생성하세요.

CREATE OR REPLACE FUNCTION utl_file_utility.init(     )     RETURNS void     LANGUAGE 'plpgsql'     COST 100     VOLATILE AS $BODY$ BEGIN       perform set_config       ( format( '%s.%s','UTL_FILE_UTILITY', 'region' )       , 'us-east-1'::text       , false );       perform set_config       ( format( '%s.%s','UTL_FILE_UTILITY', 's3bucket' )       , 'testaurorabucket'::text       , false ); END; $BODY$;

래퍼 함수 생성

fopen,put_linefclose 래퍼 함수를 생성합니다.

fopen

CREATE OR REPLACE FUNCTION utl_file_utility.fopen(     p_file_name character varying,     p_path character varying,     p_mode character DEFAULT 'W'::bpchar,     OUT p_file_type utl_file_utility.file_type)     RETURNS utl_file_utility.file_type     LANGUAGE 'plpgsql'     COST 100     VOLATILE AS $BODY$ declare     v_sql character varying;     v_cnt_stat integer;     v_cnt integer;     v_tabname character varying;     v_filewithpath character varying;     v_region character varying;     v_bucket character varying; BEGIN     /*initialize common variable */     PERFORM utl_file_utility.init();     v_region := current_setting( format( '%s.%s', 'UTL_FILE_UTILITY', 'region' ) );     v_bucket :=  current_setting( format( '%s.%s', 'UTL_FILE_UTILITY', 's3bucket' ) );         /* set tabname*/     v_tabname := substring(p_file_name,1,case when strpos(p_file_name,'.') = 0 then length(p_file_name) else strpos(p_file_name,'.') - 1 end );     v_filewithpath := case when NULLif(p_path,'') is null then p_file_name else concat_ws('/',p_path,p_file_name) end ;     raise notice 'v_bucket %, v_filewithpath % , v_region %', v_bucket,v_filewithpath, v_region;         /* APPEND MODE HANDLING; RETURN EXISTING FILE DETAILS IF PRESENT ELSE CREATE AN EMPTY FILE */     IF p_mode = 'A' THEN         v_sql := concat_ws('','create temp table if not exists ', v_tabname,' (col1 text)');         execute v_sql;         begin         PERFORM aws_s3.table_import_from_s3             ( v_tabname,             '',               'DELIMITER AS ''#''',             aws_commons.create_s3_uri             (     v_bucket,                 v_filewithpath ,                 v_region)             );         exception             when others then              raise notice 'File load issue ,%',sqlerrm;              raise;         end;         execute concat_ws('','select count(*) from ',v_tabname) into v_cnt;         IF v_cnt > 0         then             p_file_type.p_path := p_path;             p_file_type.p_file_name := p_file_name;         else                     PERFORM aws_s3.query_export_to_s3('select ''''',                             aws_commons.create_s3_uri(v_bucket, v_filewithpath, v_region)                                           );             p_file_type.p_path := p_path;             p_file_type.p_file_name := p_file_name;                 end if;         v_sql := concat_ws('','drop table ', v_tabname);                 execute v_sql;                 ELSEIF p_mode = 'W' THEN             PERFORM aws_s3.query_export_to_s3('select ''''',                             aws_commons.create_s3_uri(v_bucket, v_filewithpath, v_region)                                           );             p_file_type.p_path := p_path;             p_file_type.p_file_name := p_file_name;     END IF;         EXCEPTION         when others then             p_file_type.p_path := p_path;             p_file_type.p_file_name := p_file_name;             raise notice 'fopenerror,%',sqlerrm;             raise; END; $BODY$;

put_line

CREATE OR REPLACE FUNCTION utl_file_utility.put_line(     p_file_name character varying,     p_path character varying,     p_line text,     p_flag character DEFAULT 'W'::bpchar)     RETURNS boolean     LANGUAGE 'plpgsql'     COST 100     VOLATILE AS $BODY$ /************************************************************************** * Write line, p_line in windows format to file, p_fp - with carriage return * added before new line. **************************************************************************/ declare     v_sql varchar;     v_ins_sql varchar;     v_cnt INTEGER;     v_filewithpath character varying;     v_tabname  character varying;     v_bucket character varying;     v_region character varying;     BEGIN  PERFORM utl_file_utility.init(); /* check if temp table already exist */  v_tabname := substring(p_file_name,1,case when strpos(p_file_name,'.') = 0 then length(p_file_name) else strpos(p_file_name,'.') - 1 end );  v_sql := concat_ws('','select count(1) FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace where n.nspname like ''pg_temp_%'''                          ,' AND pg_catalog.pg_table_is_visible(c.oid) AND Upper(relname) = Upper( '''                          ,  v_tabname ,''' ) ');    execute v_sql into v_cnt;     IF v_cnt = 0 THEN          v_sql := concat_ws('','create temp table ',v_tabname,' (col text)');         execute v_sql;         /* CHECK IF APPEND MODE */         IF upper(p_flag) = 'A' THEN             PERFORM utl_file_utility.init();                                     v_region := current_setting( format( '%s.%s', 'UTL_FILE_UTILITY', 'region' ) );             v_bucket :=  current_setting( format( '%s.%s', 'UTL_FILE_UTILITY', 's3bucket' ) );                         /* set tabname*/                         v_filewithpath := case when NULLif(p_path,'') is null then p_file_name else concat_ws('/',p_path,p_file_name) end ;                                     begin                PERFORM aws_s3.table_import_from_s3                      ( v_tabname,                           '',                          'DELIMITER AS ''#''',                         aws_commons.create_s3_uri                            ( v_bucket,                                v_filewithpath,                                v_region    )                     );             exception                 when others then                     raise notice  'Error Message : %',sqlerrm;                     raise;             end;             END IF;         END IF;     /* INSERT INTO TEMP TABLE */                   v_ins_sql := concat_ws('','insert into ',v_tabname,' values(''',p_line,''')');     execute v_ins_sql;     RETURN TRUE;     exception             when others then                 raise notice  'Error Message : %',sqlerrm;                 raise; END; $BODY$;

fclose

CREATE OR REPLACE FUNCTION utl_file_utility.fclose(     p_file_name character varying,     p_path character varying)     RETURNS boolean     LANGUAGE 'plpgsql'     COST 100     VOLATILE AS $BODY$ DECLARE     v_filewithpath character varying;     v_bucket character varying;     v_region character varying;     v_tabname character varying; v_sql character varying; BEGIN       PERFORM utl_file_utility.init();       v_region := current_setting( format( '%s.%s', 'UTL_FILE_UTILITY', 'region' ) );     v_bucket :=  current_setting( format( '%s.%s', 'UTL_FILE_UTILITY', 's3bucket' ) );     v_tabname := substring(p_file_name,1,case when strpos(p_file_name,'.') = 0 then length(p_file_name) else strpos(p_file_name,'.') - 1 end );     v_filewithpath := case when NULLif(p_path,'') is null then p_file_name else concat_ws('/',p_path,p_file_name) end ;     raise notice 'v_bucket %, v_filewithpath % , v_region %', v_bucket,v_filewithpath, v_region ;         /* exporting to s3 */     perform aws_s3.query_export_to_s3         (concat_ws('','select * from ',v_tabname,'  order by ctid asc'),             aws_commons.create_s3_uri(v_bucket, v_filewithpath, v_region)         );    v_sql := concat_ws('','drop table ', v_tabname);     execute v_sql;        RETURN TRUE; EXCEPTION        when others then      raise notice 'error fclose %',sqlerrm;      RAISE; END; $BODY$;

래퍼 함수 설정 및 테스트

다음 익명 코드 블록을 사용하여 설정을 테스트합니다.

쓰기 모드 테스트

다음 코드는 S3 버킷에 s3inttest라는 파일을 작성합니다.

do $$ declare l_file_name varchar := 's3inttest' ; l_path varchar := 'integration_test' ; l_mode char(1) := 'W'; l_fs utl_file_utility.file_type ; l_status boolean; begin select * from utl_file_utility.fopen( l_file_name, l_path , l_mode ) into l_fs ; raise notice 'fopen : l_fs : %', l_fs; select * from utl_file_utility.put_line( l_file_name, l_path ,'this is test file:in s3bucket: for test purpose', l_mode ) into l_status ; raise notice 'put_line : l_status %', l_status; select * from utl_file_utility.fclose( l_file_name , l_path ) into l_status ; raise notice 'fclose : l_status %', l_status; end; $$

추가 모드 테스트

다음 코드는 이전 테스트에서 만든 s3inttest 파일에 줄을 추가합니다.

do $$ declare l_file_name varchar := 's3inttest' ; l_path varchar := 'integration_test' ; l_mode char(1) := 'A'; l_fs utl_file_utility.file_type ; l_status boolean; begin select * from utl_file_utility.fopen( l_file_name, l_path , l_mode ) into l_fs ; raise notice 'fopen : l_fs : %', l_fs; select * from utl_file_utility.put_line( l_file_name, l_path ,'this is test file:in s3bucket: for test purpose : append 1', l_mode ) into l_status ; raise notice 'put_line : l_status %', l_status; select * from utl_file_utility.put_line( l_file_name, l_path ,'this is test file:in s3bucket : for test purpose : append 2', l_mode ) into l_status ; raise notice 'put_line : l_status %', l_status; select * from utl_file_utility.fclose( l_file_name , l_path ) into l_status ; raise notice 'fclose : l_status %', l_status; end; $$

Amazon SNS 알림

선택적으로 S3 버킷에 Amazon CloudWatch 모니터링 및 Amazon SNS 알림을 설정할 수 있습니다. 자세한 내용은 Amazon S3 모니터링Amazon SNS 알림 설정을 참조하세요.