MongoDB 체인지 스트림

MongoDB 체인지 스트림 (Change Streams)

체인지 스트림은 애플리케이션이 데이터의 실시간 변경을 구독하도록 해줘요. 옛날처럼 oplog를 직접 뒤져가며 테일링하는 복잡함과 위험 없이, 컬렉션 하나든 데이터베이스 전체든 배포 전체든 데이터 변경 이벤트를 받아 즉시 반응할 수 있답니다. 체인지 스트림이 집계 프레임워크를 기반으로 하기 때문에, 특정 변경만 걸러 보거나 알림의 형태를 자유롭게 변형할 수도 있어요.

출처: MongoDB Change Streams (공식 문서)

본문

어디서 쓸 수 있을까요

체인지 스트림은 복제 세트와 샤딩 클러스터에서 사용할 수 있어요. 다만 몇 가지 조건이 붙는데, WiredTiger 스토리지 엔진을 써야 하고, 복제 세트 프로토콜 버전 1(pv1)이어야 해요. read concern majority 지원은 켜져 있어도(기본값) 꺼져 있어도 사용할 수 있어요.

중요한 제약이 하나 있어요. 시계열 컬렉션은 문서 단위가 아니라 최적화된 저장 형식을 쓰기 때문에 체인지 스트림을 지원하지 않아요.

무엇을 감시할 수 있나요

체인지 스트림은 대상에 따라 세 가지로 열 수 있어요.

대상 설명
컬렉션 단일 컬렉션에 대한 체인지 스트림 커서를 열 수 있어요. 다만 system 컬렉션이나 admin, local, config 데이터베이스의 컬렉션은 제외돼요.
데이터베이스 한 데이터베이스 안의 모든 비-system 컬렉션의 변경을 감시해요. admin, local, config는 제외돼요.
배포 복제 세트든 샤딩 클러스터든 배포 전체의 변경을 감시해요. 역시 admin, local, config를 제외한 모든 데이터베이스가 대상이에요.

성능 고려사항

한 데이터베이스에 열려 있는 활성 체인지 스트림이 연결 풀 크기를 넘어서면 알림 지연이 생길 수 있어요. 체인지 스트림 하나가 다음 이벤트를 기다리는 동안 getMore 연산으로 연결을 하나 잡고 있기 때문이에요. 지연을 피하려면 maxPoolSize를 열려 있는 체인지 스트림 수보다 크게 잡아줘야 해요.

샤딩 클러스터에서 주의할 점이 있어요. mongos가 각 샤드에 개별 체인지 스트림을 만들고, 그 결과를 정렬·필터링하며 필요하면 fullDocument 조회도 수행해요. 성능을 위해 체인지 스트림 안에서는 $lookup 쿼리를 아껴 쓰는 게 좋아요.

체인지 스트림 열기

복제 세트에서는 데이터를 담고 있는 아무 멤버에서 열면 되고, 샤딩 클러스터에서는 mongos에서 열면 돼요. 커서를 반복(iterate)해서 변경 이벤트 문서를 받아와요. 커서는 명시적으로 닫거나, 연결이 끊기거나 타임아웃되거나, 샤드 제거로 닫히기 전까지 계속 열려 있어요.

출력 변경하기

체인지 스트림 이벤트 문서의 _id 필드는 리줌 토큰(resume token) 역할을 해요. 파이프라인으로 이 _id 필드를 수정하거나 제거하면 안 돼요. MongoDB 4.2부터는 파이프라인이 이벤트의 _id를 수정하면 예외를 던져요.

업데이트 연산의 전체 문서 조회

기본적으로 체인지 스트림은 업데이트 연산의 필드 델타만 반환해요. 대신 fullDocument: "updateLookup"을 설정하면 업데이트된 문서의 가장 최신 majority-커밋 버전을 돌려받을 수 있어요.

주의할 점이 있어요. 빠른 삭제가 일어나거나 트래픽이 급증하는 상황에서 fullDocument: "updateLookup"$match 필터와 함께 쓰면 'Resume Token Not Found' 오류가 날 수 있어요. 문서가 삭제되면 fullDocument가 null이 되면서 리줌 토큰을 찾지 못하기 때문이에요. 그런 경우에는 fullDocumentBeforeChange: "whenAvailable"fullDocument: "whenAvailable"로 문서의 pre/post 이미지를 쓰는 걸 권장해요.

리줌하기

체인지 스트림은 커서를 열 때 resumeAfter 또는 startAfter에 리줌 토큰을 지정해 이어갈 수 있어요. 리줌할 때는 토큰을 만든 시점과 같은 파이프라인과 옵션을 그대로 써야 해요. 다르게 쓰면 예측할 수 없는 동작, 데이터 일관성 저하, 이어가기 실패가 생길 수 있어요.

pre/post 이미지와 보존 정책

문서의 변경 전·후 이미지를 함께 받고 싶다면 changeStreamOptionsexpireAfterSeconds를 설정해 보존 시간을 정할 수 있어요. off로 두면 기본 정책대로, 해당 체인지 스트림 이벤트가 oplog에서 사라질 때까지 이미지를 보관해요. 이벤트가 oplog에서 제거되면 대응하는 이미지도 함께 삭제돼요.

pre/post 이미지를 켜면 저장 공간을 쓰고 처리 시간이 늘어나요. 꼭 필요할 때만 켜는 게 좋아요. 체인지 스트림 이벤트 크기는 16 mebibytes 미만으로 유지하는 걸 권장해요.

pre-image를 요청하려면 fullDocumentBeforeChangerequiredwhenAvailable로, post-image는 fullDocument로 지정해요. pre-image는 config.system.preimages 컬렉션에 기록돼요. 이 컬렉션이 커질 수 있으니 expireAfterSeconds로 크기를 제한하고, config.system.preimages.totalSize().stats()로 크기를 모니터링할 수 있어요. pre-image는 백그라운드 프로세스가 비동기로 제거해요.

더 알아보기

  • Change Stream Events — 변경 이벤트 문서의 정확한 구조
  • Replication — 복제 세트와 체인지 스트림의 관계
  • Sharding — 샤딩 클러스터에서 체인지 스트림 동작