카운트 증가 로직 성능 향상을 위한 방법 :: 소림사의 홍반장!
 
 
각각의 요청마다 조회 수나 노출 수 등을 증가시켜야 한다면 어떻게 처리하는 것이 좋을까?
단순하게는 요청이 올때마다 +1씩 업데이트를 해주면 된다.
하지만 대량의 트래픽이 발생되는 환경에서 매 번 많은 비용이 소모되는 디비 연결과 update가 이루어진다면 부하를 감당하지 못 해 장애로 연결될 것 이 뻔하다.
 
개선을 위한 방법 중 하나는, 로컬 메모리로 매 번 요청되는 카운트 증가를 관리하고 일정시간마다 한 번 씩 모아서 db에 update하는 방법이다.
참고용으로 코드를 적어둔다.
 
 
흐름
 
  • 카운트 증가 요청 시 매번 countMap에 누적 (CountService.addReadCount)
  • 매 5초마다 countMap의 누적 카운트를 DB로 업데이트 (ScheduledTasks.addCountInDb)
 
 
 
public class CountService {
 
   public void addReadCount(int pageNo) {
 
      CountHelper countHelper = CountHelper.getInstance();
      countHelper.addCount(pageNo);
 
   }
}
 
 
public class CountHelper {
 
    private static CountHelper instance;
    private ConcurrentHashMap<Integer,AtomicInteger> countMap;
 
 
    public static CountHelper getInstance() {
        if ( instance == null ) {
            instance = new CountHelper();
        }
        return instance;
    }
 
    public CountHelper() {
        this.countMap = new ConcurrentHashMap<>();
    }
 
    public void addCount(Integer mediaNo) {
        countMap.putIfAbsent( mediaNo, new AtomicInteger(0) );
        countMap.get(mediaNo).incrementAndGet();
    }
 
    public boolean isEmpty() {
        return this.countMap == null || this.countMap.size() <= 0;
    }
 
    public Map<Integer,AtomicInteger> getCountMap() {
        return this.countMap;
    }
 
    public void init() {
        this.countMap.clear();
    }
 
}
 
 
@Component
public class ScheduledTasks {
 
    @Autowired private CountRepository countRepository;
 
    @Scheduled(fixedDelay = 5000L)
    public void addCountInDb() {
 
        CountHelper countHelper = CountHelper.getInstance();
 
        if ( !countHelper.isEmpty() ) {
 
            Map<Integer, AtomicInteger> countMap = new HashMap<>();
            countMap.putAll(countHelper.getCountMap());
            countHelper.init();
 
            countRepository.addReadCountNum(countMap);
        }
    }
 
 
}
 
 
@Repository
public class CountRepository {
 
   public int addReadCountNum(Map<Integer,AtomicInteger> map) {
      int result = 0;
      for(Map.Entry<Integer,AtomicInteger> entry : map.entrySet() ) {
         CountVo vo = new CountVo( entry.getKey(), entry.getValue().intValue() );
         result += update("addReadCountNum", vo );
      }
      return result;
   }
}
 
 
 
이 방법은 CountHelper 인스턴스 생성, countMap의 데이터를 복사하고 초기화하는 순간의 동기화 이슈가 발생할 가능성은 있기때문에 데이터의 누락이 있을 수 있다. 극히 적은 확률이니 단순 통계 용도라면 전혀 무리 없을 듯하나 염두는 해 두시길.
요청에 대해 100% 처리를 보장하는 좀 더 향상된 방법도 다음에 정리해봐야겠다.
 
 
 
 
 
 

다른 카테고리의 글 목록

Dev. 자바/참고소스 카테고리의 포스트를 톺아봅니다