조회수 요구사항
- 조회수
- 조회수 어뷰징 방지 정책
- 각 사용자는 게시글 1개 당 10분에 1번 조회수 집계
- 10분동안 100번 조회하더라도, 1번만 집계된다.
1. 조회 수 설계
1.1. 데이터 특성
조회수는 좋아요 수, 게시글 수, 댓글 수처럼 다른 데이터의 개수로 파생되는 데이터가 아니다. 사용자는 조회 내역을 확인하지 못하므로, 조회수만 보여주면 된다. 즉, 전체 조회수를 단일 레코드에 비정규화하여 저장해도 충분하다. 비정규화 했던 게시글 수/ 댓글 수/좋아요 수와 데이터의 특성을 비교하면 아래와 같다.
- 데이터 일관성
- 게시글 수/댓글 수/좋아요 수 : 비교적 중요
- 원본 데이터를 통해 개수가 파생되므로, 불일치가 발생하면 안된다. 데이터 불일치가 발생하면 사용자가 인지할 수 있다.
- 조회수 : 비교적 덜 중요
- 모든 조회 내역을 보여주진 않는다. 단순히 조회된 횟수만 보여주면 된다. 드물게 불일치가 발생하더라도 사용자가 인지하기 어렵다.
- 게시글 수/댓글 수/좋아요 수 : 비교적 중요
- 쓰기 트래픽
- 게시글 수/댓글 수/좋아요 수 : 비교적 적다
- 게시글 작성, 댓글 작성, 좋아요 클릭 등 사용자의 직접적인 액션에 의해 쓰기 작업이 필요.
- 조회수 : 비교적 많다.
- 단순히 게시글 조회만 해도 쓰기 작업이 필요.
- 게시글 수/댓글 수/좋아요 수 : 비교적 적다
1.2. 기술의 선택
게시글/댓글/좋아요 수는 데이터의 일관성이 중요하기 때문에 트랜잭션을 지원하는 RDB를 사용했다 .하지만, RDB를 사용하게 된다면 디스크 접근 비용과 트랜잭션 관리 비용이 발생한다.
조회수의 경우 데이터 일관성이 덜 중요하고 다른 데이터에 의해 파생되는 데이터가 아니므로, 트랜잭션이나 안전한 저장소가 반드시 필요한 것은 아니다. 또한 조회수는 트래픽이 많이 발생하기 때문에, 접근 비용이 비싼 디스크보다 더 빠른 저장소인 In-memory Database를 고려해볼 수 있다.
1.3. Redis
Redis 특징
- In-Memory Database
- 고성능
- NoSQL Database
- 정해진 스키마가 없고, 유연한 데이터 모델
- Key-Value 저장소
- 다양한 자료 구조 지원
- String, List, Set, Sorted Set, Hash 등
- TTL (Time To Live) 지원
- 일정 시간이 지나면 데이터 자동 삭제
- Single Thread
- Redis는 단일 스레드에서 순차적으로 처리
- 각 명령어가 순차적으로 처리되므로, 동시성 문제를 해결하는데 유리
- 데이터 백업 지원
- 메모리는 휘발성 저장소지만, Redis에서 데이터를 안전한 디스크에 저장하는 방법도 제공(AOF, RDB)
- Redis Cluster
- 확장성, 부하 분산, 고가용성을 위한 분산 시스템 구성 방법 제공
- 논리적 샤드 지원
Redis 활용 사례
- 고성능 작업
- 메모리는 상대적으로 빠르기 때문에 Redis 자체를 데이터베이스로 이용할 수 있다.
- 싱글 스레드로 동작하기 때문에, 동시성 문제를 다루는데 유리하다.
- 캐시
- 더 느린 저장소에서 더 빠른 저장소에 데이터를 저장해두고 접근하는 기술
- 매번 디스크에서 데이터를 접근하면 느리므로, Redis에 데이터를 일정 시간 캐시해둘 수 있다.
- pub/sub
- Redis는 메시지를 발행 및 구독할 수 있기 때문에 실시간 통신에도 활용할 수 있다.
1.4. 데이터 백업
조회수를 위한 데이터베이스는 Redis로 채택했지만, Redis는 In-Memory DB이기 때문에 휘발성인 메모리에 데이터를 저장한다. 조회수 데이터의 일관성이 비교적 덜 중요하지만, 완전히 유실되면 안되기 때문에 어느 정도 데이터의 영속성은 필요하다. 데이터 백업을 위해 Redis에서 제공하는 AOF, RDB 기능을 활용할 수도 있고, 주 데이터베이스로 사용했던 MySQL에 데이터를 백업할 수도 있다.
데이터 백업은 어떻게 할 것인가? 조회수는 약간의 데이터 유실을 허용한다는 관점이기 때문에, 실시간으로 모든 데이터를 백업할 필요는 없다. 그렇기 때문에 아래와 같은 방법으로 데이터를 백업할 수 있다.
- 시간 단위 백업
- N분 단위로 Redis 데이터를 MyySQL로 백업한다.
- 배치 또는 스케줄링 시스템 구축 필요
- 백업 전 장애 시에 유실될 수 있음
- 개수 단위 백업
- N개 단위로 Redis의 데이터를 MySQL로 백업한다.
- 조회 시점에 간단히 처리 가능
- 백업 전 장애 시 유실될 수 있음
- 개수 단위가 채워지지 않으면 유실될 수 있음
- 두 방식을 적절히 조합
각각의 특성을 고려하여 상황에 맞게 선택하면 된다.
2. 어뷰징 방지 - 분산락
2.1. 조회수 어뷰징 방지 정책 설계
어뷰저는 특정 게시글을 여러 번 조회해서 데이터를 조작할 수 있다. 어뷰징 방지를 위한 조회 여부는 아래와 같은 방식으로 식별할 수 있다.
- 로그인 사용자
- 사용자별로 식별
- 비로그인 사용자
- IP, User-Agent, 브라우저 쿠키, 토큰 등 다양한 방법
조회수는 사용자마다 게시글 1개당 10분에 1번 증가하도록 할 것이다. 그러기 위해서는 10분 내 게시글을 조회했었다는 사실을 알아야 한다. 무상태(stateless) 애플리케이션은 이러한 상태를 관리하기 위해 상태 저장소를 활용할 수 있다.
상태 저장소로 우선 MySQL을 고려해볼 수 있다. 하지만, 게시글 조회는 트래픽이 많기 때문에 성능이 빨라야 한다는 점, 동시성 문제 발생 가능성, MyhSQL에서는 데이터 자동 삭제를 지원하지 않는 점을 고려했을 떄, MySQL은 적절한 선택지가 아닐 수 있다.
Redis는 In-Memory DB로 성능이 빠르고, 싱글 스레드로 동작하기 때문에 동시성 문제를 해결할 수 있으며, TTL을 지원하여 10분 후에 데이터를 자동으로 삭제할 수 있으므로 Redis가 적절한 선택지로 보인다.
조회수 증가 요청이 오면, Redis에 TTL=10분으로 데이터를 저장한다. 게시글 조회는 사용자 단위로 식별되므로, key=(articleId + userId)가 된다. 이미 저장된 데이터가 있으면 저장에 실패하도록 하는 setIfAbsent를 사용한다.
데이터 저장 성공 여부에 따라,
- 성공했으면, 조회 내역이 없었음을 의미하고, 조회수를 증가한다.
- 실패했으면, 조회 내역이 있었음을 의미하고, 조회수를 증가시키지 않는다.
이러한 과정은 사용자의 게시글 조회수 증가에 대해서 Lock을 획득한다고 볼 수 있다. 이렇게 분산 시스템에서 락을 획득하는 것을 분산 락(Distributed Lock)이라고 한다.
MSA 환경은 Scale-Out을 고려한 환경이다. 조회수 서비스는 여러 개의 서버 애플리케이션으로 구성된다.
조회수 서비스의 여러 서버 애플리케이션들은
- 사용자의 게시글 조회수 증가에 대해서 10분간 분산 락을 획득한다.
- 분산 락이 점유되면, 다른 요청은 락이 10분 후에 해제되기까지, 락을 추가로 점유할 수 없다.
REFERENCE
스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판| 쿠케 - 인프런 강의
현재 평점 4.9점 수강생 1,164명인 강의를 만나보세요. 대규모 데이터와 트래픽을 지탱하기 위한 시스템을, 스프링부트로 직접 만들면서 배워봅니다. 대규모 시스템 디자인, Microservice Architecture, Ev
www.inflearn.com
'프로그래밍_인강' 카테고리의 다른 글
| 좋아요 수 설계 - 비정규화, 락 (0) | 2025.09.04 |
|---|---|
| 게시글 목록 조회 - 페이지 번호 (with 인덱스) (3) | 2025.07.21 |
| Distributed Relational Database (1) | 2025.07.15 |