마이그레이션 노트: Atlas MongoDB => AWS DocumentDB

[원본 링크]

비용/트래픽 최적화, 비용 관리의 용이성을 위해 기존에 Atlas Cloud의 MongoDB를 쓰던 것을 AWS DocumentDB로 옮기게 됐다.

그 히스토리를 대강 정리한 글이다.




버전 호환성 체크

DocumentDB는 MongoDB와의 버전 호환성을 제공한다.
그러니까 DocumentDB 8.0은 MongoDB 8.0과 거의 호환된다는 것이다.
내 경우에는 MongoDB 7.0을 쓰고 있었기 때문에 DocumentDB 8.0으로 옮겼다. DocumentDB에는 7.0이 아예 없기 때문이기도 했다.

상세한 내용은 별도 포스트를 참조한다.
https://docs.aws.amazon.com/ko_kr/documentdb/latest/developerguide/compatibility.html#mongodb-80




DocumentDB 스펙 및 비용

DocumentDB는 비용 책정 기준이 꽤 독특하다.
I/O 비용을 별도로 받는 타입이 있고, I/O 비용이 포함된 완전 프로비저닝 버전이 있다.

t4g.medium를 예로 들면, EC2는 시간당 비용이 $0.122인데, DocumentDB로는 I/O 별도 DB가 $0.317, I/O 포함 DB가 $0.349다. 둘 다 거의 2.5배에 가까운 금액이다.


그리고 I/O 포함은 디스크 비용도 3배나 비싸다.
그럼에도 불구하고, 내 경우에는 I/O가 매우 빈번하고 방대하기 때문에 I/O 포함 버전으로 선택했다. I/O 때문에 비용이 무한대로 확장되는 것은 리스크가 너무 컸다.


그리고 기존에는 2 cpu/8GB가 3개짜리인 클러스터를 썼는데, 마스터가 쓰기를 적당히 버티긴 했지만 읽기가 몰리면 특정 노드에서 peak가 종종 치곤 했다. 그래서 단일노드로 구성하되, 단일노드의 스펙은 더 크게 선택했다.
결국 선택한 타입은 db.r8g.xlarge(4 cpu, 32GB)이었다. 이건 특히 RI도 가능했다.




마이그레이션 계획

다운타임을 최소화한 채로 중단 없이 옮기기를 원했다.
그래서 기본 논리에 따라서 점진적 교체 방법을 사용했다.

새로운 DB를 먼저 띄우고


서버가 write를 할 때 양쪽에 다 동일한 값으로 쓰도록 배포한다.
2가지에 다 쓰기에 성공했을 때만 성공으로 처리하게 하는 이른바 dual write 방식이다. 물리적 복제나 논리적 복제보다 이게 더 확실하다.


이미 양쪽에 다 쓰고 있다면, 부족한 것은 dual write를 배포하기 전에 있던 old data들이다.
이건 수동으로 마이그레이션을 해줘야 한다.

이를 위해서 별도의 마이그레이션 도구를 구현해서 돌렸다.
https://blog.naver.com/sssang97/224211981013
전체 테이블 데이터들을 전부 읽어서 새 DB에 insert해주는 것이었다. 만약 id 충돌이 난다면 이미 dual write를 통해서 잘 동기화된 것이라는 뜻이니, skip하고 없는 것만 옮기도록 했다.

마이그레이션이 다 돌었다면 그때부터는 read까지 전면적인 교체가 가능해야 한다.

그렇게 해서 최종 교체까지 완료했다.

전반적으로 잘 동작하고, 큰 문제까지는 없었다.




이슈 1: 인덱스 크기 문제

AWS DocumentDB는 인덱싱 기능을 더욱 정교하게 지원한다.
예를 들어, Object 타입 컬럼에 대해서 인덱스를 걸면 그 내부 필드들에 대해서도 영리하게 인덱스를 걸어주는데, 그 때문에 인덱스 크기가 MongoDB보다 훨씬 커질 수 있다.

그래서 동일한 인덱스 구성에 동일한 데이터를 넣어도 인덱스 크기가 지나치게 커져서 성능 문제가 생기거나 그냥 INSERT가 실패할 수 있는 것이다. 인덱스로 들어가는 값에는 상식적인 크기 제한이 존재한다.

내 경우에는 일부가 사용 빈도가 낮은 인덱스라서 제거했고, 나머지 인덱스는 다른 형태로 치환해서 해결했다.




이슈 2: Collation 호환성

DocumentDB는 Collation에 기반한 인덱싱을 지원하긴 하는데, 미묘하게 다른게 있고 안맞는 것도 좀 있다.
(단, DocumentDB 8.0에서만 지원)


기존에 있던 이런 인덱스를 옮기려고 했는데,


이미 인덱스가 있다고 오류가 난 것이다.
그러니까, target에서 저 컬럼들이 조합된 인덱스가 있다고 튕기는건데, MongoDB의 경우에는 컬럼 조합이 같아서 collation이 다르면 다른 조합으로 인정해줬다.

AWS 얘들이 좀 이상하게 만들어놔서 다른 옵션이 달라도 컬럼이 같으면 같다고 치기도 한다.

유일한 해결법은, 그냥 컬럼 순서를 바꾸든 후행 컬럼을 추가하든 해서 유일성 조건을 회피하는 것이었다.
이거 개선하긴 하려나?




이슈 3: $elemMatch 동작 미호환 문제

내 경우에는 필터링 조건을 복잡하게 주는 경우가 많았는데, 세부적인 부분에서 동작이 다른 부분이 있었다.
대표적인 것이 elemMatch다.

이건 배열에 대해서 배열 안에 들어있는 필드를 조건으로 거는건데, 원래 MongoDB 7에서는 빈 객체를 넣어도, 관용적으로 처리했다.

조건이 없으므로 사실상 프리패스인 조건절처럼 동작한 것이다.


하지만 DocumentDB는 보다 엄격하게 처리한다. 진짜로 빈 객체가 들어있는 것만 통과시켜버리는 것이다.

이건 코드를 더 명확한 방향으로 수정했다. 조건이 없으면 그냥 안 넣는 것으로




확실히 개선된 점

**IOPS 제한이 없다. **
옵션에 따른 것이긴 한데, 내 경우에는 I/O 포함 디스크 옵션으로 넣어서 I/O 많이 쓴다고 스로틀링이 걸릴 일은 없게 됐다. 이전에는 굉장히 치명적인 장애 지점이었다.

**클러스터 관리를 AWS에서 책임진다. **
Atlas MongoDB는 자기들 말로는 고가용성 클러스터라고 하는데, 실제로는 가용성 수준이 낮다. 그에 비하면 신뢰성 수준이 더 낫다.

**디스크 확장이 매우 자유롭다. **
DocumentDB는 AWS AuroraDB나 HBase처럼 디스크 계층이 인스턴스와 별도로 관리되는 시스템이다.
그래서 디스크 크기를 지정하지 않아도 S3처럼 무한히 자동으로 확장되는 특성을 가진다.

**인덱스 성능 **
이건 일장일단이 있긴 한데, DocumentDB가 더 복잡하고 세부적인 인덱스 튜닝이 가능하다. 특히 Object가 들어가는 경우에 자동으로 그 필드들에 대해서 재귀적 인덱싱을 해준다.

스펙 결정의 용이함
MongoDB에서는 M10, M20, M30 같은 식으로 깜깜이식 스펙 결정만이 가능했다. 다음과 같은 식으로 조합을 한정적으로 정해놓은 것이다.

그래서 가성비 좋은 arm CPU를 쓰지도 못하고, 16GB를 쓰려면 무조건 vcpu 4개 짜리로 강매당한다.

DocumentDB에서는 좀 더 유연한 성능 선택이 가능하다. 특히 graviton 4 arm 인스턴스를 쓸 수도 있고, cpu 개수 대비 메모리가 더 큰 편이다.

**RI를 통한 비용 절감 **
DocumentDB는 RI로 할인을 받는 것이 가능하다. 타입도 제한적이고, 할인률도 20% 정도지만, 그래도 없는 것보단 낫다.

https://aws.amazon.com/ko/savingsplans/database-pricing/





별로인 점

전반적으로는 많이 나아졌지만, 좋은 점만 있는 것은 아니었다.

**악화된 쓰기 처리량 **
MongoDB를 비롯한 LSM Tree DB들의 장점은 write 처리량이 매우 뛰어나다는 것이다. 근데 DocumentDB는 그만큼 쓰기 최적화를 잘 하진 못한 것 같다.
기존의 MongoDB은 쓰기량이 많은 시스템인데도 2 cpu짜리 master node로 잘 버텼었는데, DocumentDB는 2 cpu로 버티질 못했다.

Update Query에서 CPU가 엄청 튀고 I/O를 과다하게 소모했다.
arm/graviton4이라 실제 물리 코어량이 많고 메모리도 더 큰데도 버티질 못한 것이다. 그래서 결국 4 cpu로 늘리니까 감당이 됐다.
일설에는 MySQL을 내부 엔진으로 쓴다는 썰을 봤었는데, 정확하게는 알지 못해도 엔진 성능이 WiredTiger보다 확연하게 좋은건 아닌거같다.

이런 이유에서 비용을 생각만큼은 줄이지 못했다.



참조
https://docs.aws.amazon.com/dms/latest/sbs/chap-mongodb2documentdb.html
https://docs.aws.amazon.com/ko_kr/documentdb/latest/developerguide/docdb-mvu.html
https://stackoverflow.com/questions/63310526/how-can-i-have-a-case-insensitive-unique-index-in-aws-documentdb