성능 니즈도 있고 집계 니즈도 있고
성능 니즈야 다들 아는걸테고
집계 니즈는 일간/월간 조회수, 중복조회 제외 조회수 등이 있음
모두 제대로 다루면 이야기가 길어지니까 성능 니즈만 제대로
내가 고차원적인 조회 수 성능 최적화를 해본것도 아니고, 한시간만에 쓴 글이라 잘못된 점이 있을 수 있음
오류가 있다면 댓글로 지적 부탁
분석
성능 니즈는 잦은 락 때문에 생기는거니까, 락을 줄여주면 됨
여기까지는 당연히들 아는 이야기일거고
성능 개선은 먼저 서비스 특성을 파악해야함
수많은 유저들이 자유롭게 글을 작성하고 조회하는 서비스는 원래 쓰기와 읽기의 비율이 크게 차이나지 않지만
성능 개선 이야기 하는데 대충이라도 자료를 참고해보겠음
가볍게 보는거니까 나무위키 데이터를 참고함
2022년 기준 하루 페이지 뷰는 1억 7천만회, 게시물은 100만건
예상 외로 읽기가 170배나 되지만, 이건 글 하나당 몇만번의 조회가 일어나는 실베 + 시간이 지나면서 쌓이는 조회 수 때문임
1위 갤러리인 리그오브레전드 갤러리만 봐도 방금 올라온 글들의 조회 수는 10 미만인걸 보면
순간적인 부하 기준에서는 쓰기와 읽기 비율의 차이가 거의 없다고 봐도 됨
이건 개념글도 마찬가지임
그리고 오래된 글은 더더욱 조회되지 않음
실베도 하루도 안돼서 조회 수가 안오르기 시작
정리해보자면,
- 조회 수 업데이트 경합으로 인한 성능 저하가 발생할 일이 별로 없음
- 특정 게시판의 글의 글은 조회 수가 높을 가능성이 있음
- 작성된지 오래된 글은 조회 수 증가가 거의 일어나지 않음
- 한적한 게시판이더라도 예상치 못한 조회 부하가 가해질 가능성이 있음, 특정 글이 어딘가에 박제돼서 외부 트래픽이 유입된달지
- 그런데 누가 하꼬 게시판 글을 박제할까? 핫한 게시판 글을 박제하지
이런 특성으로 미루어보아
게시판 ID이라는 정적인 요소와
작성 시각, 시간당 조회수 상승량이라는 동적인 요소로 성능 최적화를 진행할 수 있을 것 같음
시간당 조회수 상승량은 여기서 다루기에는 너무 복잡하고, 최적화 한다 해도 배보다 배꼽이 클 가능성이 높음
그래서 이번에는 게시판 ID와 작성 시각만 다뤄볼 생각
분석은 여기까지만 하고, 적용할 수 있는 조회수 업데이트 방안을 리스트업 해보겠음
해결책
1. 매번 조회할때마다 +1 업데이트
- 가장 간단함
- 디시 일반 갤러리처럼 게시물은 많고 조회 수는 얼마 안될때는 이것도 방법임
- 실베 같이 조회 수가 많은 경우에는 성능 문제가 생김
2. 서버마다 in memory 카운팅해서 주기적으로 db에 flush
- 싸고 쉽게 성능을 개선시킬 수 있음
- 서버가 갑자기 디지거나, 배포 시 graceful을 보장해주지 않으면 데이터가 일부 누락될 위험이 있음
- 조회수를 실시간으로 확인할 수 없음
- view 전용 조회수는 별로 중요한게 아니라서 이런 방법을 쓸 수도 있음
3. redis에서 카운팅하다가 주기적으로 db에도 업데이트
- 무한히 많은 데이터가 redis에 올라감
- 성능이야 개선되겠지만 도저히 가성비가 안나오는 해결책
4. 일반 글은 매번 조회할때마다 +1 업데이트, 실베 글은 redis에 카운팅하다가 주기적으로 db에도 업데이트
- 조금 복잡해지지만 성능 문제는 상당히 개선됨
- 실베 글이 늘어날수록 redis에서 관리하는 데이터도 무한히 늘어남
5. 일반 글은 매번 조회할때마다 +1 업데이트, 실베 글은 최근 글만 redis에 카운팅(write-back)
- 더욱 복잡해지지만, redis에서 관리하는 데이터가 최근 글 개수로 제한됨
- flow: 글 작성 시 redis에 조회수 카운터 등록
- flow: 글 조회 시 redis에서 조회수 get
- flow: redis에 없으면 DB에서 가져온 글 데이터의 조회수를 사용하고 db에 카운팅
- flow: redis에 있으면 redis에서 가져온 조회수를 사용하고 redis에 카운팅
- flow: 배치용 별도 스레드에서 삭제 주기마다 해당되는 key 목록을 가져오고(ex: scan dcbest:202230214:*)
- flow: key 하나마다 lock을 걸고 값을 db에 업데이트 한 후, key 삭제
- flow: 이후 글 조회는 자연스럽게 db에 카운팅하게 됨
- redis 데이터 삭제 및 db 동기화 속도보다 실베 글의 업로드 주기가 빠른 경우에, 여전히 redis에서 관리하는 데이터가 무한히 늘어남
- 실제 실베 업로드 주기를 보면 그럴 일 없긴 함
6. 일반 글은 매번 조회할때마다 +1 업데이트, 실베 글은 최근 글만 redis에 카운팅, 동시에 db에도 카운팅(write-through)
- 더더욱 복잡해지지만 스케일아웃이 가능해짐, 가성비는 별로
- 그러나 매 건마다 db에도 카운팅하면 lock으로 인한 성능 저하가 다시 발생함
- 메시지 큐 등을 사용해서 조회 이벤트를 모아놨다가 별도 스레드(혹은 프로세스)에서 벌크 업데이트를 수행해야함
- redis 데이터는 만료일시를 줘서 자동으로 삭제되도록 처리해도 되고 수동으로 해도 되고, 중요한건 6번 해결책 같이 lock을 걸 필요 없다는거
- redis 데이터 만료 시점에 조회 이벤트가 모두 처리되지 않았다면, 일시적으로 조회 수 미스매치가 발생할 수 있음
7. 집계 니즈 대응
조회수 조작을 막기 위해, 유니크 한 쿠키 값 등을 기준으로 중복 업데이트를 방지해야할수도 있음
다만, 중복 방지를 실시간에 가깝게 수행하면서 정확한 값을 보장하려면 너무 큰 자원이 소모될 수 있음
그러므로 view용 조회 수 집계와 정밀 분석용 조회 수 집계를 투 트랙으로 진행하는 것도 방법임
view용 조회 수 집계는 redis Hyperloglog를 사용하는 것을 추천하고
정밀 분석용 조회는 접속 로그를 모두 저장해서 분석하는 것을 추천함
좋아요. 심도가 깊어지는 글입니다 - dc App
트래픽 특성을 분석해 다 다르게 구현하는게 인상적이네요 - dc App
개추드립니다
성능 개선 방법을 알려주려고 글을 쓴건 아니고 단순한 주제여도 이렇게 파고들어가며 고민하는 경험을 해보라고 쓴거임 바로 이런 포폴이 필요한거라고
주딱의 품격.. ㅆㅅㅌㅊ