Post

서비스 최적화

서비스 최적화


개요

  • 서비스 전반에 걸친 최적화 작업의 기록입니다.


배경

  • 저는 회사에서 ‘코크플레이‘라는 이커머스 서비스 운영에 참여하고 있습니다. (링크)
  • 위 서비스는 각종 기술부채로 인해 상품목록 조회, 결제 등 핵심적인 기능의 속도가 느렸으며 이벤트로 트래픽이 몰리는 날에는 서비스 전체에 장애가 발생했습니다.
  • 이에 대응하여 안정적인 서비스 운영을 위해 서비스 최적화 작업을 진행했습니다.


장애 분석

  • 분석 결과 주요 장애 원인은 RDB였습니다.
    • 이벤트로 대량의 트래픽이 유입될 때 WAS, Redis 등의 서버는 정상적으로 작동했지만 RDB만 CPU 사용량이 100%를 찍으면서 장애가 발생했습니다.
    • 특히 이커머스 서비스 ‘코크플레이’는 현재 1대의 MySQL 서버만 사용하고 있어 장애가 서비스 전체로 전파되는 문제가 있었습니다.
    • INSERT, UPDATE의 비중은 상대적으로 매우 적었고 SELECT가 부하의 대부분을 차지했습니다.
  • cf) 코크플레이의 주요 트래픽 패턴
    • ‘코크플레이’는 평소에는 초당 500 미만인 낮은 빈도의 request가 발생합니다.
    • 그러나 특정 이벤트의 경우 광고를 통해 트래픽이 유입되므로 많게는 초당 1만건이 넘는 request가 발생합니다.


개선 목표

위 분석에 따라 2개의 개선 목표를 설정했습니다.

  • 1)read-only DB , 캐싱 등의 방법을 이용하여 가능한 RDB의 사용량을 줄이기
  • 2)쿼리 최적화를 통해 RDB를 효과적으로 사용하기


유의미했던 최적화 작업 내용

read-only DB

  • Amazon RDS의 Read Replicas 서비스를 사용하여 대부분의 SELECT 쿼리가 read-only DB에서 실행되도록 변경했습니다.
  • 덕분에 DB에 가해지는 부하를 분산시켰습니다.

캐시 적용

  • 상술한대로 RDB에 주로 SELECT 쿼리가 실행되었습니다
  • 모든 회원에게 공통으로 보여지는 데이터에 대해 Redis를 이용하여 캐싱했습니다.

집계 테이블을 이용하여 집계와 조회 쿼리 분리

  • ‘코크플레이’의 상품 추천 로직 중 ‘최근 많이 구매한 상품’ 추천 로직이 있습니다.
    • 위 로직에 맞는 상품목록을 얻기 위해 매번 특정 기간의 구매내역을 전부 카운트하는 쿼리가 실행됬습니다.
    • 그런데 특정 상품의 경우 최근 주문량이 많아 40초 이상 걸리는 무거운 쿼리가 발생했습니다.
  • 위 문제를 해결하기 위해 별도의 스케쥴러를 통해 집계테이블을 갱신하고 서비스에서는 집계테이블을 사용하도록 수정하여 문제를 해결했습니다.

Spring 로직 최적화

  • 동일 request scope 내에서 동일한 엔티티를 데이터베이스에서 가능한 한 번만 조회하도록 수정했습니다.
    • 기존 레거시 코드에서는 무분별하게 데이터베이스를 조회하고 있었습니다
  • SUM, COUNT 등의 계산은 가능한 WAS에 위임했습니다. WAS는 스케일 아웃이 용이하기 때문입니다.
  • 관리자의 ‘통계 다운로드 기능’의 경우 대량의 데이터를 가공해야 하기 때문에 응답이 늦습니다.
    • 일부 관리자가 응답을 기다리지 않고 여러 번 다운로드를 시도하는 문제가 발생했습니다.
    • Redis의 setIfAbsent 명령어를 이용한 Lock을 적용하여 동시에 여러 번 시도할 수 없도록 수정했습니다.

쿼리 최적화

  • Subquery를 Join으로 대체, Index 적용 등의 잘 알려진 방법으로 쿼리를 최적화하였습니다.
  • *배송상태 컬럼의 경우 selectivity 자체는 크지 않지만 인덱스로 사용했습니다.
    • 조회 시 주로 ‘배송 진행 중’ 상태를 조회하며 ‘배송 진행 중’ 인 데이터의 비율이 1% 대이기 때문에 인덱스의 이점을 살릴 수 있기 때문입니다.

메인페이지 비동기 처리

  • 메인페이지에서는 많은 종류의 데이터를 조회합니다.
    • 기존에는 조회를 순차적으로 처리했습니다.
    • CompletableFuture.allOf(…futures).join() 으로 조회를 비동기 처리하도록 변경하였습니다.
    • RDB에 가해지는 부담을 줄인 것은 아니지만 응답속도가 빨라짐으로써 UX가 개선되었습니다.


결과

  • 최근 진행된 이벤트에서 초당 1만의 Request를 안정적으로 처리했습니다.
This post is licensed under CC BY 4.0 by the author.