배경
SNS 서비스를 개발하다 보면 피드 또는 타임라인 기능은 필수적입니다. 저희 Film it을 개발하면서도 다른 SNS(인스타그램, 페이스북, 트위터, 링크드인 등)처럼 사용자에게 실시간으로 업데이트되는 타임라인을 제공하기 위해 다양한 고민을 했습니다.
문제점
기존의 Pull 모델에서는 사용자가 타임라인을 요청할 때마다 실시간으로 데이터베이스에서 필요한 정보를 가져와서 조합합니다. 이 방식은 구현이 비교적 간단하지만, 요청할 때마다 사용자의 모든 팔로잉의 새로운 계시물들을 가져와서 타임라인을 조합해야 하기 때문에, 팔로잉수가 많아질 수록 타임라인을 만드는 시간이 계속해서 늘어날 수 있다고 판단하였습니다.
이를 해결하기 위해, 고민했던 방법들과 과정에 대해서 설명하겠습니다.
Push 모델
앞선 문제를 해결하기 위해 사용자가 타임라인을 요청할때, 타임라인을 pull 하는것이 아닌 계시물이 발행될때, push 하는 것으로 문제 해결을 시도했습니다.
Push 모델에서는 새로운 게시물이 생성될 때마다 해당 게시물의 메타데이터를 팔로워들의 큐에 미리 캐싱해 둡니다. 이렇게 하면 사용자가 타임라인을 요청할 때 이미 준비된 데이터가 있으므로 빠르게 응답할 수 있습니다. 즉, 사용자별로 Redis에 큐를 하나씩 두고, 새로운 포스트를 해당 큐에 추가하는 방식입니다.
Push 모델의 한계점
하지만 Push 모델에도 한계가 있습니다:
1. 인플루언서 문제: 팔로워가 많은 인플루언서의 경우, 새로운 게시물이 생성될 때마다 수많은 팔로워들의 큐에 데이터를 넣어야 하므로 오버헤드가 발생합니다. 이는 Redis에 부하를 줄 수 있으며, 심한 경우 Hot Key 문제로 이어질 수 있습니다.
2. 유휴 사용자 문제: 게시물이 발행될 때 모든 팔로워에게 캐싱되기 때문에, 현재 앱을 사용하지 않는 유휴 사용자에게도 데이터가 전달되어 리소스 낭비가 발생할 수 있습니다.
하이브리드 모델의 도입
이러한 한계를 극복하기 위해 Push와 Pull 모델을 결합한 하이브리드 모델을 도입했습니다.
인플루언서 문제 해결
• 팔로워 수 기준 적용: 팔로워 수가 일정 기준 이하인 일반 사용자에게는 Push 모델을 적용하여 빠른 응답성을 유지합니다.
• 인플루언서에 대한 Pull 모델 적용: 팔로워 수가 많은 인플루언서의 경우에는 Push 모델의 오버헤드를 줄이기 위해 Pull 모델을 적용합니다.
• 시스템 설계: 게시물이 생성될 때 해당 사용자의 팔로워 수를 확인하여 Push 또는 Pull 방식을 선택하도록 시스템을 설계했습니다.
유휴 사용자 문제 해결
• Redis 명령어 활용: LPUSHX, RPUSHX와 같은 Redis 명령어를 사용하면 키가 존재할 때만 큐에 데이터를 저장할 수 있습니다.
• 활성 사용자만 대상: 이를 통해 타임라인을 자주 사용하는 유저들의 큐에만 데이터를 캐싱합니다. 유휴 사용자의 경우 키가 존재하지 않으므로 데이터가 저장되지 않아 저장소의 공간을 효율적으로 사용할 수 있습니다.
• 캐시 크기 제한: 또한 타임라인 캐싱의 경우 큐의 크기를 제한합니다. 대부분의 사용자는 최신 포스트를 확인하기 때문에, 일정 수의 최신 게시물만 캐싱해도 캐시 미스(cache miss)가 발생할 확률이 낮습니다.
결론
SNS 타임라인 기능은 사용자 경험에 직접적인 영향을 미치는 중요한 요소입니다. Redis를 활용한 Push 모델은 빠른 응답성을 제공하지만, 모든 경우에 적용하기에는 한계가 있습니다. Push와 Pull 모델의 장점을 결합한 하이브리드 모델, Redis의 LPUSHX, RPUSHX 명령어를 통한 활성 사용자 관리로 성능과 효율성, 실시간성을 모두 잡을 수 있었습니다.
참고 자료
https://www.youtube.com/watch?v=92NizoBL4uA
https://www.infoq.com/presentations/Twitter-Timeline-Scalability/
https://droomii.tistory.com/18
https://algodaily.com/lessons/dive-into-facebook-newsfeed-architecture