mysql

5 개의 포스트

스마트스토어센터 Oracle에서 MySQL로의 무중단 전환기 (새 탭에서 열림)

네이버 스마트스토어센터는 비즈니스 성장에 따른 Oracle DBMS의 리소스 경합과 라이선스 비용 문제를 해결하기 위해 오픈소스인 MySQL로의 무중단 마이그레이션을 단행했습니다. 10년 이상의 레거시 시스템을 안정적으로 전환하기 위해 '이중 쓰기(Dual Write)' 전략을 채택했으며, 이를 통해 데이터 손실 없는 실시간 동기화와 즉각적인 롤백 가능성을 확보했습니다. 결과적으로 서비스 중단 없이 DB 환경을 성공적으로 전환하며 운영 효율성을 높였습니다. ### 이중 쓰기(Dual Write)를 통한 무중단 전환 전략 * **3단계 전환 프로세스**: 전환 전에는 Oracle을 메인으로 사용하며 MySQL에 백그라운드 쓰기를 수행하고, 데이터 마이그레이션 후에는 MySQL을 메인으로 전환하되 Oracle에 백그라운드 쓰기를 지속하여 정합성을 유지합니다. * **롤백 안정성 확보**: 신규 시스템 배포 후 치명적인 성능 저하나 장애가 발생하더라도, Oracle에 실시간으로 데이터가 쌓이고 있으므로 별도의 복구 작업 없이 즉시 이전 환경으로 복구가 가능합니다. ### JPA 환경에서의 기술적 대응 * **Proxy DataSource 활용**: `datasource-proxy` 라이브러리를 사용하여 Oracle에서 수행되는 쿼리를 가로챈 뒤 MySQL DataSource에서도 동일하게 실행하는 구조를 구축했습니다. * **트랜잭션 분리 및 동기화**: MySQL 쿼리 실패가 메인 트랜잭션(Oracle)에 영향을 주지 않도록 `TransactionSynchronizationManager`를 사용했습니다. Oracle 커밋이 성공한 시점(`afterCommit`)에 모아둔 MySQL 쿼리를 일괄 실행하여 정합성을 맞춥니다. * **엔티티 및 PK 전략 변경**: Oracle의 Sequence 전략을 MySQL의 Identity(Auto-increment)로 변경하고, `columnDefinition` 설정을 통해 Oracle의 VARCHAR2, CLOB 등을 MySQL의 TEXT, LONGTEXT 타입에 맞게 조정했습니다. ### MyBatis 기반의 중앙 집중형 이중 쓰기 구현 * **SqlSession Proxy 적용**: 수천 개의 비즈니스 로직을 수정하는 대신, MyBatis의 `SqlSession`을 프록시로 감싸 쓰기 작업(CUD)이 발생할 때 Oracle과 MySQL 쿼리를 동시에 호출하도록 구현했습니다. * **DBMS별 쿼리 매핑**: Oracle과 MySQL의 SQL 문법 차이를 해결하기 위해 별도의 MySQL용 쿼리 파일을 작성하고, 실행 시점에 Query ID에 접두사(예: `mysql.`)를 붙여 적절한 쿼리를 찾아 실행하는 방식을 사용했습니다. ### 데이터 정합성 검증 및 최종 전환 * **배치 기반 검증**: 두 DB 간의 레코드 카운트와 데이터 해시값을 주기적으로 비교하는 배치 프로그램을 운영하여 미세한 데이터 불일치를 식별하고 수정했습니다. * **기능 토글을 이용한 전환**: ZooKeeper 등을 활용한 설정 변경만으로 메인 DB(Read/Write 주체)를 즉시 교체할 수 있는 환경을 구성하여 배포 없이 안정적으로 전환을 완료했습니다. 이와 같은 전략은 대규모 레거시 시스템에서 DB를 교체해야 할 때, 코드 수정을 최소화하면서도 서비스 안정성을 최우선으로 고려하는 개발자들에게 실무적인 가이드라인을 제공합니다. 특히 트랜잭션 동기화와 프록시 패턴을 활용한 중앙 집중식 제어는 복잡한 시스템 마이그레이션의 위험 부담을 낮추는 핵심 기술 요소입니다.

레거시 결제 원장을 확장 가능한 시스템으로 (새 탭에서 열림)

토스페이먼츠는 20년 된 레거시 결제 원장의 구조적 한계와 도메인 간 강한 결합을 해결하기 위해 MySQL 기반의 신규 원장 시스템을 구축했습니다. 데이터 불변성을 보장하는 INSERT-only 원칙과 이벤트 기반 아키텍처를 도입하여 복합 결제 지원 등 비즈니스 확장성을 확보했습니다. 이 과정에서 발생한 데이터 불일치와 타임아웃 문제를 해결하며 시스템의 자가 회복 능력을 강화하고 안정적인 운영 환경을 마련했습니다. ### 레거시 원장 시스템의 한계와 과제 - **데이터 구조의 불일치:** 결제수단별로 테이블 구조가 다르고, 동일한 성격의 데이터가 서로 다른 테이블에 저장되어 유지보수와 온보딩에 큰 비용이 발생했습니다. - **도메인 간 강한 결합:** 결제, 정산, 회계 등 여러 서비스가 하나의 원장 테이블과 컬럼을 공유하여, 작은 기능 수정 시에도 전사적인 영향도 분석이 필요했습니다. - **구조적 확장성 부족:** 결제와 결제수단이 1:1 관계로 묶여 있어, 더치페이나 복합 결제(카드+포인트)와 같은 현대적인 결제 시나리오를 지원할 수 없었습니다. ### 신규 원장 설계의 3가지 전략 - **데이터 불변성과 일관성:** 모든 승인 내역을 공통 테이블(`approve`)에 저장하고, 수정 대신 INSERT-only 방식을 채택하여 데이터의 정합성을 높이고 데드락을 방지했습니다. - **이벤트 기반의 도메인 분리:** 각 도메인이 직접 DB를 조회하는 대신 Kafka 이벤트를 구독하여 데이터를 처리하게 함으로써 도메인 간 의존성을 제거했습니다. - **결제와 승인 개념의 분리:** '결제'는 주문의 상태를, '승인'은 실제 결제수단의 실행을 의미하도록 분리하여 하나의 결제에 여러 승인 수단이 연결될 수 있는 유연한 구조를 만들었습니다. ### 무중단 마이그레이션 및 정합성 검증 - **비동기 점진적 적재:** 실서비스 장애를 방지하기 위해 기존 원장에 먼저 저장한 후, 신규 원장에는 별도의 ThreadPool을 통한 비동기 방식으로 데이터를 적재했습니다. - **검증 배치 운영:** 비동기 적재 중 발생할 수 있는 누락을 방지하기 위해, 매 5분마다 Read-Only DB를 기반으로 기존 원장과 신규 원장의 데이터를 비교하고 보정하는 배치를 실행했습니다. - **고성능 이관 작업:** 수억 건의 데이터 이관을 위해 Bulk Insert를 도입하고, 네트워크 지연 최소화를 위해 마이그레이션 서버를 DB와 동일한 가용 영역(AZ)에 배치했습니다. ### 운영 중 장애 대응과 시스템 고도화 - **쿼리 최적화:** 옵티마이저의 판단 오류로 발생한 풀 스캔(Full Scan) 문제를 인덱스 힌트(Index Hint) 추가와 롤백 시스템을 통해 빠르게 해결했습니다. - **타임아웃 및 정합성 관리:** MSA 구조에서 서버 간 타임아웃 설정을 일치시키고, 외부 원천사와의 상태 불일치를 해결하기 위한 망취소(Network Cancellation) 로직을 강화했습니다. - **이벤트 처리의 신뢰성:** 아웃박스(Outbox) 패턴과 로그 기반 복구를 통해 이벤트 누락을 방지하고, 헤더에 멱등키를 포함해 중복 이벤트 처리 문제를 해결했습니다. 신규 시스템으로의 전환은 단순한 DB 교체가 아니라 시스템의 지속 가능성을 확보하는 과정입니다. 초기 설계의 완벽함보다 중요한 것은 운영 중 발생하는 예외 상황에 시스템이 스스로 대응하고 회복할 수 있는 '자가 회복 구조'를 갖추는 것이며, 이를 위해 데이터 보정 배치와 로깅 시스템 같은 안전장치를 반드시 고려해야 합니다.

6개월 만에 연간 수십조를 처리하는 DB CDC 복제 도구 무중단/무장애 교체하기 (새 탭에서 열림)

네이버페이는 차세대 아키텍처 개편 프로젝트인 'Plasma'의 최종 단계로, 연간 수십조 원의 거래 데이터를 처리하는 DB CDC 복제 도구인 'ergate'를 성공적으로 개발하여 무중단 교체했습니다. 기존의 복제 도구(mig-data)가 가진 유지보수의 어려움과 스키마 변경 시의 제약 사항을 해결하기 위해 Apache Flink와 Spring Framework를 조합한 새로운 구조를 도입했으며, 이를 통해 확장성과 성능을 동시에 확보했습니다. 결과적으로 백엔드 개발자가 직접 운영 가능한 내재화된 시스템을 구축하고, 대규모 트래픽 환경에서도 1초 이내의 복제 지연 시간과 강력한 데이터 정합성을 보장하게 되었습니다. ### 레거시 복제 도구의 한계와 교체 배경 * **유지보수 및 내재화 필요성:** 기존 도구인 `mig-data`는 DB 코어 개발 경험이 있는 인원이 순수 Java로 작성하여 일반 백엔드 개발자가 유지보수하거나 기능을 확장하기에 진입 장벽이 높았습니다. * **엄격한 복제 제약:** 양방향 복제를 지원하기 위해 설계된 로직 탓에 단일 레코드의 복제 실패가 전체 복제 지연으로 이어졌으며, 데이터 무결성 확인을 위한 복잡한 제약이 존재했습니다. * **스키마 변경의 경직성:** 반드시 Target DB에 칼럼을 먼저 추가해야 하는 순서 의존성이 있어, 작업 순서가 어긋날 경우 복제가 중단되는 장애가 빈번했습니다. * **복구 프로세스의 부재:** 장애 발생 시 복구를 수행할 수 있는 인원과 방법이 제한적이어서 운영 효율성이 낮았습니다. ### Apache Flink와 Spring을 결합한 기술 아키텍처 * **프레임워크 선정:** 저지연·대용량 처리에 최적화된 **Apache Flink(Java 17)**를 복제 및 검증 엔진으로 채택하고, 복잡한 비즈니스 로직과 복구 프로세스는 익숙한 **Spring Framework(Kotlin)**로 이원화하여 구현했습니다. * **Kubernetes 세션 모드 활용:** 12개에 달하는 복제 및 검증 Job을 효율적으로 관리하기 위해 세션 모드를 선택했습니다. 이를 통해 하나의 Job Manager UI에서 모든 상태를 모니터링하고 배포 시간을 단축했습니다. * **Kafka 기반 비동기 처리:** nBase-T의 binlog를 읽어 Kafka로 발행하는 `nbase-cdc`를 소스로 활용하여 데이터 유실 없는 파이프라인을 구축했습니다. ### 데이터 정합성을 위한 검증 및 복구 시스템 * **지연 컨슈밍 검증(Verifier):** 복제 토픽을 2분 정도 지연하여 읽어 들이는 방식으로 Target DB에 데이터가 반영될 시간을 확보한 뒤 정합성을 체크합니다. * **2단계 검증 로직:** 1차 검증 실패 시, 실시간 변경으로 인한 오탐인지 확인하기 위해 Source DB를 직접 재조회하여 Target과 비교하는 보완 로직을 수행합니다. * **자동화된 복구 흐름:** 일시적인 오류는 5분 후 자동으로 복구하는 '순단 자동 복구'와 배치 기반의 '장애 자동 복구', 그리고 관리자 UI를 통한 '수동 복구' 체계를 갖추어 데이터 불일치 제로를 지향합니다. ### DDL 독립성 및 성능 개선 결과 * **스키마 캐싱 전략:** `SqlParameterSource`와 캐싱된 쿼리를 이용해 Source와 Target의 칼럼 추가 순서에 상관없이 복제가 가능하도록 개선했습니다. Target에 없는 칼럼은 무시하고, 있는 칼럼만 선별적으로 반영하여 운영 편의성을 극대화했습니다. * **성능 최적화:** 기존 대비 10배 이상의 QPS를 처리할 수 있는 구조를 설계했으며, CDC 이벤트 발행 후 최종 복제 완료까지 1초 이내의 지연 시간을 달성했습니다. * **모니터링 강화:** 복제 주체(ergate_yn)와 Source 커밋 시간(rpc_time)을 전용 칼럼으로 추가하여 데이터의 이력을 추적할 수 있는 가시성을 확보했습니다. 성공적인 DB 복제 도구 전환을 위해서는 단순히 성능이 좋은 엔진을 선택하는 것을 넘어, **운영 주체인 개발자가 익숙한 기술 스택을 적재적소에 배치**하는 것이 중요합니다. 스트림 처리는 Flink에 맡기고 복잡한 복구 로직은 Spring으로 분리한 ergate의 사례처럼, 도구의 장점을 극대화하면서도 유지보수성을 놓치지 않는 아키텍처 설계가 대규모 금융 플랫폼의 안정성을 뒷받침합니다.

일 평균 30억 건을 처리하는 결제 시스템의 DB를 Vitess로 교체하기 - 2. 개발 및 운영기 (새 탭에서 열림)

LINE Billing Platform 팀은 일 평균 30억 건의 요청을 처리하는 대규모 결제 시스템을 운영하기 위해 기존 Nbase-T에서 Vitess로 성공적인 데이터베이스 마이그레이션을 수행했습니다. 이 글에서는 성능 문제와 개발 편의성을 고려해 gRPC 대신 MySQL 프로토콜을 선택한 과정과 효율적인 데이터 처리를 위한 샤딩 전략을 상세히 다룹니다. 또한 VTOrc와 Prometheus를 활용한 자동 복구 및 모니터링 체계를 구축하여 분산 데이터베이스 환경에서도 높은 안정성을 확보한 실무 노하우를 공유합니다. ### 프로토콜 선정 및 개발 환경 구축 * VTGate는 gRPC와 MySQL 프로토콜을 모두 지원하지만, gRPC 사용 시 `http2: frame too large` 에러와 CPU 오버헤드가 발생하여 최종적으로 MySQL 프로토콜을 채택했습니다. * Java 클라이언트 사용 시 gRPC 프로토콜은 쿼리 결과를 객체로 변환하는 과정이 번거롭고 Vitess 측에서도 현재 MySQL 프로토콜 사용을 권장하고 있습니다. * 익숙한 MySQL 프로토콜을 사용함으로써 기존 개발 경험을 유지하면서도 Vitess의 샤딩 기능을 안정적으로 활용할 수 있게 되었습니다. ### 키스페이스 설계 및 데이터 처리 방식 * 시스템은 크게 두 개의 키스페이스로 분리되어 있습니다. '글로벌 키스페이스'는 단일 샤드로 구성되어 자동 증가(Auto-increment)하는 샤딩 키를 관리합니다. * 실제 데이터가 저장되는 '서비스 키스페이스'는 N개의 샤드로 분산되어 있으며, 코인 잔액 및 충전/사용 내역 등의 데이터를 저장합니다. * 서비스 키스페이스는 'Hash Vindex'를 사용하여 데이터를 균등하게 분산하며, 애플리케이션이 쿼리에 샤딩 키를 포함하면 VTGate가 해당 샤드를 자동으로 특정해 효율적인 요청 처리가 가능합니다. ### MySQL 호환성 및 주요 기능 활용 * 트랜잭션 격리 수준은 단일 샤드일 경우 `REPEATABLE READ`, 다중 샤드일 경우 `READ COMMITTED`가 적용됩니다. * Vitess는 MySQL 프로토콜을 지원하지만 일부 쿼리 제약 사항이 존재하므로, `unsupported_cases.json`을 통해 사전에 호환성을 확인해야 합니다. * 분산 샤드 간 트랜잭션을 지원하는 'Two-Phase Commit(2PC)' 기능과 쿼리 실행 계획을 분석하는 'VEXPLAIN/VTEXPLAIN' 등을 통해 분산 환경의 제약을 보완하고 있습니다. ### 안정적인 운영을 위한 모니터링 및 장애 복구 * 자동 복구 도구인 'VTOrc'를 도입하여 토폴로지 서버와 VTTablet의 데이터를 기반으로 문제를 자동 감지하고 복구합니다. * Prometheus를 통해 VTOrc의 지표(Metrics)를 수집하며, 장애 발생 시 이메일과 Slack으로 알람이 전달되도록 구성했습니다. * VTAdmin 웹 UI를 활용해 복구 내역을 시각적으로 확인하고, `tablet_alias`를 통해 문제가 발생한 MySQL 노드를 즉각적으로 식별하여 운영 효율성을 높였습니다. 대규모 분산 환경에서 Vitess를 도입할 때는 성능과 유지보수를 위해 gRPC보다는 MySQL 프로토콜 사용을 우선적으로 고려하는 것이 좋습니다. 또한 단일 샤드와 다중 샤드 간의 트랜잭션 격리 수준 차이 및 쿼리 제약 사항을 면밀히 검토하여 애플리케이션 로직을 설계해야 하며, VTOrc와 같은 도구를 적극 활용하여 고가용성 운영 체계를 구축하는 것이 중요합니다.

일 평균 30억 건을 처리하는 결제 시스템의 DB를 Vitess로 교체하기 - 1. 솔루션 선정기 (새 탭에서 열림)

LINE 결제 플랫폼 팀은 라이선스 비용 절감과 시스템 확장성 확보를 위해 기존 Nbase-T 시스템을 오픈소스 데이터베이스 클러스터링 솔루션인 Vitess로 마이그레이션하기로 결정했습니다. Apache ShardingSphere, TiDB 등 다양한 분산 DB 솔루션을 대상으로 성능과 운영 편의성을 비교 분석한 결과, 대규모 트래픽 환경에서 검증된 안정성과 고가용성을 제공하는 Vitess가 최종 후보로 선정되었습니다. 이번 과정은 결제 시스템이라는 특수성에 맞춰 서비스 중단 없는 전환과 물리 서버 환경에서의 최적화 가능성을 검증하는 데 주력했습니다. ### 후보 솔루션별 특징 및 제외 사유 * **Apache ShardingSphere**: 프락시(Proxy)와 JDBC 레이어 방식을 모두 지원하여 유연한 아키텍처 구성이 가능하지만, 데이터가 각 샤드에 고르게 분배되지 않을 경우 리샤딩(리밸런싱) 기능을 직접 구현해야 한다는 치명적인 단점이 있어 후보에서 제외되었습니다. * **TiDB**: MySQL 호환 분산 SQL DB로, SQL 계층(TiDB), 메타데이터 관리(PD), 행/열 기반 저장소(TiKV/TiFlash)로 분리된 구조를 가집니다. 샤딩키 설정 없이도 데이터를 자동 리밸런싱하여 운영 비용을 낮출 수 있다는 장점이 있어 유력한 후보로 PoC를 진행했습니다. * **Vitess**: YouTube에서 개발된 CNCF 프로젝트로, 샤딩 기술을 통해 수평 확장을 지원하며 베어 메탈 환경 설치가 가능해 결제 시스템에 필요한 높은 수준의 안정성을 확보할 수 있습니다. ### Vitess의 구조적 장점과 컴포넌트 역할 * **VTGate**: 클라이언트의 쿼리를 적절한 샤드로 라우팅하고 분산 트랜잭션을 처리하며, 애플리케이션에는 단일 DB처럼 보이도록 추상화 레이어를 제공합니다. * **VTTablet 및 VTorc**: 각 MySQL 인스턴스 앞에 위치하여 쿼리 실행과 복제를 관리하며, VTorc를 통해 장애 발생 시 자동으로 장애 조치(Failover)를 수행하여 고가용성을 유지합니다. * **토폴로지 서버**: ZooKeeper나 etcd를 활용해 클러스터의 구성 정보와 노드 상태를 중앙에서 관리함으로써 분산 환경의 일관성을 보장합니다. ### PoC를 통한 성능 및 운영 환경 검증 * **환경 일치화**: 실제 결제 시스템과 동일한 사양의 장비와 테이블 구조를 설정하여 Nbase-T, Vitess, TiDB 간의 성능 비교를 수행했습니다. * **성능 테스트 결과**: 순수 성능(TPS 및 CPU 효율) 관점에서는 기존 Nbase-T가 가장 우수했으나, Vitess 역시 대규모 요청 상황에서 안정적인 리소스 처리 능력을 보여주었습니다. * **유연한 설정**: Vitess는 필요에 따라 조회와 입력을 프라이머리와 레플리카로 분리하는 기능을 지원하며, 모든 DB 노드에 일괄적인 DDL 수행이 가능하여 관리 효율성이 높음을 확인했습니다. 결제와 같이 높은 신뢰성이 요구되는 시스템을 마이그레이션할 때는 단순한 쿼리 처리 성능뿐만 아니라 자동 장애 복구(Failover) 능력과 데이터 리샤딩의 용이성을 우선적으로 고려해야 합니다. Vitess는 글로벌 대형 서비스들에서 이미 안정성이 검증되었고, 베어 메탈 환경에서도 유연한 최적화가 가능하므로 대규모 MySQL 클러스터 운영이 필요한 조직에 강력히 추천되는 솔루션입니다.