조회수 증가하기 - With Redis -2- (임시)

영슈·2023년 8월 29일
0

앞선 글에 이어서 설명하겠다.

사용자 Identification

UV ( Unique Visitors ) 또는 UB ( Unique Browsers ) 로 구분
1. 검출 단위에서 Access Log 를 읽음
2. WAS Request 부분에서 HashSet 기반 자료구조로 식별
=> Access Log 는 처리 시간이 오래 걸리며 , 실시간 상황 인지 불가능
=> HashSet 역시 문제점이 있음.

HashSet 기반의 문제점

  1. 대규모 사용자를 상대로 할 시 비용이 많이 듬.
  2. 조회수가 충분히 클 시 완벽히 정확한 조회수는 필요없음.
  3. HashSet 이 매우 커질 시 , 충돌 위험 및 시간이 오래 걸림.

HLL 이란

사실 HLL 이란거에 대해서 완벽하게 조사하고 이해한 것은 아니다.
그저 , 어떻게 개발이 됐는지 와 효율적인지에 대해서만 설명하려고 찾아봤다.

  • 2007 년 발표
  • 유일 원소 개수 ( cardinality ) 추정 사용하는 확률적 자료구조
  • Redis 에서는 자체 지원 ( 2.8.9 >= )

HashSet VS HyperLogLog

  1. HashSet : 10,447,016 Byte Memory , 67,801 Cardinality , 0% Relative Error
  2. HyperLogLog : 512 Byte Memory , 70,002 , 3% Relative Error
    => 정확도는 3% 차이나나 , 메모리 사용량은 1/2500 정도로 차이남!

Nazar

  • nazar 는 앞서 설명한 , Reddit 의 Consumer
  • postId 와 userId 를 활용해서 , 해당 사용자가 조회수를 올릴때 유효한지 검증한다.

Code

export async function nazar(postId:string,userId:string){
  const key = `postViewed:${userId}`;
  try{
    const result =  await redisClient.sIsMember(key,postId);
    return result;
  }
  catch {
    throw Error
  }
}
  • Redis 에서 제공하는 Set을 이용한다.
  • set 에는 userId 를 key 로 , postId들을 value 로 넣는다.
  • set 에 있을시 , 중복이므로 밑에 있는 abacus 를 실행하지 않는다.

abacus

  • Reddit 의 Consumer
  • postId 와 userId 를 활용해 , 조회수를 증가시키고 Set에 저장한다.

Code

export async function abacus(postId:string){
  try{
    const key = `postViews:${postId}`;
    const log = `postViewed:${userId}`;
    await redisClient.pfAdd(key,userId);
    await redisClient.sAdd(log,postId);
  }
  catch{
    throw Error
  }
}
  • Set 은 120개 정도 postId를 넣었을 시 , 9816 Byte 정도를 차지한다. ( memory usage 로 확인 )
  • HLL 은 550개 정도 postId를 넣었을 시 , 1592 Byte 정도를 차지한다.
    ( 550개 넣었는데 , 543 개로 count 되긴 한다. ) - UUID 에서 중복 된걸수도 있음.

Crontab

  • 설명 외 , 불필요한 부분은 삭제 했다.
export default new CronJob(
  "* 2 * * *",
  async () => {
    await deletePostSets();
    const expiredTime = new Date(Date.now() - expire);
    const posts = await PostRepository.find({
        where: { deletedAt: LessThan(expiredTime) }
      });
    if (posts) {
      try {
        posts.forEach(post=>deletePostViews(post.id));
      } catch (err) {
        logger.error(err);
      }
    }
  },
);
export async function deletePostSets(){
  try{
    const keys = await redisClient.keys('postViews:*');
    if(keys)await redisClient.del(keys);
    return;
  }
  catch{
    throw Error
  }
}
  • 새벽 2시 실행하는 Cron 으로 , Redis에 존재하는 Set 과 HLL 을 관리한다.
  • postViews 를 전부 삭제해서 , 조회수를 초기화 한다.
  • 삭제된 post 를 검색한 후 , 해당 post에 대한 HLL을 삭제한다.
profile
https://youngsu5582.life/ 로 블로그 이동중입니다~

0개의 댓글