오랜만에 제가 좋아하는 데이터베이스 분야에서 신기술이 나왔습니다. 처음엔 영상으로 접했는데 그냥 AWS에서 신기술 냈구나 정도로 생각했지만 계속 영상을 보면서 흥미를 가질 수 밖에 없었습니다. Serverless RDBMS라고? 이게 무슨 혼종이지? 싶었죠.
오늘은 AWS Aurora의 후속 버전, 클라우드 데이터베이스의 미래 AWS Aurora DSQL에 대해서 공부한 내용을 정리해보는 포스팅을 작성해보려고합니다.
AWS의 클라우드 데이터베이스 역사
AWS는 처음엔 RDS를 선보였습니다. 이는 단순히 오픈소스로 되어있는 데이터베이스 엔진을 이용해서 RDBMS를 구축한 것에 불과했죠. AWS RDS를 시작으로 점점 사용자들은 다양한 니즈가 생기기 시작했습니다.
2009년에 RDS가 선보인 것을 시작으로 2011년 아이폰의 엄청난 보급으로 어플에 대한 니즈가 생기기 시작하면서 NoSQL이 급격히 부상하게 되었고 이에 맞춰 AWS에서는 DynamoDB라는 Serverless NoSQL을 선보이게 되었죠.
이후 스마트폰과 어플의 보급으로 기존과 비교할 수도 없을 정도의 빅데이터가 쌓이게 되었고 사용자의 패턴을 분석하거나 개인화를 하는 과정에서 통계쿼리가 필요했지만 이미 엄청나게 거대해져버린 데이터베이스들은 통계쿼리에도 엄청난 리소스를 먹기 시작했습니다. 이런 니즈가 AWS Redshift를 런칭하게 되었죠.
NoSQL이 흥하긴 했어도 높은 일관성을 보여주는 RDBMS에 대한 니즈도 착실하게 늘어나기 시작했습니다. 이에 AWS는 운영 복잡성과 안정성을 높이면서도 높은 성능을 요구하는 니즈에 발맞춰 AWS Aurora를 런칭했습니다.
RDBMS의 한계
RDBMS는 기본적으로 CAP중 CP에 가깝습니다. PostgreSQL의 경우 데이터의 일관성이 깨질 것 같으면 아예 노드를 내려버려서 쓰기를 아예 막는 경우도 있고 대부분의 RDBMS 진영에선 마스터 노드가 죽으면 재빠르게 슬레이브 노드가 승격되면서 쓰기 작업을 이어나가야했죠.
또한, 트랜잭션으로 일관성을 보장하기 때문에 데이터의 일관성을 위해 행단위의 락을 걸어야했고 뒤에 도착한 트랜잭션은 이 락이 해제될 때까지 기다려야했습니다. 이는 쓰기 작업에서 지연시간도 생기고 처리량도 높아지기 힘들었죠.
그래서 RDBMS를 사용한다면 읽기 처리량은 슬레이브 노드를 확장하거나 캐싱을 통해서 낮은 지연시간과 높은 처리량을 보여줬지만 쓰기 작업은 읽기에 비해 무겁고 인덱싱이 되어있다면 더 무거워지죠. 그리고 앞서 언급했듯이 락을 획득해야했기에 지연시간도 높고 처리량도 낮았죠.
그래서 보통 이런 대규모 서비스에선 쓰기 작업은 비동기로 처리함으로써 클라이언트 입장에선 금방 작업이 된 것처럼 보이지만 사실 뒤에선 Kafka나 RabbitMQ, Redis Stream같은 비동기 메세지 큐 (이벤트 드리븐 아키텍처) 를 이용해야했습니다.
하지만 이는 운영 복잡도를 엄청나게 늘리는 계기가 되었죠. KRaft를 지원하기 이전의 Kafka는 Zookeeper의 관리도 필요했을 뿐더러 KRaft로 높은 처리량을 가져갈지라도 partition과 Consumer Group의 샤딩이 필수였고 샤딩으로 인해 복잡성이 추가로 증가하게 되면서 운영 복잡도는 말도안되게 높아진 상황이 되었죠.
AWS팀에선 이런 상황이 싫었던 것 같습니다. 그리고 RDBMS를 사용하던 개발자들도 니즈가 확실히 생긴 것 같습니다.
아니 쓰기 작업도 읽기처럼 할 수 없어? 읽기 작업은 확장하기도 용이하고 락에 의한 지연시간도 높지 않고 좋던데? RDBMS는 너무 읽기 성능을 올리는데 진심이야. B- tree부터가 읽기 성능을 최적화하기 위한 알고리즘이고 인덱싱도 그렇고 Optimizer도 읽기 성능을 위한거잖아?
그랬더니 결과가 이게 뭐야. 쓰기 작업이 많아지면 TPS가 급격하게 떨어지고 인덱스까지 걸려있으면 성능이 더 떨어지니 쓰기 작업이 많은 서비스들은 죄다 NoSQL로 갈아타고 있잖아. 쓰기 작업이 많아지면 비동기 메세지 큐를 도입해야하고 운영 복잡도도 너무 높아져.
AWS Aurora DSQL은 여기서 출발합니다.
AWS Aurora DSQL
일단 아키텍처를 던져보고 시작하겠습니다. 기존 Query Processor 와 Storage 사이에 뭔가 덕지덕지 많은게 추가됐습니다.
하나씩 풀어보겠습니다.
- Query Processor : 쿼리를 실질적으로 실행하기위한 엔진이 여기 들어가게 됩니다. 쿼리를 해석하고 실행하기위해 다음 단계로 넘기는 역할이죠.
- Adjudicator : 이친구는 트랜잭션이 충돌인지 아닌지를 판단하는 간단한 컴포넌트입니다. 해당 트랜잭션이 충돌이 아니라면 트랜잭션의 커밋을 확정짓고 Journal이라는 객체에 값을 전달해줍니다.
- Journal : Journal은 트랜잭션의 단위이고 Crossbar로 쿼리를 전달하게 되는데 이 Journal에 해당하는 우리가 잘 아는 개념이 바로 Kafka에 대응되는 개념입니다. 물론 정확히 100퍼센트 일치하는 개념은 아니지만 Adjudicator가 판단한 커밋된 쿼리가 날아가면서 어떤 쿼리가 실행됐는지 적는 "로그 시스템" 이기 때문에 중간 다리로서의 분산된 형태의 로그 기반 시스템이라는 것이죠.
- Crossbar : Crossbar는 스토리지와 연결되어있고 Crossbar는 Range 기반 샤딩이 되어있고 각 범위에 키를 구독하고 있는 스토리지에 쓰기 작업을 넣어주는 역할을 합니다.
DSQL은 동시성 문제를 읽는 시점이 아닌 커밋 시점에 판단하고 이를 뒤로 미룸으로써 성능을 가져오는 아키텍처를 선택했습니다.
이건 흔히 RDBMS가 MVCC (Multi Version Concurrency Control / 다중 버전 동시성 제어) 를 채택하는 것과 다르게 OCC (Optimistic Concurrency Control / 낙관적 동시성 제어) 를 채택한다는 것이 특이한 점인데요.
AWS측에선 OCC를 활용하는만큼 애플리케이션 레벨에서 트랜잭션 충돌로 인한 예외 발생시 재시도하는 로직이 반드시 필요하다고 말하고 있습니다.
이렇게 아키텍처를 설계하면 어떤 이점을 가지게 될까요?
- 낮은 지연시간, 높은 처리량이 가능하다는게 확실히 두드러지는 장점입니다.
- 보통 읽기와 쓰기 중 하나의 성능을 높이고 싶은 경우 각각을 확장시킬 수 없고 양쪽 다 확장하는 수직적인 확장만이 이를 가능케 했지만 읽기와 쓰기를 나눔으로써 각각 다른 확장이 가능하다는 것이 큰 장점입니다.
- Serverless라면 cold start의 문제가 있는 것을 미리 Firecracker 기반의 Mircro VM을 미리 만들어두어 cold start를 최소화했기에 스파이크성 트래픽이 아니라면 성능이 유지된다는 것이 특징입니다.
- 이미 Adjudicator에서 트랜잭션의 충돌을 검사하기에 커밋이 확실한 것들만 Journal로 넘어가고 Crossbar를 구독하고 있는 스토리지는 커밋이 확정된 쿼리만 스토리지에 전달하면 되기 때문에 분산 합의 알고리즘 (Paxos, Raft 등) 이 필요 없습니다.
- 비동기 인덱스를 설정할 수 있다. (이 이야기는 조금 뒤에서 풀겠습니다.)
Serverless RDBMS
여기서 Serverless에 대한 내용을 얘기를 하지 않았는데 기존 Micro VM은 Lambda에서 활용되었던 기술을 사용하는 것이고 기존 Lambda에서 문제였던 cold start 문제는 로직이 실행될 때 VM이 실행되는 과정에서 구동되는데까지 걸리는 시간 (애플리케이션이 뜨는 시간) 을 의미하죠.
DSQL도 Micro VM을 사용하지만 쿼리한번에 VM이 뜨는 cold start가 걸리면 성능에 지대한 영향을 미치기에 미리 VM을 만들어두어 쿼리 성능을 유지시키는 방향을 생각했다는 것이죠.
이는 우리에게도 흔한 개념인 커넥션 풀을 클라우드식으로 구현했다고 볼 수 있을 것 같습니다.
Serverless인 것과 별개로 full managed 모델이기 때문에 원래라면 개발자가 직접 Auto Vacuum을 설정해줘야 하지만 DSQL에서는 자동으로 Vacuum이 동작합니다. 물론 원래의 PostgreSQL처럼 유연하게 Auto Vacuum을 몇초에 한번씩 할건지 설정할 수는 없겠지만 Auto Vacuum에 대해 신경쓰지 않아도 되니 굉장히 편할 것 같습니다.
또한, 그렇기 때문에 인덱싱 과정도 자동으로 관리해주는데요. 기존의 PostgreSQL은 CLUSTER 명령어를 이용해서 테이블을 주기적으로 정렬해주는 작업이 필요했습니다. CLUSTER 명령은 딱 한번만 수행되기 때문에 이후에는 정렬되지 않은 값들이 들어오게 되는데 이 때문에 지속적으로 CLUSTER 명령어를 실행시켜야만 했습니다.
하지만 DSQL은 새로운 데이터가 들어올 때마다 자동으로 정렬해주기 때문에 테이블을 주기적으로 정렬해줄 필요가 없습니다.
이것이 장점인지 단점인지는 확실히 알 수는 없지만 AWS팀에서 선택한 것이니 합리적인 의사결정에 의해 내려진 판단이라고 생각하고 저는 넘어갈 생각입니다.
분산 합의 알고리즘
분산 합의 알고리즘이 필요한 이유가 쓰기 작업만 전문으로 하는 마스터 노드가 장애 발생 시 슬레이브 노드가 이를 빠르게 감지하고 바로 마스터 노드로 승격해 쓰기 작업을 이어나가야 하기 때문에 반드시 필요한 개념이었습니다.
하지만 애초에 DSQL은 Adjudicator에서 충돌을 감지하고 커밋이 확실한 쿼리만 Crossbar로 넘어가고 이를 구독한 스토리지가 그냥 받아서 쓰기만 하면 되기 때문에 분산 합의 알고리즘이 필요가 없는 것이죠.
비동기 인덱스
보통 RDBMS에서 인덱스를 설정할 때는 테이블 단위의 락이 걸립니다. 이 때 트랜잭션이 쓰기 작업을 혹은 읽기 작업을 위해 트랜잭션이 접근하면 락을 놓을 때 까지 (인덱스가 설정될 때 까지 / 인덱스를 건 컬럼이 정렬될 때까지) 트랜잭션이 접근하지 못하기 때문에 곧바로 성능 저하가 생깁니다.
하지만 Aurora DSQL의 경우 OCC를 기반으로 하고 있기 때문에 락을 굳이 걸 필요가 없습니다. 만약 인덱스를 설정하고 있는 과정에서 트랜잭션이 접근하면 그냥 예외를 던져버려서 재시도를 유도하면 되기 때문이죠.
물론 이 과정에서 애플리케이션은 재시도를 해야 하기 때문에 부하가 생길 수도 있는 것이 단점이겠네요.
AWS Aurora DSQL의 단점
와 진짜 DSQL 만능이네요 무조건 써야겠네
확실히 신기하고 매력적이긴 하지만 DSQL에도 단점이 있습니다.
1. 외래키 제약조건
DSQL은 외래키 제약조건을 사용할 수 없다는 것이 단점입니다. 물론 서비스를 운영할 때는 외래키는 논리적으로만 설정하지 실제로 물리적인 데이터베이스에 외래키를 직접적으로 맺지 않는 것이 보통인 경우가 많습니다.
그 이유는 외래키 제약조건이 생기면 데이터베이스의 무결성은 유지되겠지만 관리하기가 매우매우 까다로워져 보통 외래키를 직접적으로 맺지 않는 것입니다.
너무 깨끗한 물에는 물고기가 살지 않는 것과 같은 이치이죠.
하지만 그렇다고 외래키 제약조건이 쓸모없는 것은 아닙니다. 상황에 따라선 물을 깨끗하게 유지해야하는 경우도 있기 때문이죠. 깨끗한 물에는 물고기가 안살지만 사람은 깨끗한 물을 마시는것이 좋은 것을 예로 들 수 있겠네요.
이런 점은 아쉽게 느껴지는 부분인 것 같습니다.
2. Auto Increment의 부재
PK에 대해 Auto Increment가 불가능하다는 것이 두번째 단점입니다. AWS측에선 UUID를 PK를 정하는 것을 권장하는데 그 이유가 분산 시스템에 최적화된 데이터베이스를 지향하고 쓰기 작업에도 OCC의 철학을 접목시키기 위해 쓰기 작업에 조금 더 초점이 맞춰진 데이터베이스입니다.
보통 NoSQL진영 혹은 RDBMS에서도 분산 환경이라면 쓰기 작업이 많은 테이블의 경우 랜덤한 PK를 사용하는 것이 일반적인 경우입니다. 이 때 랜덤한 PK를 사용하게 되면 인덱싱하는 과정에서 성능 하락이 불가피하지만 그럼에도 불구하고 분산 환경에서는 한쪽 노드로 요청이 쏠리는 hotspot 문제를 피할 수 있기 때문에 이 방법이 많이 사용되곤 합니다.
만약 단조 증가하는 PK라면 반드시 다음 데이터가 맨 마지막에 위치할 수 밖에 없고 이런 것들이 hotspot을 야기한다는 것이죠.
때문에 분산 환경에서는 쓰기 작업이 많은 테이블의 경우 PK를 랜덤한 값으로, 읽기 작업이 많은 테이블의 경우 인덱싱의 효과를 제대로 볼 수 있게 Auto Increment를 설정하는 편이죠.
Aurora DSQL에서는 Auto Increment를 사용할 수 없는게 단점으로 꼽힙니다. 아무래도 언급했듯이 쓰기 작업에 더 초점이 맞춰져 있기 때문에 분산 환경에 적합한 방식을 채택한 것이죠.
Aurora DSQL에서 Auto Increment를 사용하고싶다면 DynamoDB에 단조 증가하는 넘버링을 적어주고 해당 값을 끌어다 저장하는 방식을 사용할 수 있습니다. 사용해본 사람들의 말로는 꽤나 유용한 방법이라고 하니 이렇게 사용하는 것도 방법일 수 있겠네요.
하지만 개인적으로 아쉬운건 PostgreSQL에 기반하고 있지만 Auto Increment가 안되게 설정한 것은 아쉽습니다. 차라리 개발자가 유동적으로 선택할 수 있게 했으면 어땠을까 하는 아쉬움이 있습니다.
3. PostgreSQL의 호환성 문제
기본적으로 DSQL은 PostgreSQL을 기반으로 만들어졌습니다. 하지만 PostgreSQL과 100퍼센트 호환이 안되기 때문에 만약 PostgreSQL로 이미 서비스를 하고 있는 상황이라면 DSQL로 마이그레이션 하는 것을 조금 진지하게 생각해봐야할 문제입니다.
PG 어드민도 사용할 수 없어서 AWS는 DBeaver같은 GUI 외부 툴을 이용하라고 권장하고 있죠.
이외에도 어떤 부분이 호환되지 않는지에 대해서 공식 문서에서 자세히 다루고 있기 때문에 해당 문서를 보시는 것을 추천드립니다.
Unsupported PostgreSQL features in Aurora DSQL - Amazon Aurora DSQL
Unsupported PostgreSQL features in Aurora DSQL Aurora DSQL is PostgreSQL compatible. This means that Aurora DSQL supports core relational features such as ACID transactions, secondary indexes, joins, insert, and updates. For an overview of supported SQL fe
docs.aws.amazon.com
4. JSON, JSONB 타입 저장 불가능
이건 꽤나 아쉬운 점이라고 생각하는데 PostgreSQL이 다른 RDBMS와 다른 점이 JSON타입을 저장할 수 있는 점이라고 생각했는데요. AWS Aurora DSQL의 경우 PostgreSQL 기반이긴 하지만 JSON, JSONB 타입의 데이터를 저장할 수 없습니다.
전체적인 Aurora DSQL의 방향성이 쓰기 작업도 읽기 작업같이 높은 확장성과 높은 유연성을 가지고 싶어하는 철학을 접목시켜서 쓰기 작업과 읽기 작업을 분리해 쓰기 작업에 대한 성능 최적화를 위한 것처럼 보이기 때문에 읽기 작업에서 큰 활약을 보여주는 JSON타입은 저장할 수 없는 것으로 보입니다.
또한, 만약 JSON타입을 저장할 수 있게 해주면 Range 기반의 샤딩을 처리할 때 파티셔닝이 제대로 이루어지지 않는 문제도 있을 것 같긴 하네요.
아니면 개발하는 과정에서 Adjudicator가 커밋할 쿼리를 판단하는 과정에서 JSON타입은 큰 성능 저하와 같은 문제가 있었기 때문에 넣지 않은 것일 수도 있겠네요.
아무튼 AWS 팀에서 밝히지 않았으니 정확한 이유는 알 수 없지만 Aurora DSQL과는 철학이 맞지 않는 것은 확실해보입니다. 하지만 그래도 아쉽네요...
마치며
제가 데이터베이스를 공부하는 것을 좋아하기도 하고 클라우드도 관심이 있는지라 이번엔 정말 재밌게 공부했습니다. 특히 AWS에서 DSQL을 직접 개발했던 개발자의 블로그까지 찾아가서 비하인드 스토리까지 보면서 씹고 뜯고 맛보고 즐길 수 있었습니다.
특히 100퍼센트 JVM기반으로 기획하고 개발하던 도중 100퍼센트 Rust로 변경하게 되는 과정은 정말로 흥미로웠고 역시 AWS가 클라우드의 1등 자리를 지키는 이유가 있구나 싶었습니다.
물론 단점이 없는 것은 아니기에 아쉬운 점도 있었지만 대부분 Aurora DSQL과 철학적으로 맞지 않아 납득이 가는 단점들이라 크게 신경이 쓰이진 않았습니다.
백엔드 개발이라는 것이 보수적이고 격변이랄게 딱히 없는 분야이기 때문에 이것이 장점이기도, 단점이기도 합니다. 이번에 DSQL로 인해 클라우드 네이티브 데이터베이스에 격변이 크게 일어난 것 같은 느낌이 들었습니다.
저도 기회가 되면 한번 사용해볼 기회가 있으면 좋을 것 같습니다. 이번 포스팅은 여기서 마치도록 하겠습니다. 긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요~
함께 보면 좋을 글
https://www.allthingsdistributed.com/2025/05/just-make-it-scale-an-aurora-dsql-story.html
Just make it scale: An Aurora DSQL story
AWS Senior Principal Engineers, Niko Matsakis and Marc Bowes, take us inside Aurora DSQL's development: scaling write operations without two-phase commit, overcoming garbage collection hurdles, and embracing Rust for both data and control planes.
www.allthingsdistributed.com
> AWS Aurora DSQL 팀의 회고 글
https://brooker.co.za/blog/2024/12/03/aurora-dsql.html
DSQL Vignette: Aurora DSQL, and A Personal Story - Marc's Blog
DSQL Vignette: Aurora DSQL, and A Personal Story It's happening. In this morning’s re:Invent keynote, Matt Garman announced Aurora DSQL. We’re all excited, and some extremely excited, to have this preview release in customers’ hands. Over the next fe
brooker.co.za
> AWS Aurora DSQL 핵심 개발자의 개인적인 회고
https://brooker.co.za/blog/2024/12/04/inside-dsql.html
DSQL Vignette: Reads and Compute - Marc's Blog
DSQL Vignette: Reads and Compute The easy half of a database system? In today’s post, I’m going to look at half of what’s under the covers of Aurora DSQL, our new scalable, active-active, SQL database. If you’d like to learn more about the product
brooker.co.za
> AWS Aurora DSQL 핵심 개발자의 읽기 쿼리 관련 포스팅
https://brooker.co.za/blog/2024/12/05/inside-dsql-writes.html
DSQL Vignette: Transactions and Durability - Marc's Blog
DSQL Vignette: Transactions and Durability The hard half of a database system? In today’s post, I’m going to look at the other half of what’s under the covers of Aurora DSQL, our new scalable, active-active, SQL database. If you’d like to learn mor
brooker.co.za
> AWS Aurora DSQL 핵심 개발자의 쓰기 쿼리 관련 포스팅
'DevOps > AWS' 카테고리의 다른 글
AWS SQS, SNS (0) | 2024.07.30 |
---|---|
클라우드는 어떻게 대세가 되었는가 (0) | 2024.07.05 |
AWS로 3 Tier Architecture 고도화하기 (0) | 2024.05.25 |
Elastic Load Balancer (ELB) : (2) (0) | 2023.10.13 |
Elastic Load Balancer (ELB) : (1) (0) | 2023.10.13 |