지난 글에 이어서 TopN을 구하는 방식에 대해서 잦은 변경이 있는 데이터 관련 TopN을 구하는 방식을 이야기 해보고자 한다.
최종적으로 부하가 많은 서비스에서 TopN을 실시간으로 집계를 해서 보여주는 것은 별도의 데이터 파이프라인을 통해 집계하는 방식이 될 것이다.
하지만 데이터 파이프라인을 구성하는 것과 관리하는 것. 추가로 중간 단계에서의 엔지니어링을 거치지 않고 끝판왕 구조를 바로 적용하게 된다면. 비용측면에서 적절한 엔지니어링이 아니라고 생각한다.
또한, 실제 서비스가 요구하는 트래픽 상황이 데이터 엔지니어들이 파이프라인을 구축해서 처리해야 할 규모가 아닐 수도 있다.
그래서 적지는 않지만 적당한 양의 트래픽을 받는 서비스의 경우 어떻게 TopN을 처리할 수 있을지에 관해서 고민해봤다.
Materialized View + Redis - Sorted Set 사용
어느정도 규모가 있는 서비스는 캐시 레이어를 사용할 것이다. 그리고 여러 자료구조를 사용하고 싱글 스레드로 연산의 원자성을 보장해주는 Redis를 대부분 채택하고 있다고 생각한다.
Redis에는 Sorted Set 이라는 자료구조가 있다.
이 자료구조의 특징은 key - value - score를 바탕으로 특정 키 내부의 value들을 score 기준으로 정렬해준다.
TopN을 구하는 구조는 어떤 화면으로 보여주느냐에 따라서 최대 10 ~ 20개정도라고 예상이 된다.
아이디어는 간단하다.
배치잡을 통해 주기적으로 순위를 집계하는 Materialized Veiw 안에서 TopN + a를 구하고 Redis의 Sorted Set으로 해당 내용을 일정 주기로 동기화 해준다.
이후에 들어오는 데이터들은 추가로 Redis Soreted Set안에 존재하는 항목이면 score를 갱신해준다.
존재하지 않는다면, 무시한다.
Sorted Set 구현은 정렬 방식에 따라서 첫 번째부터 N번째 값을 가져오거나, 마지막 인덱스 부터 -N 까지의 값을 가져오도록 처리한다.
이러면 매 번 집계를 할 필요가 없고 최소 값의 변화에도 유연하게 처리할 수 있으며 처리량도 매우 크게 늘릴 수 있다.
정합성 관련
동기화 문제
위 로직에는 임계영역이 존재하며, 동기화를 해주지 않는다면 정합성이 틀어지는 문제가 있다.
임계영역은 내부에 score가 존재하는 값이고 조회 후 값을 갱신하는 부분이 원자적이지 않기 때문에 순서가 섞일 수 있고 이는 값이 의도하지 않게 될 수 있다.
이는 간단하게 해결할 수 있는데 Redis에서 지원하는 Multi 라는 커맨드를 통해서 2가지 연산을 트랜잭션으로 묶어주게 된다면 이를 풀 수 있다.
2가지 연산은 다음과 같다.
1. 데이터 확인
2. 존재하면 score 값 갱신 (기존 값 + 새로운 값)
이렇게 갱신하는 연산을 원자적으로 처리하게 해주면
TopN에 존재하지 않는 데이터의 score가 급증하는 경우
기존에 계산 된 TopN에 존재하지 않지만 갑작스럽게 해당 데이터가 급증하는 경우
일시적으로 데이터가 맞지 않을 수 있다. (동기화 주기동안)
만약, 이 부분이 비지니스적으로 민감하다면, 메모리 사용량을 늘려주는 수밖에 없다. 결국 모든 비지니스 데이터를
Sorted Set 안에 넣고 동기화 해주는 수 밖에는 없어보인다.
정리
사실 회사에서는 규모가 있기 때문에 이미 이런 기능들이 파이프라인을 통해서 집계되어서 처리되고있다.
즉, 적당한 규모의 서비스일 경우에 이 방식은 적절하지 않기 때문에 규모가 작을 때는 어떻게 처리할 수 있을지를 고민해보면서 글을 통해서 정리를 해봤다.
최종적인 답을 알고 있는 것은 항상 좋지많은 않다. 겪고있는 상황이 다 다르기 때문에 해당 상황에 맞는 적절한 엔지니어링이 필요하다고 생각한다.
'허브 살리기 프로젝트' 카테고리의 다른 글
Feign Client Retry (0) | 2024.08.30 |
---|---|
Redis로 Rate Limit 구현 (0) | 2024.06.23 |
TopN 구하기 (1) (1) | 2024.06.09 |
로컬에서만 Spring Boot에서 HTML Resource 파일이 제공 가능 했던 이유 (1) | 2024.05.02 |
Spring Kafka Record 삭제하기 (1) | 2024.05.01 |