ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 캐싱으로 조회수기능 개선하기
    TIL 2024. 11. 27. 14:57

    캐싱으로 조회수기능 개선하기

     

    기존 조회수기능 방식)

    DB update 조회수+1 

    async increaseViewCount(reviewIdx: number): Promise<void> {
        await this.prismaService.reviewTb.update({
          where: {
            idx: reviewIdx,
          },
          data: {
            viewCount: {
              increment: 1,
            },
          },
        });
    }

     

     

     

    기존 조회수 기능의 문제점)

    1. 트래픽이 많아질수록 인덱싱된 데이터의 수정작업이 계속 이루어지게되면서 DB작업에 부담이 늘어난다.
    2. DB작업은 컴퓨터작업의 가장 느린부분으로 작업량이 늘어날때 병목현상이 일어나서 시스템 전체의 성능이 떨어질 수 있다.
    3. 그래서 불필요하게 DB에서 입출력작업이 많이 발생하지 않도록 해야한다.

     

     

    캐싱기법 도입시의 장점)

    1. 캐싱기법은 조회수 증가작업을 일시적으로 메모리에 모아놨다가 한번에 할 수 있도록 해준다. 따라서 기존에 수시로 DB데이터 수정작업이 이루어지던 것을 한번의 메모리의 입출력작업으로 대체한다. 이처럼 주기적으로 캐시에서 DB로 데이터를 동기화해줌으로써 DB부하를 줄인다.
    2. 캐싱은 메모리 데이터를 저장함으로써 디스크의 I/O보다 훨씬 빠르게 동작한다.

     

    하지만 캐싱을 통해 주기적으로 조회수를 업데이트하는 것만 반영한다면 실시간으로 조회수를 보여줄 수 없게된다.

    이를 해결하기위해 조회수를 증가시키는 작업뿐만아니라 조회수를 표시할 때도 캐시를 이용해야한다.

     

     

    캐싱기법 도입시의 단점과 보완)

    1. 캐시와 DB의 데이터 일관성을 유지하기 위해 동기화 전략이 필요하다

    -> 캐시된 데이터를 주기적으로 DB에 반영했다.

     

     

     

    캐싱을 통한 실시간 조회수 반영)

    • 조회수 증가 시 : 조회수 증가를 캐시에 저장.
    • 조회수 표시 시 : 조회수를 표시할 때도 캐시에서 가져와 실시간으로 보여준다.

     

     

    캐싱이 적용된 조회수 기능 코드)

     

    조회수 증가 메서드

    //조회수 증가 메서드
    async increaseViewCount(reviewIdx: number): Promise<void> {
        await this.redis.incr(`review:${reviewIdx}:viewCount`);
      }

     

    조회수 불러오기 메서드

    1.캐싱된 조회수 찾기

    2.캐싱된 조회수가 없다면 DB에서 찾기

    
    // 조회수 불러오기 메서드
    async getViewCount(reviewIdx: number): Promise<number> {
        //메모리에서 조회수를 불러오기
        let viewCount = parseInt(
          await this.redis.get(`review:${reviewIdx}:viewCount`),
          10,
        );
    
        //없다면 DB에서 조회수를 불러오기
        if (!viewCount) {
          const review = await this.getReviewByIdx(reviewIdx);
          viewCount = review.viewCount;
    
          await this.redis.set(`review:${reviewIdx}:viewCount`, viewCount);
        }
    
       return viewCount;
    }

     

    주기적으로 캐싱된 데이터 DB 동기화

    1. 생성자내부에서 실행하여 객체 생성 즉시 주기적으로 동기화 진행

    2.동기화한뒤 캐싱된 데이터 삭제

    //주기적으로 캐싱된 조회수 DB에 동기화 
    //생성자 내부에서 실행하여 객체생성 즉시 주기적으로 동기화
    setInterval(
      async () => {
        const keys = await this.redis.keys(`review:*:viewCount`);
        for (const key of keys) {
          const reviewIdx = key.split(':')[1];
          const viewCount = await this.redis.get(key);
          await this.prismaService.reviewTb.update({
            where: {
              idx: parseInt(reviewIdx, 10),
            },
            data: {
              viewCount: parseInt(viewCount, 10),
            },
          });
        }
      },
      10 * 60 * 1000,
    );

     

     

     

     

Designed by Tistory.