액티브 유저 3억명 이상인 SNS를 개발한다면?


SNS 프로젝트는 현재 다음과 같은 서버 구조를 갖고 있다.

스크린샷 2021-11-17 오후 3 23 18

메시지큐, 캐싱, 로드밸런싱 등 성능 개선을 위해 노력했고 실제로 성능이 개선되기는 했으나

프리티어로 구성한 서버라 한계가 있었고 특히 좋아요, 타임라인 기능 개선이 쉽지 않았다.

실제로 3억명 이상의 액티브 유저를 보유한 트위터의 타임라인 시스템을 알아보고

현재 프로젝트에 적용해볼 수 있는 부분이 있다면 학습해보고 적용해보기로 했다.



SNS 특징

트위터는 다음과 같은 통계를 내고 있다.


위 통계는 트위터 기준이지만 대부분 SNS는 다음과 같은 특징을 갖고 있다.


쓰기 요청은 메시지큐를 적용했기 때문에 약간의 딜레이가 있을 수 있지만

데이터가 유실되지 않고 많은 요청을 받을 수 있게 개선된 상태이다.

문제는 읽기 요청이다. 캐싱을 적용하긴 했지만 읽기 요청이 엄청나기 때문이다.



팔로워 수에 따른 타임라인 구현

트위터는 새로운 트윗을 작성한 트위터의 팔로워 수에 따라 타임라인 캐시를 만드는 방식이 다르다고 한다.

출처: https://medium.com/@narengowda/system-design-for-twitter-e737284afc95


1) 팔로우 수가 많은 셀럽

유명 트윗터인 경우 팔로워가 8000만명이 넘는 경우도 있다고 한다.

위 사진은 많은 팔로워 수로 유명한 배우의 SNS이다.

2,361만명으로 이 경우, 팬아웃 방식을 사용하는 것은 시스템 부담이 있다.

이 경우, 타임라인 조회 요청 시, 일반 타임라인 캐시에 유명 트윗터의 트윗을 합쳐주는 방식을 사용해야한다.

현재 프로젝트에서도 사용하는 방식이다.

쿼리는 다음과 같다.

SELECT 
        * 
    
FROM 
        post
        JOIN USER ON post.user_id = user.id 
        JOIN follows ON follows.following_id = user.id 
    
WHERE 
        follows.follower_id = user_id

RDB에서 데이터를 조회한다.



2) 일반 팔로워

매번 Post 테이블을 search하기엔 부담되기 때문에 일반 사용자들의

트위터 쓰기 요청이 들어오면, 팔로워의 타임라인 캐시에 새로운 트윗 아이디를 추가해주는 방식이다.

이를 fan-out 방법이라고 한다.


1. 사용자가 게시글 작성을 요청한다. 
2. 게시글 요청은 로드밸런싱을 통해 서버로 전달된다. 
3. 서버는 DB와 Cache 저장소에 해당 데이터를 저장한다. 
4. 서버는 cache 저장소에서 해당 유저를 팔로우 한 사람의 유저 정보들을 가져 온다.
5. 해당(팔로우한 유저들) 유저들의 in-memory(Redis)에 해당 데이터를 저장한다.
6. 트윗을 작성한 유저를 팔로우한 유저들은 타임라인에서 해당 트윗을 볼 수있다.


* 모든 팔로우 유저의 in-memory에 데이터를 저장하지 않고 
2주내로 접속한 액티브 유저들의 타임라인에만 저장한다. 

타임라인 조회 시 DB를 접근할 필요 없이 해당 유저의 in memory 데이터에서 타임라인 데이터를 가져오면 된다.


대용량 트래픽을 감당하는 시스템에 대해 알아봤다.

현재 개발중인 SNS 프로젝트에서 사용자를 이렇게 구분할 수 없어서 DB에서 조회한 데이터를

캐싱처리해서 DB에 읽기 요청을 최대한 줄이는 방법으로 구현했다.



좋아요 기능

현재 많은 SNS에서 좋아요 수를 보여주는 기능을 없애는 분위기지만 좋아요 기능은 아직 남아있다.

좋아요를 요청하고 취소하는 작업은 자주 발생하기 때문에 개선할 부분이라고 생각하고 개선 방법을 찾아봤다.

가장 유명한 방법은 Redis의 Set을 이용해 중복 검사를 따로 하지 않고

RDB에는 Spring Batch를 이용하여 bulk insert로 데이터를 저장하는 것이다.

redis와 spring batch를 학습해보고 적용해보면 좋을 것 같다.



참고