가장 간단하게 구현한 시스템 부터 대규모 트래픽을 감당할 수 있을 정도의 시스템 까지 생각하면서.. 시스템 설계 방법을 나열해보겠다.. (아래 내용은 트랜잭션 격리 수준에 따라 내용이 좀 달라질 수 있다.) (틀린 점이 있을 수 있다.. 있으면 댓글 부탁드립니다.)
기술 스택 : Spring, JPA, MySQL
요구사항
타임라인 기능 설계에 대해 고민해보자.. 타임라인 기능이란.. 내가 어떤 게시글을 작성하면.. 나를 팔로우하는 사람들은 내 게시글을 각자의 타임라인 페이지(보통 home 페이지이다.)에서 볼 수 있는... 기능이다.
각 방법에서 쓰기 관점과 조회 관점으로 나눠서 설명하겠다. A 사용자가 B 사용자를 팔로우 하였고, B 사용자가 게시글을 쓰는 상황이라 생각하자.
첫번째 방법
가장 간단하게 생각할 수 있는 설계 방법이다.
post Table 을 위와 같이 설계한다.
follow Table 은 위와 같이 설계한다.
시스템은 위와 같다.
쓰기 관점
자 이제.. B 사용자가 게시물을 작성하면 post Table 에 row 가 하나 추가될 것이다.
조회 관점
문제는 조회 관점이다. A 가 타임라인 페이지를 진입 하는 순간.. 다음과 같은 과정이 일어난다.
1. 우선 A 가 팔로우 하는 모든 member_id 를 follow Table 에서 조회 한다. 2. 조회한 follow list 를 post Table 로 가서 적재된 게시물을 싹 가져와야 한다.
참고 원래 정렬도 해야하겠지만, 주제와 상관 없어서 생략한다. 조인을 하건 쿼리 두번을 하건 주제와 상관 없어서 생략한다.
즉, user Table(그림엔 없음) 과 follow Table 이 일대다 관계이고, follow Table 과 post Table 이 일대다 연관관계를 형성하고 있어서 조회 시점에 큰 부하가 존재하는 것이다. (fan-out 문제) -> A 가 팔로우 한 사람이 엄청 많다고 생각해보자..
첫번째 방법의 문제점은.. 보통 sns 는 게시글을 작성하는 것보다 게시글을 조회하는 상황이 훨씬 많다. 보통 조회와 쓰기는 서로의 성능을 희생시키면 본인의 성능이 올라가는 상황이 많다. (Trade-off) 두번째 방법에서 알아보자..
두번째 방법
조회 관점에서 일대다 연관관계를 없애보자..
위와 같이 timeline Table 을 추가하였다.
쓰기 관점
B 사용자가 게시물을 쓰면.. 다음 과정을 따른다.
1. post Table 에 row 하나를 저장 2. follow Table 에서 나(B)를 팔로우하는 사용자(A) 들을 리스트로 조회한다. 3. 나를 팔로우하는 사용자 리스트를 member_id 로 하여 timeline Table 에 하나씩 적재한다.
첫번째 방법과 비교하여 2번 과정이 조회에서 쓰기로 넘어왔고, 쓰기 성능에 부하가 생기게 되었다. 또한, timeline 이라는 Table 이 추가 되어 대용량 데이터 고민도 필요하게 되었다. -> 실제 Twitter 에선 Redis 로 해결하며 적절한 TTL 을 정해둔 것 같다.
조회 관점
조회 관점에서는..
A 사용자가 타임라인 페이지에 들어가면.. 다음 과정을 따른다.
1. timeline Table 에서 A 의 member_id 로 적재된, 게시물 post_id 를 리스트로 조회한다. 2. post_id 리스트로 post Table 에 가서 게시물을 조회한다.
첫번째 방법과 비교하여.. 조회 관점의 부하가 많이 준 것을 생각해 볼 수 있다. 특히, 2번 로직은 일대일 매핑으로 이루어졌으며, 팔로우 관련 fan-out 문제가 타임라인 조회에서 게시물 쓰기로 넘어갔다.
하지만, 데이터 정합성(일관성) 문제가 생길 수 있다. 첫번째 방법에서 테이블이 추가되었고 그 데이터는 모두 다른 테이블에서 복제한 값이다.
정리 (시간복잡도, logN 은 생략하였다.)
게시물 쓰기
타임라인 조회
첫번째 방법(fan out on read, pull model)
O(1)
O({내가 팔로우한 수} * {게시글 수})
두번째 방법(fan out on write, push model)
O({나를 팔로우한 수})
O({게시글 수})
-> 성능에 가장 영향을 많이 미치는 요소가 팔로우 수이고, 게시글 수는 캐시 외에 어찌할 방법이 없어보인다. (접근을 해야 보여주니..)
참고> twitter 는 push model, facebook 은 pull model 을 따른다고 한다..
세번째 방법
두번째 방법에서 게시물 쓰기시.. 나를 팔로우한 수만큼 timeline Table 에 쓰기 연산을 하는데.. 이 로직은 굳이.. 게시물 쓰기 시점에 완료할 필요는 없다. 특히, 엄청난 팔로워를 보유한 연예인들이 게시물 하나 쓰면 대기시간이 엄청나게 오래 걸리니까.. 비동기로 빼도 된다. -> 그러면 첫번째 방법처럼 Post Table 에 row 하나만 추가하면 되는 수준의 시간복잡도를 기대할 수 있다.
그러나.. 두번째 방법보다 더욱 데이터 일관성을 깊게 생각해야한다. 중복데이터를 비동기로 뺀 덕분에 데이터 일관성은 더 고민해봐야 한다. -> 시스템 복잡도가 높다..
위 사진은 Twitter 의 System Design 이다. 쓰기 시점에 Tweet(게시물) 은 DB 로 적재하고 팔로워에 해당 게시물을 알리는 로직은 비동기로 빠져있는 것을 볼 수 있다. 조회 시점에도 Redis 를 이용하여 두번째 방법보다 빠르게 조회 할 수 있도록 한 것을 볼 수 있다.
참고> 아래 링크를 보면.. twitter 의 시스템을 설명해준다. https://www.youtube.com/watch?v=KmAyPUv9gOY
시스템 설계 고민 2
가장 간단하게 구현한 시스템 부터 대규모 트래픽을 감당할 수 있을 정도의 시스템 까지
생각하면서..
시스템 설계 방법을 나열해보겠다..
(아래 내용은 트랜잭션 격리 수준에 따라 내용이 좀 달라질 수 있다.)
(틀린 점이 있을 수 있다.. 있으면 댓글 부탁드립니다.)
기술 스택 : Spring, JPA, MySQL
요구사항
타임라인 기능 설계에 대해 고민해보자..
타임라인 기능이란..
내가 어떤 게시글을 작성하면..
나를 팔로우하는 사람들은 내 게시글을
각자의 타임라인 페이지(보통 home 페이지이다.)에서 볼 수 있는...
기능이다.
각 방법에서 쓰기 관점과 조회 관점으로 나눠서 설명하겠다.
A 사용자가 B 사용자를 팔로우 하였고,
B 사용자가 게시글을 쓰는 상황이라 생각하자.
첫번째 방법
가장 간단하게 생각할 수 있는 설계 방법이다.
post Table 을 위와 같이 설계한다.
follow Table 은 위와 같이 설계한다.
시스템은 위와 같다.
쓰기 관점
자 이제..
B 사용자가 게시물을 작성하면
post Table 에 row 가 하나 추가될 것이다.
조회 관점
문제는 조회 관점이다.
A 가 타임라인 페이지를 진입 하는 순간..
다음과 같은 과정이 일어난다.
1. 우선 A 가 팔로우 하는 모든 member_id 를 follow Table 에서 조회 한다.
2. 조회한 follow list 를 post Table 로 가서 적재된 게시물을 싹 가져와야 한다.
참고
원래 정렬도 해야하겠지만, 주제와 상관 없어서 생략한다.
조인을 하건 쿼리 두번을 하건 주제와 상관 없어서 생략한다.
즉, user Table(그림엔 없음) 과 follow Table 이 일대다 관계이고,
follow Table 과 post Table 이 일대다 연관관계를 형성하고 있어서
조회 시점에 큰 부하가 존재하는 것이다. (fan-out 문제)
-> A 가 팔로우 한 사람이 엄청 많다고 생각해보자..
첫번째 방법의 문제점은..
보통 sns 는 게시글을 작성하는 것보다 게시글을 조회하는 상황이 훨씬 많다.
보통 조회와 쓰기는 서로의 성능을 희생시키면 본인의 성능이 올라가는 상황이 많다. (Trade-off)
두번째 방법에서 알아보자..
두번째 방법
조회 관점에서 일대다 연관관계를 없애보자..
위와 같이 timeline Table 을 추가하였다.
쓰기 관점
B 사용자가 게시물을 쓰면.. 다음 과정을 따른다.
1. post Table 에 row 하나를 저장
2. follow Table 에서 나(B)를 팔로우하는 사용자(A) 들을 리스트로 조회한다.
3. 나를 팔로우하는 사용자 리스트를 member_id 로 하여 timeline Table 에 하나씩 적재한다.
첫번째 방법과 비교하여
2번 과정이 조회에서 쓰기로 넘어왔고,
쓰기 성능에 부하가 생기게 되었다.
또한, timeline 이라는 Table 이 추가 되어 대용량 데이터 고민도 필요하게 되었다.
-> 실제 Twitter 에선 Redis 로 해결하며 적절한 TTL 을 정해둔 것 같다.
조회 관점
조회 관점에서는..
A 사용자가 타임라인 페이지에 들어가면..
다음 과정을 따른다.
1. timeline Table 에서 A 의 member_id 로 적재된, 게시물 post_id 를 리스트로 조회한다.
2. post_id 리스트로 post Table 에 가서 게시물을 조회한다.
첫번째 방법과 비교하여..
조회 관점의 부하가 많이 준 것을 생각해 볼 수 있다.
특히, 2번 로직은 일대일 매핑으로 이루어졌으며,
팔로우 관련 fan-out 문제가 타임라인 조회에서 게시물 쓰기로 넘어갔다.
하지만, 데이터 정합성(일관성) 문제가 생길 수 있다.
첫번째 방법에서 테이블이 추가되었고 그 데이터는 모두 다른 테이블에서 복제한 값이다.
정리 (시간복잡도, logN 은 생략하였다.)
-> 성능에 가장 영향을 많이 미치는 요소가 팔로우 수이고,
게시글 수는 캐시 외에 어찌할 방법이 없어보인다. (접근을 해야 보여주니..)
참고>
twitter 는 push model, facebook 은 pull model 을 따른다고 한다..
세번째 방법
두번째 방법에서 게시물 쓰기시..
나를 팔로우한 수만큼 timeline Table 에 쓰기 연산을 하는데..
이 로직은 굳이.. 게시물 쓰기 시점에 완료할 필요는 없다.
특히, 엄청난 팔로워를 보유한 연예인들이 게시물 하나 쓰면 대기시간이 엄청나게 오래 걸리니까..
비동기로 빼도 된다.
-> 그러면 첫번째 방법처럼 Post Table 에 row 하나만 추가하면 되는 수준의 시간복잡도를 기대할 수 있다.
그러나..
두번째 방법보다 더욱 데이터 일관성을 깊게 생각해야한다.
중복데이터를 비동기로 뺀 덕분에 데이터 일관성은 더 고민해봐야 한다.
-> 시스템 복잡도가 높다..
위 사진은 Twitter 의 System Design 이다.
쓰기 시점에 Tweet(게시물) 은 DB 로 적재하고
팔로워에 해당 게시물을 알리는 로직은 비동기로 빠져있는 것을 볼 수 있다.
조회 시점에도 Redis 를 이용하여 두번째 방법보다 빠르게 조회 할 수 있도록 한 것을 볼 수 있다.
참고>
아래 링크를 보면.. twitter 의 시스템을 설명해준다.
https://www.youtube.com/watch?v=KmAyPUv9gOY
'대규모 시스템 설계' 카테고리의 다른 글