로그가 곧 데이터베이스인 Aurora (Aurora, SIGMOD 2017)
데이터베이스 엔진이 페이지를 쓰지 않고 redo 로그만 네트워크로 보내며, 페이지 재구성은 여러 가용 영역에 걸친 저장 서비스가 맡는 클라우드 네이티브 관계형 데이터베이스 설계입니다. 6개 사본 쿼럼과 10GB 세그먼트 구조는 지금 Amazon Aurora 저장 계층의 기본 틀입니다.
출처
| 항목 | 내용 |
|---|---|
| 원제 | Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases |
| 저자 | Alexandre Verbitski, Anurag Gupta, Debanjan Saha, Murali Brahmadesam, Kamal Gupta, Raman Mittal 외 4명 (Sailesh Krishnamurthy, Sandor Maurice, Tengiz Kharatishvili, Xiaofeng Bao) |
| 실린 곳 | Proceedings of the 2017 ACM International Conference on Management of Data (SIGMOD '17), 1041-1052쪽 |
| 발표 | 2017년 5월 |
| DOI | https:// |
| 원문 | ACM Digital Library |
| 이용 조건 | 저작권은 저자 보유, 출판권은 ACM 에 허락 (Copyright held by the owner/author(s). Publication rights licensed to ACM). 개인과 강의용 비상업 복제만 허용 |
참고 이 페이지는 원문 번역이 아니라 한국어 해설이에요. 정확한 표현과 수치는 원문을 확인하세요.
한눈에
- 문제: 클라우드에서 계산과 저장을 분리하면 병목은 CPU 나 디스크가 아니라 네트워크가 됩니다. 기존 MySQL 을 그대로 복제하면 한 번의 쓰기가 여러 번의 네트워크 쓰기로 불어납니다.
- 제안: 데이터베이스 엔진은 redo 로그 레코드만 저장 계층으로 보내고, 데이터 페이지를 만드는 일은 분산 저장 서비스가 백그라운드에서 합니다. 저장은 3개 가용 영역(AZ) 에 2개씩, 모두 6개 사본에 쿼럼으로 씁니다.
- 결과: 같은 장비에서 미러링된 MySQL 보다 트랜잭션을 35배 더 처리하면서 트랜잭션당 I/O 는 7.7배 적었습니다(표 1).
- 지금의 의미: 이 구조가 지금 Amazon Aurora 의 MySQL 호환판과 PostgreSQL 호환판이 공유하는 저장 계층의 원형입니다.
왜 나왔나
전통적인 데이터베이스는 데이터 페이지, redo 로그, 바이너리 로그, 이중 쓰기 버퍼 등 여러 종류의 쓰기를 디스크에 남깁니다. 여기에 고가용성을 위해 다른 AZ 의 대기 인스턴스로 미러링하면, 각 쓰기가 순서대로 여러 단계를 거쳐야 하고(그림 2), 그만큼 지연이 쌓이고 흔들립니다. 논문은 이 구조가 사실상 "4개 중 4개" 쓰기 쿼럼처럼 동작해 하나만 느려도 전체가 느려진다고 지적합니다.
저자들은 클라우드에서 계산 노드와 저장 노드가 분리되는 순간 진짜 제약은 네트워크라고 보았습니다. 그렇다면 네트워크로 보내는 양을 줄이고, 동기식으로 기다리는 단계를 없애는 것이 핵심입니다.
핵심 아이디어
규모에서의 내구성 (2절)
대형 함대에서는 디스크, 노드, 네트워크 장애가 늘 배경 소음처럼 일어납니다. 흔한 "3개 중 2개" 쿼럼은 AZ 하나가 통째로 나간 상태에서 다른 노드 하나만 더 고장 나도 쿼럼을 잃습니다. Aurora 는 3개 AZ 에 2개씩 6개 사본을 두고, 쓰기는 6개 중 4개, 읽기는 6개 중 3개를 쿼럼으로 삼습니다. 그러면 AZ 하나를 잃어도 쓰기를 계속할 수 있고, AZ 하나와 노드 하나를 더 잃어도(AZ+1) 데이터를 잃지 않습니다.
볼륨은 10GB 단위 세그먼트로 잘게 나누고, 같은 구간의 세그먼트 6개를 "보호 그룹(PG)" 으로 묶습니다. 세그먼트가 작으니 10Gbps 링크로 약 10초면 복구됩니다. 논문은 고장 확률(MTTF) 을 낮추기보다 복구 시간(MTTR) 을 줄여서 이중 장애가 겹칠 확률을 낮추는 쪽을 택했다고 설명합니다. 같은 장치로 과열된 노드의 세그먼트를 옮기거나, AZ 하나씩 순서대로 패치하는 운영도 쉬워졌습니다.
로그가 곧 데이터베이스 (3절)
가장 중요한 결정은 데이터베이스 인스턴스가 네트워크로 redo 로그 레코드만 보낸다는 것입니다. 체크포인트, 캐시에서 페이지를 내보낼 때, 백그라운드 쓰기 등 어떤 경우에도 엔진은 페이지를 저장 계층에 쓰지 않습니다. 저장 노드가 로그를 받아 필요할 때 페이지를 만들어 냅니다(materialize). 덕분에 충돌 후 복구도 거의 즉시 끝나는데, redo 적용이 평소 백그라운드 작업에 흩어져 있기 때문입니다.
그림 4 는 저장 노드가 하는 일을 8단계로 보여줍니다. 로그를 받아 큐에 넣고, 디스크에 남기고 응답하고, 정렬하며 빠진 레코드를 찾고, 이웃 세그먼트와 가십으로 빈칸을 채우고, 로그를 합쳐 페이지를 만들고, S3 에 백업하고, 오래된 버전을 정리하고, CRC 로 검증합니다. 이 가운데 앞의 두 단계만 사용자 요청 경로에 있고 나머지는 모두 비동기입니다.
로그를 앞으로만 진행시키기 (4절)
합의 프로토콜 없이 일관성을 지키기 위해 Aurora 는 단조 증가하는 로그 순번(LSN) 과 몇 가지 기준점을 씁니다.
- VCL(Volume Complete LSN): 그 이하의 모든 로그가 쿼럼을 만족한 가장 높은 지점
- CPL(Consistency Point LSN): 미니 트랜잭션의 마지막 레코드처럼 잘라도 안전한 지점
- VDL(Volume Durable LSN): VCL 이하에서 가장 높은 CPL. 복구 때는 VDL 위의 로그를 잘라냅니다.
커밋은 비동기입니다. 트랜잭션은 커밋 레코드의 LSN 을 기록해 두고, VDL 이 그 지점을 넘으면 그때 클라이언트에 응답합니다. 작업 스레드는 기다리지 않고 다른 요청을 처리합니다. 2단계 커밋이나 합의 라운드가 필요 없습니다.
읽기도 보통은 쿼럼 읽기를 하지 않습니다. 엔진은 각 세그먼트가 어디까지 받았는지(SCL) 알고 있으므로, 필요한 버전을 가진 세그먼트 하나에서 바로 읽습니다. 최대 15개의 읽기 복제본이 같은 저장 볼륨을 공유하고, 쓰기 인스턴스가 보내는 로그 스트림을 받아 캐시에 있는 페이지만 갱신합니다. 복제 지연은 보통 20ms 이하입니다.
전체 구조 (5절)
엔진은 MySQL/InnoDB 를 갈라 만든 것으로, 읽고 쓰는 경로가 바뀌었습니다. 저장 서비스는 최소 3개 AZ 의 EC2 위에서 돌고, 관리 기능은 Amazon RDS 의 제어 평면을 씁니다. 저장 메타데이터는 DynamoDB 에, 오래 걸리는 작업 조율은 Simple Workflow Service 에 맡겼습니다(그림 5).
실험과 결과
6절의 실험은 r3.8xlarge(vCPU 32개, 메모리 244GB) 인스턴스에서 Aurora 와 MySQL 을 비교합니다.
- 표 1: 30분 SysBench 쓰기 전용 실험에서 Aurora 는 27,378,000 건, 미러링된 MySQL 은 780,000 건의 트랜잭션을 처리했습니다(약 35배). 트랜잭션당 I/O 는 0.95 대 7.4 였습니다.
- 그림 6, 7: 인스턴스 크기에 따라 처리량이 거의 직선으로 늘었고, 가장 큰 크기에서 초당 쓰기 121,000 건, 읽기 600,000 건에 이르렀습니다.
- 표 2: 데이터가 커질수록 차이가 벌어져, 100GB 에서 101,000 대 1,500(67배), 1TB 에서 41,000 대 1,200(34배) 이었습니다.
- 표 3: 동시 연결 5,000 개에서 초당 110,000 대 13,000 의 처리량을 보였습니다.
- 표 4: 초당 쓰기 10,000 건일 때 복제 지연이 Aurora 5.38ms, MySQL 300,000ms 였습니다.
- 고객 사례 (그림 8 에서 11): 한 게임 회사는 응답 시간이 15ms 에서 5.5ms 로 줄었습니다(그림 8). 또 다른 고객 사례(그림 11) 에서는 복제본 4개의 최대 복제 지연이 MySQL 에서 12분까지 치솟던 것과 달리 Aurora 에서는 20ms 아래로 유지되었습니다.
7절의 교훈에서는 수만 개의 스키마를 한 인스턴스에 몰아넣는 SaaS 고객, 페이지 단위로 스키마 버전을 관리해 즉시 끝나는 스키마 변경, 그리고 활성 트랜잭션이 없는 순간을 골라 연결을 끊지 않고 엔진을 바꾸는 무중단 패치(ZDP, 그림 12) 를 소개합니다.
지금 AWS 에서는
- Amazon Aurora 는 MySQL 호환판과 PostgreSQL 호환판 모두 이 논문의 분산 저장 계층 위에서 동작합니다. 3개 AZ 에 걸친 6개 사본, 최대 15개의 Aurora 복제본이 저장 볼륨을 공유하는 구조는 지금 공식 문서의 설명과 같습니다.
- 논문의 무중단 패치(ZDP) 는 Aurora MySQL 의 기능 이름으로 남아 있습니다.
- 저장 계층을 공유한다는 아이디어는 이후 리전 간 복제(Aurora Global Database), 자동 용량 조절(Aurora Serverless) 로 넓어졌습니다. Serverless 의 자원 관리는 Aurora Serverless 논문 에 따로 정리되어 있습니다.
함께 읽기
- RDS 와 Aurora, Aurora DSQL: 이 사이트의 기능 문서
- Aurora 합의 회피 논문 해설: 이 논문의 후속편으로, 쿼럼 멤버십 변경과 복구를 더 자세히 다룹니다.
- Aurora Serverless 논문 해설
- Werner Vogels 의 해설 글