query-optimization

2 개의 포스트

MongoDB 8.0 업그레이드 해야하는 12가지 이유 - tech.kakao.com (새 탭에서 열림)

MongoDB 8.0은 기존 버전에서 지적받았던 성능상의 아쉬움을 해결하고 안정성을 극대화하는 데 초점을 맞춘 중대한 업데이트입니다. 약 5년의 장기 지원 정책을 도입하여 운영의 지속성을 보장하며, 쓰기 처리량 향상과 쿼리 최적화 등 기술적 아키텍처 개선을 통해 실질적인 성능 이득을 제공합니다. 특히 대규모 트래픽을 처리하는 환경에서 쓰기 지연 시간을 줄이고 복제 효율을 높인 점이 이번 버전의 핵심적인 결론입니다. **장기 지원 정책과 온프레미스 지원 확대** * MongoDB 8.0은 출시 후 5년간(2029년 10월까지) 지원되는 사실상의 LTS(Long-Term Support) 버전으로, 잦은 업그레이드 부담을 줄여줍니다. * 기존에 클라우드(Atlas)에만 우선 적용되던 최신 기능들을 온프레미스 환경에서도 마이너 릴리스를 통해 빠르게 도입할 수 있도록 정책이 변경되었습니다. * 이를 통해 운영 조직은 안정 중심의 운영과 신규 기능 도입 사이에서 유연한 전략을 선택할 수 있는 기반을 마련했습니다. **Write Concern "majority" 성능의 혁신적 개선** * 쓰기 완료 판단 기준을 데이터가 파일에 물리적으로 기록되는 시점(`lastApplied`)에서 Oplog에 기록되는 시점(`lastWritten`)으로 변경했습니다. * 이러한 내부 동작 방식의 변화로 세컨더리 노드의 적용 대기 시간이 단축되어, 쓰기 처리량이 이전 버전 대비 약 30~47% 향상되었습니다. * 세컨더리에서 즉시 읽기 시 발생할 수 있는 데이터 일관성 문제는 '인과적 일관성 세션'을 통해 보완 가능하도록 설계되었습니다. **벌크 쓰기(Bulk Write) 및 Oplog 처리 최적화** * 단일 요청으로 여러 컬렉션에 대한 대량 작업을 동시에 수행할 수 있는 새로운 데이터베이스 명령어가 도입되었습니다. * 기존에 문서마다 개별적으로 생성되던 Oplog 엔트리를 최대 500개까지 하나로 묶어 기록하는 최적화가 적용되었습니다. * 이 개선을 통해 세컨더리 노드의 복제 지연(Replication Lag) 발생 가능성이 크게 낮아지고 전체적인 쓰기 효율이 개선되었습니다. **단건 조회 최적화를 위한 Express Plan 도입** * `_id` 기반의 단건 조회나 유니크 인덱스를 사용하는 쿼리에 대해 복잡한 옵티마이저 과정을 생략하는 'Express Plan'이 추가되었습니다. * 쿼리 파싱 직후 즉시 실행 경로를 확보함으로써 불필요한 플래닝 오버헤드를 제거하고 응답 속도를 극대화했습니다. * 이는 빈번하게 발생하는 PK 기반 조회의 효율을 높여 전체 시스템의 리소스 소모를 줄여주는 효과를 제공합니다. MongoDB 8.0은 성능 저하에 대한 우려를 불식시키기 위해 아키텍처 수준의 최적화를 대거 반영한 버전입니다. 5년이라는 긴 지원 기간과 가시적인 성능 향상을 고려할 때, 대규모 분산 환경을 운영하는 조직이라면 안정화 기간을 거친 후 8.0으로의 업그레이드를 적극적으로 검토할 것을 추천합니다. 특히 쓰기 성능 병목이나 복제 지연 문제를 겪고 있는 서비스에 강력한 해결책이 될 것입니다.

레거시 정산 개편기: 신규 시스템 투입 여정부터 대규모 배치 운영 노하우까지 (새 탭에서 열림)

토스페이먼츠는 20년 동안 운영되어 온 레거시 정산 시스템의 한계를 극복하기 위해 대대적인 개편을 진행했습니다. 거대하고 복잡한 단일 쿼리 기반의 로직을 객체지향적인 코드로 분산하고, 데이터 모델링을 집계 중심에서 거래 단위로 전환하여 정산의 정확성과 추적 가능성을 확보했습니다. 이를 통해 폭발적인 거래량 증가에도 대응할 수 있는 고성능·고효율의 현대적인 정산 플랫폼을 구축하는 데 성공했습니다. ### 거대 쿼리 중심 로직의 분할 정복 * **문제점:** 수많은 JOIN과 중첩된 DECODE/CASE WHEN 문으로 이루어진 거대한 공통 쿼리가 모든 비즈니스 로직을 처리하고 있어 유지보수와 테스트가 매우 어려웠습니다. * **도메인 및 기능 분리:** 거대 쿼리를 분석하여 도메인별, 세부 기능별로 카테고리를 나누는 분할 정복 방식을 적용했습니다. * **비즈니스 규칙의 가시화:** 복잡한 SQL 로직을 Kotlin 기반의 명확한 객체와 메서드로 재구성하여, 코드상에서 비즈니스 규칙이 명확히 드러나도록 개선했습니다. * **점진적 전환:** 기능을 단위별로 분할한 덕분에 전체 시스템 개편 전이라도 특정 기능부터 우선적으로 새 시스템으로 전환하며 실질적인 가치를 빠르게 창출했습니다. ### 데이터 모델링 개선을 통한 추적 가능성 확보 * **최소 단위 데이터 관리:** 기존의 집계(Sum) 기반 저장 방식에서 벗어나 모든 거래를 1:1 단위로 관리함으로써, 오류 발생 시 원인 추적을 용이하게 하고 데이터 재활용성을 높였습니다. * **설정 정보 스냅샷 도입:** 계산 결과와 함께 당시의 계약 조건(수수료율 등)을 스냅샷 형태로 저장하여, 시간이 지나도 과거의 정산 맥락을 완벽히 복원할 수 있게 했습니다. * **상태 기반 재처리:** 거래별로 독립적인 상태를 기록하는 설계를 통해, 장애 발생 시 전체 재처리가 아닌 실패한 건만 선별적으로 복구할 수 있도록 효율화했습니다. ### 고해상도 데이터 대응을 위한 DB 최적화 * **파티셔닝 및 인덱스 전략:** 정산일자 기준의 Range 파티셔닝과 복합 인덱스를 활용해 데이터량 증가에 따른 조회 성능 저하를 방지했습니다. * **조회 전용 테이블 및 데이터 플랫폼 활용:** 실시간 조회가 필요한 핵심 기능은 전용 테이블로 대응하고, 복잡한 어드민 통계는 고성능 데이터 플랫폼에 위임하여 시스템 부하를 분산했습니다. ### 배치 시스템 성능 극대화 * **I/O 횟수 최소화:** 배치 실행 시점에 가맹점 설정 정보를 전역 캐시에 로드하여 반복적인 DB 조회를 제거했습니다. * **Bulk 조회 및 처리:** Spring Batch의 `ItemProcessor`에서 개별 건별로 I/O가 발생하지 않도록 Wrapper 구조를 도입하여 묶음 단위(Bulk)로 조회하도록 개선했습니다. * **멀티 스레드 활용:** `AsyncItemProcessor`와 `AsyncItemWriter`를 도입하여 단일 스레드 제약을 극복하고 처리 속도를 비약적으로 향상시켰습니다. 이번 개편은 단순히 기술적인 스택을 바꾸는 것을 넘어, 레거시 시스템에 숨겨진 복잡한 비즈니스 맥락을 명확한 도메인 모델로 추출해냈다는 점에서 큰 의미가 있습니다. 대규모 트래픽과 복잡한 정산 규칙을 다루는 시스템이라면, 데이터를 최소 단위로 관리하고 I/O 최적화와 캐싱을 적극적으로 활용하는 아키텍처를 검토해볼 것을 추천합니다.