MySQL 레플리케이션 바이너리 로그 기록 타입 (Row, Statement, Mixed)

Posted by , December 08, 2024
데이터베이스레플리케이션

MySQL 8.0 레플리케이션 동작 과정과 아키텍처 구성 방식 에서 설명했듯이, MySQL 레플리케이션은 현재 GTID 기반의 안정적인 복제 방식을 택했다. 이때 궁금한 점은, 정확히 어떤 이벤트를 기록한다는 것일까?

Row 포맷 (변경된 데이터를 기록)

MySQL 데이터베이스에서 실행한 작업을 바이너리 로그에 저장하는 다양한 형태(포맷)이 있다. 그 중 하나는 SQL 실행을 통해 변경이 일어난 데이터 전부를 바이너리 로그 파일에 기록하는 방법이 있다. 데이터베이스 변경 내용을 각각의 행(row) 단위로 기록한다고 하여, Row 기반 바이너리 로그 포맷이라고 한다.

Row 기반 바이너리 로그 포맷은 변경된 데이터가 모두 바이너리 로그에 기록된다는 것이 가장 큰 특징이다. 따라서 변경되는 데이터가 많을수록 바이너리 로그에도 많은 데이터가 기록된다.

이는 무엇이 문제가 될까? 만약 MySQL 에서 실행한 쿼리가 대량의 데이터를 변경했거나, 또는 데이터 수가 대량으로 삭제되어도, 또는 BLOB 처럼 엄청 큰 용량의 값이 새롭게 추가되거나 수정된다면 문제가 된다. 바이너리 로그 파이릐 크기가 매우 커지면서 저장 공간을 크게 차지하게 된다.

Statement 포맷 (실행한 SQL 을 기록)

그렇다면 변경된 데이터를 모두 기록하느 방법 대신에 다른 방법이 있지 않을까? 그에 대한 방안으로, 실행한 SQL 을 기록하는 방법이 등장했다.

Statement 기반 바이너리 로그 포맷은, 각 이벤트에서 실행한 SQL 문을 바이너리 로그 파일에 기록하는 방법이다. 바이너리 로그의 용량이 크게 줄어들기 때문에, 더 이상 용량을 걱장할 필요가 없다.

하지만 이 또한 문제점이 있다. SQL 문이 복제되어 동일한다고 한들, 실행할 때 마다 결과 다르게 나오는 비확정적(Non-Deterministic) 쿼리가 존재한다면, 소스 서버와 레플리카 서버의 데이터가 다르게 기록된 데이터 정합성 문제가 발생할 수 있기 때문이다. UUID() 함수를 사용하는 쿼리가 전형적인 비확정적(Non-Deterministic) 쿼리 에 해당한다. UUID 함수는 고유한 식별자를 만들기위해 실행할 때 마다 매번 다르고 고유한 값을 만든다.

결국, 소스 서버에서 사용한 SQL 문을 가져와 레플리카 서버에서 동일하게 실행하는 방법은, 소스 서버와 레플리카 서버간에 비확정적 쿼리로 인해 데이터 정합성 문제가 발생할 수 있다.

Mixed 포맷 (Row + Statement 혼합)

이를 해결한 방식이 Mixed 포맷이다. 이는 Row 와 Statement 방식을 혼합한 방식이다. 평상시에는 Statement 포맷으로 로그를 기록하여 바이너리 로그의 크기를 최소한으로 관리한다. 그러다 Statement 포맷으로 복제했을 때 문제가 발생할 수 있는 비확정적 쿼리를 저장하는 경우, Statement 대신에 Row 방식으로 바이너리 로그에 이벤트를 기록한다. 이를 통해 용량 문제도 해결하고, 데이터 정합성 문제도 해결할 수 있다.

MySQL 의 경우 Mixed 포맷을 기본적으로 택한다. 어떤 쿼리가 비확정적 쿼리인지는 MySQL 스토리지 엔진이 자체적인 기준에 따라 판별한다.

만능은 아니다.

편리하긴 하지만 포맷 형식을 MySQL 이 잘못 판단하 저장할 가능성도 조금은 존재하기 때문에, Mixed 방식이 항상 만능은 아니라는 점을 알고있자. 따라서 자신에게 가장 맞는 바이너리 로그 포맷을 찾아서 적용할 수 있어야함을 알자.

참고

Haon
꾸준히, 배움에 대한 생각을 글로 정제하기 위한 블로그입니다.
gatsby-starter-haonkakaotech