실험 목록으로 돌아가기
E001완료P0

SQL 호환성과 실행 가능성

DSQL에서 같은 업무를 구현하려면 무엇을 바꿔야 하는가?

결과 요약

이 실험은 기존 PostgreSQL 업무를 Aurora DSQL로 옮길 때 SQL을 얼마나 고쳐야 하는지 확인했습니다. 업무 SQL 35개 항목을 DSQL(D1)과 대조군 세 서비스(RDS PostgreSQL, Aurora Provisioned, Aurora Serverless v2)에서 그대로 실행했습니다. DSQL은 17개 항목을 수정 없이 통과했고, 16개 항목은 “지원하지 않는 기능”(SQLSTATE 0A000)으로 거부했습니다. 대조군 세 서비스는 DSQL 전용 문법인 CREATE INDEX ASYNC를 제외한 34개 항목을 모두 통과했습니다.

DSQL에서 수정이 필요했던 영역은 sequence·identity 선언, serial 타입, 인덱스 생성 방식, 임시 테이블과 파티션, PL/pgSQL 함수와 트리거, 격리 수준 지정, statement_timeout, 서버 측 문장 취소입니다. 반면 PK·UNIQUE·CHECK·외래키 같은 제약, JOIN·CTE·window 함수, JSONB, 그리고 핵심 업무인 주문 트랜잭션은 원본 그대로 동작했습니다. SELECT FOR UPDATE는 문법은 통과했지만 동작이 달랐습니다. 대조군은 다른 쓰기를 대기시켰고, DSQL은 대기시키지 않고 커밋 시점에 충돌(40001)로 한쪽을 실패시켰습니다. 따라서 DSQL로 옮기는 애플리케이션에는 실패한 트랜잭션을 다시 시도하는 로직이 필요합니다.

이 결과는 소형 구성과 로컬 클라이언트로 SQL 기능만 검사한 것이며, 각 구성을 한 번씩 실행했습니다. 성능, 지연, 고가용성은 평가하지 않았고, DSQL의 지원 범위는 계속 바뀌므로 2026-09-24 시점의 관측으로 읽어야 합니다.

프로덕션 사용 관점

DSQL은 제약·외래키·JOIN·JSONB와 주문 트랜잭션 같은 핵심 OLTP SQL을 수정 없이 실행했으므로, 처음부터 DSQL 제약에 맞춰 설계하는 새 서비스라면 SQL 측면의 도입 장벽은 낮습니다. 반면 기존 PostgreSQL 서비스를 옮기려면 sequence·serial 선언, 인덱스 생성 방식, PL/pgSQL·트리거, 임시 테이블·파티션을 바꿔야 합니다. 또 격리 수준이 REPEATABLE READ뿐이고 statement_timeout과 서버 측 취소가 동작하지 않으므로, 충돌 재시도와 요청 기한을 애플리케이션에서 구현해야 합니다. 저장 프로시저와 트리거에 로직이 많은 시스템일수록 이관 비용이 커집니다. 이 실험은 기능 검사일 뿐이며, 성능·가용성은 E002 이후 실험으로 판단합니다.

확인할 질문

DSQL에서 같은 업무를 구현하려면 무엇을 바꿔야 하는가? 기존 PostgreSQL 업무 SQL 35개 항목을 D1(Aurora DSQL)과 대조군 3종(R1 RDS PostgreSQL, A1 Aurora Provisioned, A2 Aurora Serverless v2)에서 실행하고, 문법 수용 여부와 결과·불변식 일치 여부를 함께 판정했습니다.

실행 조건

  • 측정일: 2026-09-24 UTC, 리전 ap-northeast-2, 실행 prefix e001-20260924t071645z-d551.
  • 코드 커밋: dfbbe696da48df2df5434ee400c4386fff807197(4개 구성 모두 같은 코드).
  • 구성(계획 대비 축소 — SQL 기능 검사 전용):
ID 실제 구성 계획 구성과의 차이
D1 DSQL 단일 리전 클러스터, admin IAM 토큰, verify-full TLS 없음
R1-lite RDS PostgreSQL 16.15, db.t4g.micro, gp3 20 GiB, Multi-AZ 클래스·스토리지 축소
A1-lite Aurora PostgreSQL 16.15, db.t4g.medium writer 1대 reader 없음, 소형 클래스
A2-lite Aurora Serverless v2 16.15, writer 1대, 0.5–2 ACU reader 없음, ACU 범위 축소
  • 클라이언트: 로컬 PC(Python·psycopg)에서 공개 엔드포인트로 접속했습니다. 이 접속 경로의 지연은 비교 근거로 쓰지 않습니다.
  • 각 항목은 전용 테이블과 새 연결에서 실행해 한 항목의 실패가 다른 항목에 영향을 주지 않게 했습니다. 문장마다 30초 클라이언트 기한이 있습니다.
  • 실행 중 편차: R1 프로비저닝 도중(07:20Z 시작) 실행을 관리하던 에이전트 세션이 사용량 제한으로 중단되었습니다. 인스턴스는 계속 생성되어 07:31Z에 available이 되었고, 같은 prefix의 manifest에 연결 정보를 보완한 뒤 run → cleanup → verify를 수동으로 이어서 실행했습니다. 검사 코드와 입력은 바뀌지 않았습니다. R1이 Multi-AZ로 생성된 것은 CloudTrail의 CreateDBInstance 요청(multiAZ=true)으로 확인했습니다.

성능 결과

이 실험은 기능 검사이며 처리량·지연을 측정하지 않았습니다(미측정). 항목별 판정 결과는 다음과 같습니다. pass는 문법 수용과 의미 검증 통과, unsupported는 SQLSTATE 0A000, rejected_needs_review는 문법·객체 오류로 사람이 판정해야 하는 경우, semantic_mismatch는 문장은 수용되었지만 기대한 동작과 다른 경우입니다.

판정 D1 R1 A1 A2
pass 17 34 34 34
unsupported (0A000) 16 0 0 0
rejected_needs_review 1 1 1 1
semantic_mismatch 1 0 0 0

R1·A1·A2의 rejected_needs_review 1건은 DSQL 전용 문법인 CREATE INDEX ASYNC(42601)이며, PostgreSQL에서 거부되는 것이 예상된 결과입니다. 대조군 세 구성 사이에는 차이가 없었습니다.

DSQL(D1)에서 결과가 갈린 항목:

항목 D1 결과 관측 내용 이식 방법
CREATE SEQUENCE(기본 CACHE), GENERATED ... AS IDENTITY(기본 CACHE) unsupported 기본 CACHE 값 거부 CACHE 65536 명시 시 통과
serial 타입 rejected (42704) type "serial" does not exist identity 또는 sequence(CACHE 65536)로 변경
CREATE INDEX(B-tree·GIN·표현식) unsupported “please use CREATE INDEX ASYNC” B-tree는 CREATE INDEX ASYNC로 통과. GIN·표현식 인덱스는 대체 미검증
임시 테이블, 범위 파티션 unsupported — 애플리케이션 설계 변경 필요
PL/pgSQL 함수, 트리거 unsupported SQL 함수는 통과 로직을 SQL 함수나 애플리케이션으로 이동
SET TRANSACTION ISOLATION LEVEL ... 3종, BEGIN ISOLATION LEVEL READ COMMITTED/SERIALIZABLE unsupported BEGIN ISOLATION LEVEL REPEATABLE READ만 수용 REPEATABLE READ 전제로 로직 검토
statement_timeout(sleep·CPU 변형) unsupported 세션 설정 거부 클라이언트 측 기한으로 대체
클라이언트 취소 semantic_mismatch pg_sleep(5)에 취소 요청을 보냈지만 문장이 끝까지 실행되어 성공(기대: 57014) 서버 측 중단에 의존하지 않도록 작업 크기 제한

DSQL에서 수정 없이 통과한 항목: PK/UNIQUE/CHECK, 외래키(고아 행 삽입과 참조된 부모 삭제를 모두 23503으로 거부), UPSERT, JOIN/CTE/window, 재귀 CTE, JSONB 런타임·저장 컬럼, SQL 함수, SELECT FOR UPDATE, REPEATABLE READ lost update 방지, 공통 주문 트랜잭션(FK 원본과 FK 없는 변형 모두 업무 결과·불변식 일치).

SELECT FOR UPDATE는 네 구성 모두 lost update 없이 통과했지만 동작 방식이 다릅니다. 시나리오는 연결 A가 행을 FOR UPDATE로 잠그고 +1 갱신을, 그동안 연결 B가 같은 행에 +10 갱신을 시도하는 것입니다. R1·A1·A2에서는 B가 A의 커밋까지 대기했고 두 갱신이 모두 반영되었습니다(100 → 111). D1에서는 B의 쓰기가 A의 잠금 중에 먼저 끝났고, A가 커밋 시점에 40001(직렬화 충돌)로 실패해 A의 갱신이 롤백되었습니다(100 → 110). 즉 DSQL의 FOR UPDATE는 다른 쓰기를 대기시키지 않고 커밋 시점 충돌로 정합성을 지키므로, 실패한 트랜잭션을 재시도하는 로직이 애플리케이션에 있어야 같은 업무 결과를 얻습니다. 경합 조건에서의 정합성과 재시도 비용은 E004에서 검증합니다.

개발·운영 편의성

  • 준비 시간(생성 요청부터 접속 가능까지): D1 16초, A1 약 5분, A2 약 7분, R1 약 12분(Multi-AZ 전환 포함). 삭제(요청부터 삭제 완료 확인까지): D1 약 1.5분, R1 약 3.5분, A1 약 6.5분, A2 약 6분.
  • D1은 VPC·서브넷·보안 그룹·DB 비밀번호 없이 IAM 토큰으로 접속했습니다. 대조군은 구성마다 VPC, 서브넷 2개, IGW, 보안 그룹, DB 서브넷 그룹을 만들고 비밀번호를 관리해야 했습니다.
  • 반면 기존 스키마·SQL을 DSQL로 옮기려면 위 표의 수정(sequence CACHE, serial 제거, 인덱스 ASYNC, PL/pgSQL·트리거 이전, 격리 수준·timeout 처리 변경, 40001 재시도)이 필요합니다. 수정 작업 시간은 이번 실험에서 측정하지 않았습니다.
  • 위 시간은 단일 실행 값이며 반복 측정으로 변동을 확인하지 않았습니다.

비용

단가는 2026-09-24에 AWS Price List API로 확인한 서울 리전 On-Demand 가격입니다. 아래 표의 추정은 생성 요청부터 삭제 완료까지를 가동 시간 상한으로 잡은 값이고, 실제 청구액은 2026-09-28에 Cost Explorer(서울 리전, 2026-09-24 UTC, 사용 유형별)로 확인한 값입니다.

구성 가동 시간(상한) 단가 추정 실제 청구액
D1 DSQL 클러스터 생성-삭제 약 2분 $0.00001/DPU 파일럿 1회 57 DPU로 약 $0.0006(비교 실행 DPU 미수집) $0 (DSQL 월 무료 사용량 안)
R1 15.9분 $0.051/시간(db.t4g.micro Multi-AZ) 약 $0.014 약 $0.012 (스토리지 포함)
A1 12.0분 $0.113/시간(db.t4g.medium) 약 $0.023 약 $0.019 (10분 최소 과금)
A2 13.0분 $0.20/ACU-시간 0.5–2 ACU 범위로 약 $0.022–0.087 약 $0.048 (0.217 ACU-시간, Aurora I/O 포함)

추정 때는 스토리지, Aurora I/O, 공인 IPv4 요금이 소량이라 제외했고, 합계를 약 $0.06–0.13으로 추정했습니다. 실제 청구액 합계는 약 $0.08로 추정 범위 안에 있었습니다. A1은 실제 가동이 10분 미만이어서 RDS 최소 과금 시간인 10분(0.167 시간)으로 청구되었습니다.

결론과 한계

  • 결론: 이번 35개 항목 기준으로 DSQL은 17개(49%)를 수정 없이 실행했고, 대조군 세 서비스는 PostgreSQL 기능 항목을 모두 통과했습니다. DSQL로 옮길 때 수정이 필요한 영역은 sequence·identity 선언, serial, 인덱스 생성 방식, 임시 테이블·파티션, PL/pgSQL·트리거, 격리 수준 지정, statement_timeout, 서버 측 문장 취소입니다. 핵심 업무 흐름인 주문 트랜잭션은 FK를 포함한 원본 그대로 DSQL에서 통과했습니다.
  • DSQL의 FK 지원은 이번 실측(2026-09-24)에서 관측된 동작입니다. DSQL 지원 범위는 계속 바뀌므로 다른 시점에는 결과가 다를 수 있습니다.
  • 한계: 로컬 클라이언트와 소형 구성의 기능 검사이며 지연·처리량·HA를 평가하지 않았습니다. 각 구성 1회 실행입니다. SELECT FOR UPDATE와 격리 수준은 두 연결의 단일 시나리오 관측입니다. ORM 스키마 변경과 논리 복제/CDC는 미측정입니다. GIN·표현식 인덱스의 DSQL 대체 방법은 검증하지 않았습니다.

정리 기록

  • 삭제 대상: D1 DSQL 클러스터 1개, R1·A1·A2 각각의 DB 인스턴스(Aurora는 DB 클러스터 포함), DB 서브넷 그룹, 보안 그룹, 서브넷 2개, IGW, VPC.
  • 정리 완료 시각(UTC): D1 07:18:46, R1 07:35:56, A1 07:48:12, A2 08:01:18. 모든 삭제 단계에서 실패가 없었습니다.
  • 검증: 08:01:37Z에 실험 도구의 verify로 이 prefix의 manifest 리소스, 스냅샷, 보존된 자동 백업, 태그된 EC2 리소스를 조회해 remaining_count=0을 확인했습니다. 파일럿 prefix(e001-20260924t071247z-cf9e)도 08:01:38Z에 0개를 확인했습니다. 추가로 계정 전체에서 e001 접두어 DB 인스턴스·클러스터·클러스터 스냅샷, DSQL 클러스터, e001:run-prefix 태그 VPC를 조회해 모두 0개임을 확인했습니다.