트랜잭션리스, 완벽한 CDC(Change Data Capture) 시스템 구축법
2024년 11월 22일
서문
개인정보보호법, 전자금융거래법, 의료기록 개인정보보호법 등 개인정보를 포함한 민감한 정보의 변경사항을 기록해야 할 의무를 서비스 제공자에게 부여하는 규제와 법령은 나날이 강화되고 있습니다. 법에서 명시하고 있는 의무 사항 이외에도 실시간 모니터링 및 위협 탐지, 데이터 무결성 유지 및 사고 대응과 같은 서비스의 유지보수 및 보안을 위해서도 데이터베이스 변경사항 기록의 중요성은 개발자, 데이터베이스 관리자, 보안 전문가 모두에게 무시할 수 없는 사항입니다.
많은 서비스 제공자들은 위와 같은 요구사항을 만족하기 위해, 데이터베이스에서 발생하는 데이터 변경사항을 실시간으로 식별하고 추적하는 기술인 CDC(Change Data Capture)를 사용합니다. 데이터베이스 접근제어 기능을 제공하는 QueryPie에서도 역시 CDC를 지원합니다. 이 백서에서는 QueryPie에서 어떠한 방법을 통해 트랜잭션 없이 CDC 시스템을 구현했는지에 대해 다룹니다.
문제
트랜잭션 기반 CDC가 가지는 문제들
CDC는 기본적으로 변경이 발생했을 때 변경 이전 데이터와 이후 데이터를 기록해야 합니다. 이러한 기능을 구현하기 위해 사용할 수 있는 방법들 중 하나는 바로 데이터베이스의 트랜잭션을 활용하는 것입니다.
예를 들어 사용자가 UPDATE 쿼리를 통해 데이터를 변경하고자 할 경우, 다음과 같은 과정을 통해 변경 전후 데이터를 기록할 수 있습니다.

그러나 트랜잭션을 통해 CDC를 구현할 경우, 사용자가 대상 테이블에 변경 쿼리를 수행할 때마다 트랜잭션의 롤백을 수행해야 하는 문제가 발생합니다. 트랜잭션의 롤백은 DBMS에 부하를 가하게 되며, 대상 테이블의 크기가 크면 클수록 가해지는 부하도 증가하게 됩니다. 게다가 동일한 테이블에 대해 쿼리 수행 이전, 이후 총 두 번의 중복된 조회를 수행해야 하므로 데이터베이스 접근 효율성이 떨어집니다. 이외에도 NoSQL의 경우 트랜잭션을 미지원하거나, 지원하더라도 약한 레벨의 트랜잭션만을 지원하기 때문에 트랜잭션 기반으로 CDC를 구현하게 될 경우 지원이 어려울 수 있습니다.
트랜잭션의 롤백이 얼마나 영향을 끼치는지는 간단한 테스트 시나리오를 통해 확인해볼 수 있습니다. 테스트 시나리오 환경은 다음과 같습니다.
MySQL 8.0 (on-premise, 8core(vcore 16), mem 256GB)
100,000개의 레코드
다음과 같은 DDL을 가지는 테이블
actor네트워크 병목을 무시할 정도로 충분히 빠른 네트워크 환경
CREATE TABLE actor ( actor_id int NOT NULL AUTO_INCREMENT, first_name varchar(255) NOT NULL, last_name varchar(255) NOT NULL, last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (actor_id));해당 테이블의 모든 레코드에 대해 first_name을 모두 'Christopher'라고 변경하는 시나리오를 가정해 보겠습니다. 만일 트랜잭션 롤백 기능을 활용한다면 다음과 같은 쿼리를 모두 수행해야 합니다.
mysql> SET AUTOCOMMIT=0;Query OK, 0 rows affected (0.06 sec) mysql> SELECT * FROM actor;...100000 rows in set (0.42 sec) mysql> UPDATE actor4 SET first_name='Christopher' WHERE 1;Query OK, 100000 rows affected (1.54 sec)Rows matched: 100000 Changed: 100000 Warnings: 0 mysql> ROLLBACK;Query OK, 0 rows affected (0.85 sec) mysql> UPDATE actor4 SET first_name='Christopher' WHERE 1;Query OK, 100000 rows affected (1.58 sec)Rows matched: 100000 Changed: 100000 Warnings: 0 mysql> COMMIT;Query OK, 0 rows affected (0.04 sec)동작의 소요 시간을 테이블로 간소화하면 다음과 같습니다. 실제 동작에 필요한 테이블 조회와 업데이트 이외에 CDC를 위해 추가된 동작은 노란색으로 강조 표시하였습니다.
동작 | 수행 시간 |
|---|---|
테이블 조회 | 0.42s |
테이블 업데이트 | 1.54s |
트랜잭션 롤백 | 0.85s |
테이블 업데이트 | 1.58s |
트랜잭션 커밋 | 0.04s |
총 소요 시간 | 4.43s |
사용자 쿼리 동작의 총 소요 시간 | 2.04s |
추가된 동작의 총 소요 시간 | 2.39s |
보시는 바와 같이, 트랜잭션 롤백 기반 CDC의 경우 업데이트와 롤백으로 인해 두 배 이상 시간이 소요되는 것을 확인할 수 있습니다. 즉, 사용자 입장에서는 CDC 사용으로 인해 50%의 성능 저하를 경험하게 됩니다.
기타 CDC 구현이 가지는 문제들
이외에도 테이블에 트리거를 생성하여 데이터 변경을 추적하거나, 테이블에 modified_at 등 변경 시각이나 타임스탬프를 기록하고 주기적으로 조회하거나, MySQL의 binlog와 같은 로그 파일을 활용하여 CDC를 구현하는 방법이 있습니다. 각 구현마다의 장단점이 존재하지만, 공통적으로는 CDC의 동작을 위해 DBMS를 수정해야 하므로 CDC의 동작이 DBMS에 종속된다는 단점을 공유하고 있습니다. 즉, 새로운 DBMS 인스턴스를 추가할 때마다 관련된 설정을 추가해야 한다는 것입니다.
그렇다면 어떠한 방법을 사용해야 DBMS에 부하를 가하지 않고, DBMS 종속성이 생기지 않으면서 효과적으로 CDC를 구현할 수 있을까요?
목표 설정
DBMS에 가하는 부하를 줄이려면, CDC 구현 시 트랜잭션을 사용하지 않으며 동시에 동일한 테이블에 대한 중복된 쿼리를 수행하지 않아야 합니다. 또한 DBMS와의 종속성을 방지하기 위해 CDC의 동작을 위해 DBMS를 수정하지 않아야 합니다.
QueryPie에서는 이러한 요구사항을 충족하기 위해, 쿼리 수행 이후 데이터를 데이터베이스에 직접 조회하는 것이 아닌 사내 쿼리 분석 라이브러리인 QSI(Query Structure Interface)를 활용하여 취득합니다. 어떻게 하면 테이블을 직접 확인하지 않고도 쿼리 수행 이후의 데이터를 얻을 수 있을까요?
솔루션 개요

QSI는 테이블을 직접 조회하는 대신, 사용자가 질의한 쿼리의 수행이 가져올 변경사항을 변경 전 테이블 조회 결과에 시뮬레이션하여 변경 후 테이블 결과를 전달하게 됩니다. 즉, 쿼리의 수행 결과 확인의 과정을 데이터베이스에 위임하는 것이 아니라, QSI 내에서 직접 쿼리를 수행해보고 그 결과를 제공하는 것입니다. 쿼리 시뮬레이션 기술을 사용하는 QueryPie에서는 CDC를 사용하더라도 데이터베이스에서의 트랜잭션 수행과 롤백이 발생하지 않습니다. 또한, 쿼리만 수행하면 되므로 동작을 위한 DBMS의 수정도 요구하지 않으며, QueryPie 설치 즉시 바로 사용할 수 있습니다.