MySQL에서 MongoDB로 마이그레이션

JaeHyeok Kim
테이블링 기술블로그
8 min readSep 26, 2022

--

안녕하세요. 테이블링 백엔드팀의 김재혁입니다.

이번에 테이블링은 테이블링페이 서비스를 시작했습니다. 그 과정에서 기존에 백엔드팀에서 MySQL을 메인 DB로 사용하고 있었는데 MongoDB를 도입하였습니다.

저희 팀에서 테이블링페이라는 서비스 개발과 동시에 MongoDB 마이그레이션을 진행하면서 어떤 문제가 있었는지 위주로 공유해보고자 합니다.

MongoDB를 도입한 이유

기존에도 NoSQL을 사용하고 있었지만 저희 서비스의 메인 DB는 MySQL과 Aurora(읽기 DB)를 사용하고 있습니다.

기존 DB를 사용하면서 새로운 서비스나 작은 기능을 개발할 때마다 RDBMS의 테이블 변경이 자주 일어났습니다. 인기 매장이 테이블링 서비스를 이용하면서 특정 시간대에 DB 쓰기가 집중되는 문제도 자주 발생했습니다.

그리고 기존 DB를 설계한 인원이 회사에 남아있지 않았고 아무런 문서도 없었기 때문에 새로 들어온 인원이 서비스를 파악하는데 어려움이 있었습니다.

앞으로의 서비스를 위해서는 레거시를 지우고 새로운 DB 설계와 문서화가 필요해 보였고 위의 문제점들을 해결할 수 있어야 했습니다.

MongoDB 사용에 의견을 나누는 백엔드팀

MongoDB는 스키마의 유연성과 HA 정식 지원, Javascript에 친화적이었습니다.

MongoDB의 단점으로 트랜잭션 미지원의 문제도 MongoDB는 4.0 버전부터 트랜잭션을 지원했기 때문에 걸림돌이 되지 않았습니다.

위의 이유로 저희 팀은 MongoDB를 선택했고 AWS 기반의 MongoDB Atlas를 이용해서 인프라를 구축했습니다.

실험적으로 MongoDB를 사용해보기로 했기 때문에 작은 프로젝트에서 사용을 해보고 괜찮으면 다음 프로젝트에도 적극 사용하기로 했습니다.

테이블링페이 프로젝트

운영에서 MongoDB를 실험적으로 사용하고 있을 때 테이블링페이 프로젝트를 시작하게 되었습니다.

테이블링페이 프로젝트가 진행이 되면서 해당 프로젝트는 결제만 추가되는 것이 아닌 기존 대기와 예약에 보증금과 주문, 포장 주문의 추가와 기존 앱에 없었던 앱 PUSH와 앱 디자인의 변경 등 여러 변경사항들이 동시에 진행되었습니다.

요구 사항도 많아지고 개발 기간도 늘어나면서 저희 팀은 마이그레이션 계획이 계속 뒤로 미루어지면 안 되기 때문에 테이블링페이 프로젝트를 진행하면서 기존 테이블들의 마이그레이션도 같이 진행했습니다.

테이블링페이 프로젝트 후반부에는 마이그레이션 대상 테이블이 20개가 넘어갔습니다. 그러면서 마이그레이션 순서도 중요해졌는데요. 예를 들어 주문 컬렉션은 메뉴 컬렉션에 관계를 가지고 있도록 설계를 했기 때문입니다.

변경점들이 많다 보니 기존에 계획했던 한 달에 몇 개의 테이블들만 마이그레이션을 진행하면서 서비스도 변경하기로 했던 계획은 힘들어졌습니다. 그래서 테이블링페이 배포에 맞춰서 MongoDB 마이그레이션 작업을 진행하기로 했습니다.

테이블링의 키오스크와 앱 서비스 그리고 사장님들이 사용하는 서비스는 새벽 시간에는 사용이 거의 없다시피 했기 때문에 업데이트를 위한 중단을 해도 문제가 적어 보였습니다.

마이그레이션

MongoSyphon이라는 오픈소스 마이그레이션 툴을 사용하기로 했습니다.

테이블링에서는 개발(Develop), 스테이징(Staging), 운영(Production) 환경에서 여러 서비스들과 DB를 운영하고 있는데요. MongoSyphon은 마이그레이션을 위한 JS 또는 JSON 파일을 작성해야 하는데 테이블링의 DB 환경마다 연결 정보를 변경하는 것은 실수할 위험도 있었고 번거로운 작업이었습니다.

이런 작업은 gulp.js를 사용해서 환경별로 JDBC 연결 정보를 사람이 수정해서 진행하지 않고 자동화해서 MongoSyphon.jar을 편하게 사용할 수 있도록 했습니다.

MongoSyphon을 사용해서 마이그레이션 스크립트를 작성해보고 실행하다 보면 오류가 발생할 수 있습니다. 그런 경우에는 MongoSyphon.log라는 파일에서 오류 내용을 확인할 수 있습니다.

MongoSyphon.log

MongoSyphon으로 마이그레이션을 진행하면서 기존 DB의 잘못된 설계들이 문제가 되었습니다.

  • tinyint(1), tinyint(4)
    MongoSyphon은 MySQL의 컬럼이 tinyint(1)이면 MongoDB에 Bool 타입으로 값이 들어가지만 tinyint(4)인 경우에는 Int32 타입으로 값이 마이그레이션 되었습니다. 기존 DB에서는 tinyint(4) 컬럼을 Bool 역할로 사용한 흔적들이 있어서 이 부분은 MongoDB에서 aggregate를 사용해서 해결했습니다.
  • 날짜와 시간 데이터
    저장된 MongoDB의 시간과 MySQL의 시간이 다른 문제도 있었습니다. 이 문제는 MongoSyphon에서 사용하는 JDBC uri의 serverTimezone을 UTC에서 Asia/Seoul로 변경해서 해결했습니다.

개발 환경과 스테이징 환경에서 마이그레이션을 진행하면서 문제가 없는 줄 알았습니다.

느린 마이그레이션 속도

이제 운영 환경의 데이터를 마이그레이션을 했을 때 시간이 어느 정도 걸리나 측정이 필요했기 때문에 운영 DB의 스냅샷을 만들었습니다. 정확한 시간 측정을 위해서 동일한 성능으로 했습니다.

로컬에서 스크립트를 돌리다가 AWS EC2 인스턴스에서 스크립트를 돌리니 속도가 더 빨라진 것을 확인했고 단일 테이블에서는 초당 8,000건의 속도가 나오는 것을 확인했습니다.

단순한 테이블과 복잡한 테이블의 마이그레이션 시간 차이

반면에 마이그레이션 대상이 되었던 리뷰, 회원 테이블 등은 다른 여러 테이블들의 데이터를 참고해서 마이그레이션을 진행해야 하는 테이블들의 데이터는 몇 백만 건이 되었기 때문에 시간이 오래 걸렸습니다. (회원 테이블에는 약 220만 건의 데이터가 있었습니다.)

속도 문제를 개발 환경과 스테이징 환경에서 찾지 못했던 이유는 데이터가 적었기 때문이었습니다. 그리고 gulp.js로 jar 파일을 실행하면서 MongoSyphon에서 출력되는 로그를 확인하지 못한 것도 원인이었습니다.

그중에서도 대기, 예약을 저장하는 테이블은 마이그레이션 대상 데이터가 약 2,000만 건이 넘어갔고 추가로 다른 여러 테이블들을 조회해서 마이그레이션을 진행해서 너무 오래 걸렸습니다.

MongoSyphon을 수정해서 사용하려고 했지만 JAVA 기반이었기 때문에 Node.js와 TypeScript를 주로 사용하는 저희 팀에서는 한계가 있었습니다. 앞으로도 계속 마이그레이션을 하기 위해서는 익숙한 언어로 마이그레이션 툴을 만드는 게 이점이 많아 보였습니다.

그래서 마이그레이션 시간이 오래 걸리는 테이블들만 MongoSyphon을 사용하지 않고 Node.js로 마이그레이션을 진행했습니다.

그 결과 약 3시간이 걸리던 회원 테이블의 마이그레이션은 약 25분이 걸렸습니다. 하루가 넘게 걸리는 대기, 예약 테이블의 마이그레이션은 AWS Batch를 병렬로 사용하도록 해서 2시간도 되지 않아서 끝낼 수 있었습니다.

하지만 2시간도 너무 길다고 생각했기 때문에 대기, 예약 테이블의 마이그레이션은 테이블링 페이 서비스 배포 이전에 미리 마이그레이션을 하기로 했습니다.

대기, 예약 테이블은 과거 데이터의 데이터 변경이 이루어지지 않았기 때문입니다.

  1. 운영 DB의 스냅샷 생성
  2. 특정 날짜 이전의 데이터들은 미리 MongoDB로의 마이그레이션
  3. 서비스 중단
  4. 추가 데이터 마이그레이션

이 결과 저희는 단순한 구조의 테이블부터 복잡한 구조의 테이블까지 안정적으로 마이그레이션을 완료할 수 있었습니다.

마치며

테이블링 백엔드팀에서는 점진적으로 MongoDB로 마이그레이션을 진행 중에 있습니다. 아직까지는 가장 중요한 도메인이라고 할 수 있는 대기, 예약에서는 쓰기와 수정 작업들은 MySQL과 MongoDB를 둘 다 사용하고 읽기 작업은 MySQL과 MongoDB를 혼용해서 사용하고 있습니다.

지금은 모든 데이터를 옮기지는 못했지만 MongoDB를 사용하는 비용이 현재 MySQL 비용의 절반밖에 되지 않으며 트래픽이 몰리는 시간대에 MongoDB의 응답속도는 MySQL과 비교해서 느리지 않았습니다.

MySQL에서 리뷰 정보를 가져올 때는 JOIN이 필요했는데 MongoDB로의 마이그레이션을 끝내고 JOIN을 사용할 필요가 없어졌습니다. Subset pattern을 참고해서 모델링을 했기 때문에 한 번의 쿼리 작업으로 API 응답에 필요한 정보를 가져올 수 있었습니다.

그리고 MongoDB에서 지원하는 Change Streams, TTL Index를 사용할 수 있게 되었습니다.

마이그레이션을 하면서 운영 환경 스냅샷 DB로 마이그레이션 테스트를 해본 것은 정말 다행이었습니다. 사전에 기존 마이그레이션의 문제점을 알 수 있었고 자체적인 마이그레이션용 서비스를 만들어 운영할 수 있게 되었습니다.

기존 서비스에 MongoDB 도입을 고려하고 있으신 분들에게 위 내용이 도움 되었으면 좋겠습니다! 긴 글 읽어주셔서 감사합니다.

--

--