백앤드 개발자가 DB 마이그레이션을 해야할 때 가슴에 새겨야 할 7가지 규칙

박재현·2022년 9월 2일
3
post-thumbnail

글을 쓰기 앞서, 이 글은 주니어 개발자들에게 도움이 될 수 있는 글임을 말씀드립니다. 미드/시니어 수준의 개발자들은 당연히 알고계실 지식들이라 별 도움이 되지 않을 예정입니다.

백앤드 개발자라면 자주 겪게되는 마이그레이션 업무, 어떻게 잘 할 수 있을까요? 저도 여러번 마이그레이션을 진행 해봤지만 회사마다, 서비스 성향마다 매번 다른 형태로 마이그레이션을 수행해왔던것 같습니다.

하지만 공통된 필수 체크 사항들은 매번 비슷하게 느껴졌던것 같은데요, 제 경험을 토대로 저와 같은 고민을 하는 개발자들에게 꿀팁이 될 수 있는 몇가지 습관들을 이야기 해드려 보려합니다.

첫번째. 마이그레이션의 목적과 요구사항 분석을 명확하게 수행할 것

마이그레이션을 시작하기 전에 우선적으로 마이그레이션이 왜 필요한지, 어떤 범위에서 필요한지 본인이 명확하게 파악하셔야 합니다. 요구사항 분석이라고 하죠? 대표적으로 아래 정도 수준이라고 생각하는데요

  • 왜 마이그레이션이 진행되어야 하는지, 하지 않고 해결할 수 있는 방법이 있는지?
  • 해야 한다면 대상 행의 개수가 몇개 정도인지
  • 어느정도의 시간 안에 마이그레이션이 이루어져야하는지
  • 서비스 중단이 필요한 마이그레이션인지

두번째. 사이트 이펙트 발생 가능 범위를 명확하게 파악할 것

마이그레이션을 했을때 직접적인 영향을 받는 대상 도메인 뿐만 아니라, 영향을 받을 수도 있는 의존된 도메인 그리고 연관된 기존 정책들을 명확하게 파악하여야 합니다. 단순 행 추가라면 많은 문제가 일어나지 않을 가능성이 높은 반면에 기존 데이터를 조회하고, 기존 데이터를 보정 또는 수정한 뒤 DB에 업데이트 하는 작업이라면 더 꼼꼼하게 확인해야 합니다.
사실 마이그레이션이 두려운것은 기존 데이터를 업데이트할 때 발생할 수 있는 사이드이펙트를 명확하게 가늠하지 못했던 상황이 많았던 것 같습니다.

본인이 확인 가능한 범위 내에서 시간을 좀 더 들여 안전구역을 만들고 나면 마이그레이션에서 안정감을 얻을 수 있습니다.

세번째. 최적화된 쿼리 작성과 DB 인덱스/ 쿼리 실행 계획을 인지할 것

마이그레이션 요구사항을 수립하고, 쿼리를 어느정도 머리에 그려놓은 상태에서 영향받는 대상 테이블의 인덱스를 확인하고 쿼리를 작성합니다.
실제 전송될 쿼리의 실행계획을 머리속으로 그려보고, explain 확인을 통해 실제 내가 작성한 쿼리가 실행계획에 따라 적절하게 최적화되어 실행될 수 있는지 확인합니다.

그리고 batch update 작업이 가능한 경우라면, 웬만해서 batch update를 활용하는 편입니다. 훨씬 빠릅니다. JPA를 이용하고 있고 마이그레이션 코드를 빠르게 만들 수 있는 상황에서도, batch 를 지원하는 JDBC를 이용해 bulk로 접근해 쿼리 자체 성능을 최적화 시키는 방법도 있으니 상황에 맞는 적절한 방법을 잘 선택 하는것이 필요합니다. (DynamoDB는 벌크를 25개 밖에 지원안해줍니다 서운합니다ㅜ )

네번째. 테이블 Alter 실행이 필요할 때엔 Online DDL 을 적극적으로 활용할 것.

꽤나 많은 데이터를 갖고있는 DB에 무지성 DDL 갈겨버리면 테이블 락으로 인해 서비스가 중단되는 위기에 처할 수 있습니다.
다행이도, InnoDB MySQL 5.6 버전부터 Online-DDL 이라는 방법이 추가되면서 DDL을 실행시킨 테이블이 변경되는 동안 대상 테이블에 DML을 계속 처리 할 수 있는 기능이 추가되었습니다. 대상 시스템이 사용하고있는 DB 버전을 확인하고 현재 사용중인 버전에서 가장 락타임을 낮게 가져갈 수 있는 방법을 선택하는것이 좋습니다.

다섯번째. reader-writer 분리된 구조의 DB라면 복제 지연을 고려할 것

마이그레이션을 하다보면 마이그레이션 그 자체에만 신경이 가있어서, 이런 인프라 수준의 고려를 잊어먹는 경우가 많습니다.
일정 크기 이상의 서비스를 운영하는 회사에서는 대부분 데이터베이스 부하 분산을 위해 master-slave 형태로 reader-writer 인스턴스를 를 분리 해놓는 경우가 많고, 이 구조에서 마이그레이션 시 발생할 수 있는 대표적인 이슈 케이스가 복제 지연 이슈입니다.

master -> slave 데이터 복제는 실시간 수준으로 이루어진다고 얘기하고 있음에도 불구하고, writer 인스턴스를 통해 업데이트를 수행하고 reader 인스턴스를 통해 읽어오는 작업이 굉장히 빠른시간내로 이루어지게 되면 복제 지연으로 인해 대상 행을 찾을 수 없는 문제가 발생할 수 있습니다. (많이 발생 합니다)

따라서 복제지연 이슈가 완전히 해결되지 않은 상태라면 웬만해서는 마이그레이션시 writer instance를 바라보는것이 좋습니다.
Spring 기준에서 서비스들은 대부분 기본적으로 Transactional 애너테이션의 readOnly 값으로 reader-writer 인스턴스를 결정하게 세팅해놓은 경우가 많이 있을텐데요. 그러면 아래와 같이 코드를 작성 할 가능성이 높을 것 같습니다.

@Transactional(readOnly=false)
fun insertMember(memberId: Long, memberName: String, memberNumber: String)
	// writer 인스턴스에 입력...
}

@Transactional(readOnly=true)
fun getMember(id: Long){
	// reader 인스턴스에서 조회...
}

이것을 마이그레이션 용도로 사용하려면

@Transactional(readOnly=false)
fun insertMember(memberId: Long, memberName: String, memberNumber: String)
	// writer 인스턴스에 입력...
}

@Transactional(readOnly=false)
fun getMember(id: Long){
	// writer 인스턴스에서 조회...
}

이렇게 변경해서 마이그레이션에서 사용하는 모든 operation을 writer 인스턴스를 보게 해서 복제지연을 방지할 수 있습니다.

여섯번째. 마이그레이션 시나리오 수립할 것

마이그레이션 시 쉽게 피할 수 없는 사이드 이펙트가 파악되었을때,이를 해결할 수 있는 아이디어로 시나리오를 적절하게 수립하고, 이를 시뮬레이션 해보는것이 필요합니다.

여러 상황이 있긴 하겠지만, 대부분 가장 기본적이고 자주 쓰이는 시나리오는
1. 마이그레이션을 위한 선 배포
2. 마이그레이션
3. 마이그레이션 이후 후 배포

순서인 것 같습니다. 상황에 따라 조금씩 바뀌기도 하지만요.
예를들어 제가 예전에 Database의 특정 데이터들을 통으로 다른 이기종 신규 데이터베이스로 옮기는 작업을 했을때에는 아래와 같이 진행했습니다.


기존 구조는 위와 같았습니다. 이 구조에서 신규 DB로 전환하면서 새로들어오는 유저 트랜잭션에 의해 발생하는 데이터 변경사항을 포함한 모든 데이터들을 신규 DynamoDB에 옮겨야 했습니다.

마이그레이션을 위한 선 배포를 했고,이 시점부터 기존 데이터와 신규 DynamoDB에는 유저 트랜잭션에 의해 발생하는 데이터 변경사항들이 모두 함께 담기게 됩니다. 이 배포 시점 이후로는 신규 DB에 기존 DB의 변경사항이 담기고 있기 때문에, 이 시점 이전의 데이터를 모두 신규 DB로 무중단 마이그레이션 진행합니다.

마이그레이션이 끝나고, 검증 이후 후 배포를 통해 기존 DB로 가는 트래픽을 멈추고 신규 DB로 모든 트래픽을 전환합니다.

만약, RDB -> RDB 진행시에는 발생할 수 있는 이슈가 하나 있습니다. 기존 DB와 신규 DB의 auto_increment로 생성되는 id sequence가 다른 문제입니다. (새로 DB 테이블을 만들때 UUID를 항상 고려하게 되는 이유인것 같아요)

다행이도 저는 Dynamo로의 마이그레이션을 진행했기 때문에 해당되진 않았지만요.

일곱번째. 마이그레이션 검증 확실하게 하고, 재 마이그레이션 가능성을 놓치지 말 것.

마이그레이션 중 문제가 발생하지 않을 것이라고 기대하는 것은 너무 위험한 생각입니다.

마이그레이션 검증은 마이그레이션 만큼 중요한 task 이며 종료 시점에 수행하고, 해당 마이그레이션과 관련된 기능들을 모두 확인하는 절차를 거치는게 좋습니다.

검증과 테스트가 제대로 되지 않으면 나중에 더 큰 문제가 발생해서 심지어 되돌릴 수도 없는 상황을 맞이 할 수 있기 때문에 서버 리소스, DB 리소스와 개발 리소스를 좀 더 들이더라도 마이그레이션 검증은 필수적인 과정이며 그렇기에 이를 잘 수립하고 시나리오에도 포함시켜야 합니다.

또한, 마이그레이션에 실패했을 때 보정 트랜잭션 또는 재 마이그레이션을 반드시 진행시킬 수 있어야 합니다.
그렇기에 마이그레이션을 여러번 진행해도 결과적 일관성을 보장할 수 있는 형태의 시나리오를 적절하게 잘 수립하는것이 중요하겠습니다.

0개의 댓글