CS/데이터베이스

CQRS를 곁들인 Materialized View

Junuuu 2023. 12. 26. 17:07
728x90

개요

데이터베이스에서 원하는 데이터를 제공하기 위해 테이블들을 여러 번 join 했던 경험이 있습니다.

이때 paging 등이 함께 들어가면 쿼리를 작성하기도 복잡해지고, 여러 테이블 join에 대한 성능도 고려해야 합니다.

이때 사내에서 read table과 write table을 분리하는 방안이 적용된 케이스들을 알게 되었고 Materialized View 개념에 대하여 학습해보고자 합니다.

 

Database의 View란?

데이터베이스를 공부하다보면 View라는 개념을 지원합니다.

View란 실제로 데이터를 저장하고 있지 않지만 SELECT 쿼리 결과를 가상 테이블로 제공합니다.

 

테이블 join 결과를 View로 만들어보기

CREATE VIEW employee_department AS
SELECT
e.employee_id,
e.employee_name,
d.department_name
FROM employee e
INNER JOIN department d ON e.department_id = d.department_id;

 

이제 View를 통해 복잡한 쿼리를 간단한 쿼리로 읽을 수 있습니다.

SELECT employee_name, department_name
FROM employee_department;

 

만약 해당 쿼리에 SUM과 같은 집계함수나 GROUP BY 키워드가 들어가면 더 복잡한 쿼리를 더 간단하게 수행할 수 있습니다.

 

Database의 Materialized View란?

Database에서는 Materialized View라는 개념을 지원하는데 간단하게 말하자면 쿼리의 결과를 저장합니다.

일정한 상수 시간에 접근해야 하는 대규모 데이터 집합에는 종종 Materialized View를 활용합니다.

 

하지만 모든 데이터베이스에서 Materizlied View를 지원하지는 않습니다.

예를 들어 MySQL에서는 지원하지 않는 기능이며 PostgreSQL 같은 경우 지원하지만 최신 데이터가 필요한 경우 수동으로 새로 고쳐주어야 합니다.

 

즉, 아래와 같이 주기적으로 데이터 싱크가 필요합니다.

REFRESH MATERIALIZED VIEW view_name;

 

그렇다면 View와 Materialized View는 무엇이 다를까요?

View는 논리의 테이블이기 때문에 호출할 때마다 View에 지정된 쿼리가 실행됩니다.

하지만 Materialized View는 디스크에 데이터를 저장하므로 데이터의 스냅숏 역할을 수행하기 때문에 매번 쿼리가 실행되지 않습니다.

즉, 데이터가 미리 계산되어 있기 때문에 매우 빠르게 액세스 할 수 있습니다.

 

하지만 데이터가 미리 계산되어 있기 때문에 동기화를 수행해주어야 하는 점이 View와 차이점이라고 볼 수 있습니다.

 

View는 쿼리를 단순화해주지만 매번 쿼리가 수행되므로 성능적으로는 아쉬우며, Materiazlied View는 스냅샷 역할을 하기 때문에 성능적으로 좋지만 동기화해주어야 하는 아쉬움이 있습니다.

 

CQRS 관점에서 Materialized View 바라보기

CQRS관점에서 데이터베이스의 특정 문법의 개념에서 벗어나서 Query(read) 하기 위한 테이블의 개념으로 Materiazlied View를 바라볼 수 있습니다.

 

https://devocean.sk.com/blog/techBoardDetail.do?ID=165557&boardType=techBlog

 

일반적으로 우리는 Database에 적재하고 Query(read) 하기 위해서는 여러 테이블을 join 합니다.

 

하지만 Command(write)와 Query(read)를 분리하는 CQRS관점에서는 READ Table을 따로 두는 방식으로 join을 줄여 쿼리의 복잡도와 성능을 잡는 방식을 사용해 볼 수 있습니다.

 

CQRS에 대해 조금 더 알고 싶으신 분은 "CQRS 패턴이란?" 글을 참고하셔도 좋을 것 같습니다.

 

만약 각각의 Microservice가 서로 다른 DB를 바라보고 있다면 join을 수행할 수 있을까요?

https://devocean.sk.com/blog/techBoardDetail.do?ID=165557&boardType=techBlog

이런 경우에는 의존하는 데이터를 조회하기 위해 HTTP 요청을 수행하여 데이터를 받아와서 보여주어야 할 것입니다.

 

 

CQRS로 Materialized View 테이블 만들어내기

만약 Materialized View 테이블을 만들어내기 위해서 같은 데이터베이스를 바라볼 수 있는 환경이라면 Command 요청이 발생할 때마다 Materizliaed View에 동기화를 수행해 줄 수 있습니다.

 

이렇게 되면 실시간으로 Command 요청에 대해 데이터 동기화가 수행되고 Query 시에도 join을 거치지 않아도 되므로 성능적으로 이점을 가져갈 수 있습니다.

 

 

하지만 같은 데이터베이스 바라볼 수 없는 환경이라면 어떨까요?

https://devocean.sk.com/blog/techBoardDetail.do?ID=165557&boardType=techBlog#none

 

2개의 Microservice에서 Command(write) 요청이 수행될 때 Message Broker로 이벤트를 발행(publish)할 수 있습니다.

이후 Query 전용인 Microservrice에서 해당 이벤트를 수행(consume) 하여 Materialized View에 반영할 수 있습니다.

 

하지만 이런 환경이라면 Message Queue의 네트워크 지연시간, 장애발생 시 대처까지 고려해주어야 하기 때문에 조금 더 복잡해질 수 있을 것 같습니다.

 

마무리

Message Brocker를 활용하지 않더라도 Debezium과 같은 툴을 활용하여 CDC로 구성해 볼 수 있습니다.

Debezium을 활용한 CDC에 대해서는 저도 사용해보지 않았기 때문에 깊게 다루어보진 않겠습니다.

 

또한 Query 전용인 Microservice에 Query에 특화된 MongoDB와 같은 데이터베이스를 활용해 볼 수도 있는 것도 하나의 장점 같습니다.

 

하지만 아무래도 관리를 하기 위한 복잡도가 증가할 수 있으니 trade off를 적절하게 고려해보아야 할 것 같습니다.

예를 들어 Command를 수행하는 새로운 요구사항이 추가되면 까먹지 않고 Marteriazlied View를 위한 table에도 데이터 정합성을 유지해주어야 합니다.

 

모든 점을 전반적으로 고려해 보아도 늘어나는 데이터에 Query가 점점 부담스러워진다면 Marteriazlied View용 table을 한번 도입해보는건 어떨까요?

 

 

 

참고자료

https://www.baeldung.com/databases-views-simple-complex-materialized

https://devocean.sk.com/blog/techBoardDetail.do?ID=165557&boardType=techBlog