읽기 성능 개선 보고서

2025. 8. 15. 08:59·Study/Architecture

테스트 개요

테스트 시나리오

  • 대상 기능: 읽기 병목 가능성이 높은 상품 목록 조회 API
  • 목표: 현 상태 기준 p95 응답 시간과 처리량 측정
  • 조건:
    • brandId ∈ {1..5}
    • sort ∈ {LATEST, PRICE_ASC, LIKES_DESC}
    • page ∈ [0, 49], size=20
  • 데이터 준비:
    • 유저: 100,000 건
    • 브랜드: 1,000 건
    • 상품: 1,000,000 건

데이터 기준은 아래 참고

더보기

트랜잭션 계산

상품 목록 조회 기준 (행사 미실시 월 가정, 숫자 라운딩 적용)

아래 값은 테스트 설계용 가정치이며 실제 트래픽과 다를 수 있음.
MAU 출처: https://v.daum.net/v/20241212165321851
DAU 계산 근거: https://sendbird.com/ko/blog/monthly-active-users-mau

1) 트랜잭션 정의

  • MAU(비행사월 추정): 1,190,000 → 1,200,000
    • 근거: 행사 월 기준 MAU 170만 -> 비 행사월 대비 30% 증가했을 것으로 가정
  • DAU(= MAU의 20%): → 240,000
    • 근거: 서비스 점착도 가정 (보수 10%~공격 30% 중 20% 채택)
  • 피크 동시 접속자 N(= DAU의 5%): → 12,000
    • 근거: 피크 시간대 집중 + 세션길이 고려 시 동접/DAU 1~5% 범위가 실무적으로 자주 쓰임. 여기선 5% 채택

2) 트래픽 볼륨 가정

  • 응답시간 R: 0.25초(=250ms)
    • 근거: 단일 조회 API의 실용 SLO(사용자 체감+프론트 여유)로 p95 200~300ms 범위 중 중간값 채택
  • 생각시간 Z: 5초
    • 근거: 목록 스크롤/판독/다음 페이지 전환까지 수초 단위 체류 가정(팀 로그로 보정 권장)
  • 목록 호출 비중 p_list: 80% (=0.7)
    • 근거: 브라우징 세션에서 목록 호출 비중이 큼(서비스별 60~90% 내 조정)

3) 처리량 계산

  • 공식: TPS = N / (R + Z)
  • 전체 TPS: 12,000 / (0.25 + 5) = 12,000 / 5.25 ≈ 2,286 req/s → ≈ 2,300 RPS
  • 상품 목록 TPS: 2,300 × 0.7 = 1,610 RPS ≈ 1,500 RPS

4) 테스트 목표 TPS

  • Baseline(스테이징/로컬 축소): 375 TPS (피크의 ~25% 수준)
  • Peak(계산 근사): 1,500 TPS (이론상 피크)
  • Stress(여유 검증): 1,800 TPS (피크 +20%)

 

 

테스트 환경

로컬 단일 노드 환경에서 진행

  • macOS / Apple M1 Pro / 32GB RAM / 512GB SSD
  • DB: MySQL 8.0.x (InnoDB) - Docker 컨테이너 실행
  • Redis: Docker 컨테이너 실행
  • k6: 로컬 환경 실행
  • 네트워크: 로컬 환경
  • 대상: 상품 목록 조회 API

 

 

테스트 스크립트

import http from "k6/http";

export const options = {
    stages: [
        { duration: "1m", target: 100 }
    ],
};

export default function () {
    const brandId = [1, 2, 3, 4, 5][Math.floor(Math.random() * 5)]; // 가장 상품이 많은 상위 5개
    const sort = "LIKES_DESC"; // 좋아요순 정렬
    const page = Math.floor(Math.random() * 10);
    const size = 20;

    http.get(
        `http://localhost:8080/api/v1/products?brandId=${brandId}&sort=${sort}&page=${page}&size=${size}`,
        { tags: { name: "GET /api/v1/products" }, responseType: "none" }
    );
}
  • 일정한 동시 사용자 수를 유지한 상태에서 API 응답 시간, 에러율, 처리량을 측정해 시스템이 안정적으로 처리 가능한 부하 수준과 병목 가능성을 파악
  • 정렬 조건은 캐시 테스트 제외하고 LIKES_DESC 고정

 

 

 

1. 개선 전

개선 전 구조

BRAND {  
    BIGINT ID PK "ID"  
    VARCHAR NAME "브랜드명"  
    VARCHAR DESCRIPTION "브랜드설명"  
}  

PRODUCT {  
    BIGINT ID PK "ID"
    BIGINT BRAND_ID FK "브랜드 ID"  
    VARCHAR NAME "상품명"  
    INT PRICE "가격"
    INT LIKE_COUNT "좋아요개수"
}

STOCK {
    BIGINT ID PK "ID"
    BIGINT PRODUCT_ID FK "상품 ID"
    INT QUANTITY "수량"
}
public ProductInfo.Summary getList(ProductCommand.Search command) {
    Page<Product> products = productService.getList(command.getBrandId(), command.toPageable(), command.getSort());
    if (products.isEmpty()) {
        return ProductInfo.Summary.empty();
    }

    List<Long> productIds = products.stream().map(BaseEntity::getId).toList();

    List<Long> brandIds = products.stream().map(product -> product.getBrand().getId()).distinct().toList();
    List<Brand> brands = brandService.getListByIds(brandIds);

    List<Stock> stocks = productService.getStocksByProductIds(productIds);

    return ProductInfo.Summary.from(products, brands, stocks);
}

페이징 및 정렬 처리된 상품 목록 조회한 다음, 브랜드 정보와 재고 정보를 각각 조회한 후 매핑하는 구조

 

SELECT
    b.id as brand_id, b.name as brand_name, b.description as brand_desc,
    p.id as product_id, p.name as product_name, p.price, p.like_count,
    s.quantity as stock
FROM product p
LEFT OUTER JOIN brand b ON p.brand_id = b.id
LEFT OUTER JOIN stock s ON p.id = s.product_id
WHERE p.brand_id = #{brandId}
AND p.deleted_at IS NULL
ORDER BY like_count DESC // 정렬조건
LIMIT 0, 20;

세 테이블을 한번에 조회하는 쿼리를 가정해 EXPLAIN 통해 지표를 확인하면 다음과 같음

select_type table type key key_len ref rows filtered Extra
SIMPLE p ref fk_product_brand 8 const 87952 10 Using where
Using filesort
SIMPLE b const PRIMARY 8 const 1 100  
SIMPLE s eq_ref UKkhabtqwr86p7x9mt2krib98tx 8 loopers.p.id 1 100  
-> Limit: 20 row(s)  (cost=30857 rows=20) (actual time=558..559 rows=20 loops=1)
    -> Nested loop left join  (cost=30857 rows=87952) (actual time=558..559 rows=20 loops=1)
        -> Nested loop left join  (cost=13305 rows=87952) (actual time=558..558 rows=20 loops=1)
            -> Sort: p.like_count DESC, limit input to 20 row(s) per chunk  (cost=4509 rows=87952) (actual time=558..558 rows=20 loops=1)
                -> Filter: (p.deleted_at is null)  (cost=4509 rows=87952) (actual time=2.51..518 rows=43292 loops=1)
                    -> Index lookup on p using fk_product_brand (brand_id=1)  (cost=4509 rows=87952) (actual time=2.51..501 rows=43292 loops=1)
            -> Constant row from b  (cost=96.6e-6..96.6e-6 rows=1) (actual time=0.00571..0.00589 rows=1 loops=20)
        -> Single-row index lookup on s using UKkhabtqwr86p7x9mt2krib98tx (product_id=p.id)  (cost=0.996 rows=1) (actual time=0.0415..0.0418 rows=1 loops=20)
  • Extra 의 Using where + Using filesort: 정렬 인덱스가 없어 대량 결과를 filesort
  • 실측 ANALYZE: 최초 스캔 후 인덱스가 없어 결과에서 filesore 진행 (~ 558 ms)

 

성능테스트 결과 ( VUSER:100 )

성능 지표

  • 총 요청 수: 471 건
  • 평균 응답 시간: 7.56 s
  • P95 응답 시간: 19.2 s
  • TPS: 6.7 req/s
  • 에러율: 11.25 %

 

분석

  • 평균 응답 시간은 7.56초, p95 응답 시간은 19.2초로 대부분의 요청이 매우 느린 상태였음
  • TPS는 6.7로 낮고, 에러율이 11.25%로 안정성이 떨어짐
  • 지연의 주 원인이 네트워크가 아닌 서버 내부 처리이며, DB 커넥션 풀 고갈로 인한 Unable to acquire JDBC Connection 예외가 다수 발생
  • DB 부하와 긴 커넥션 점유 시간이 병목의 핵심 원인으로 판단됨

→ DB 인덱스, 캐시, 쿼리 튜닝 등 서버 쪽 최적화 진행

발생 예외 예시

Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection 

 

 

개선 방안

  • 조회 로직 개선
    • 재고 정보 컬럼 추가 및 조회 로직 개선
  • DB 인덱스 추가
    • 정렬, 필터 조합에 맞춘 복합 인덱스 추가
    • EXPLAIN으로 실행 계획 검증
  • Redis 캐시 도입
    • TTL을 짧게 설정하여 자주 조회되는 초기 페이지 캐싱
    • DB 부하 완화 및 응답 속도 개선

 

 

 

2. 조회 로직 개선

  • 목표: 불필요한 로직을 제거하여 응답 속도 개선 및 TPS 향상
  • 부가 목적: 병목이 애플리케이션 로직인지 DB 접근 구조인지 검증

 

 

개선 내용

public class Product extends BaseEntity {
    @Enumerated(EnumType.STRING)
    @Column(name = "status",nullable = false)
    private SalesStatus status; // ACTIVE, INACTIVE, SOLD_OUT
public ProductInfo.Summary getList(ProductCommand.Search command) {
    Page<Product> products = productService.getList(command.getBrandId(), command.toPageable(), command.getSort());
    if (products.isEmpty()) {
        return ProductInfo.Summary.empty();
    }

    List<Long> brandIds = products.stream().map(product -> product.getBrand().getId()).distinct().toList();
    List<Brand> brands = brandService.getListByIds(brandIds);

    return ProductInfo.Summary.from(products, brands);
}

Product 에 판매상태와 품절여부를 알 수 있는 status 필드 추가 후 조회 로직에서 재고 조회 로직 제거

 

 

성능테스트 결과 ( VUSER:100 )

성능 지표

  • 총 요청 수: 507 건
  • 평균 응답 시간: 6.91 s
  • P95 응답 시간: 19.01 s
  • TPS: 7.27 req/s
  • 에러율: 12.62 %

 

결과 분석

  • 평균 응답 시간은 6.91초로 소폭 개선되었으나, TPS는 7.27로 큰 변화가 없음
  • 에러율은 12.62%로 오히려 소폭 증가
  • 조회 쿼리 일부를 제거했음에도 성능 개선 폭이 제한적이었으며 병목 원인이 애플리케이션 로직이 아님을 확인

 

 

 

3. DB 인덱스 추가

  • 목표: 정렬·필터 조건에 맞춘 복합 인덱스 적용으로 쿼리 실행 계획 최적화 및 DB 부하 완화

 

개선 내용

  • 상품 목록 조회에 사용되는 brand_id + 정렬조건 으로 복합 인덱스 생성
  • 개선 결과를 EXPLAIN, ANALYZE 로 확인
    • select_type: 쿼리의 형태. PRIMARY(바깥), DEPENDENT SUBQUERY(상관 서브쿼리) 등.
    • type: 접근 방법. 좋은 순서 대략 const > eq_ref > ref > range > index > ALL.
    • ALL이면 풀스캔.
    • key / possible_keys: 실제 사용 인덱스 / 사용 가능 후보 인덱스. 비어있으면 인덱스 미사용.
    • rows: 옵티마이저의 예상 스캔 건수(정확치 않지만 크기 판단 지표).
    • filtered: where로 추가로 걸러질 비율(%). 낮을수록 불필요 스캔이 많다는 뜻.
    • Extra: Using filesort(인덱스로 정렬 불가 → 임시 정렬), Using where(where로 필터) 등.

 

인덱스 도입 시 고려한 점

  • 단일 인덱스를 사용할 것인지, 복합 인덱스를 사용할 것인지
  • 어떤 컬럼을 인덱스로 선택할 것인지

 

단일 인덱스 vs 복합 인덱스

특성 단일 인덱스 복합 인덱스
적용 대상 하나의 컬럼 여러 컬럼 함께 사용
성능 최적화 단일 컬럼 대한 검색 최적화 여러 컬럼 결합한 조건에 최적화
공간 사용 적은 공간 사용 더 많은 공간 사용
업데이트 성능 인덱스 업데이트가 빠름 인덱스 업데이트가 느릴 수 있음
사용 예시 단일 컬럼 검색, 범위 검색 여러 컬럼에 대한 검색 조건이 포함된 쿼리
  • 상품 목록 조회 API의 경우 여러 조건이 결합된 쿼리 사용
  • 옵티마이저는 보통 테이블 당 하나의 인덱스를 사용

→ 검색 조건을 한번에 만족시킬 복합 인덱스 선택

 

복합 인덱스 순서

  • 복합 인덱스의 경우 작성된 순서대로 정렬을 진행하기 때문에 순서가 매우 중요함
  • 아래와 같은 규칙에 따라 순서 결정
    • 공통적으로 사용하는 필수 조건절 컬럼 우선
    • ‘=’ 조건 컬럼 우선
    • 대분류 → 중분류 → 소분류 컬럼 순
    • WHERE → ORDER BY 컬럼 순

 

어떤 필드를 선택할 지

그렇다고 검색에 사용되는 모든 필드와 경우의 수를 전부 인덱스에 추가한다면, 오히려 쓰기 성능이 떨어지고 메모리를 차지해 성능저하가 일어날 수 있음

카디널리티를 비교해 복합인덱스를 생성할 컬럼을 결정함

현재 정렬 조건은 다음과 같음

  • 좋아요순, 생성일순, 가격낮은순
  • 셋 다 중복도가 낮아 카디널리티가 높은 필드들이라고 판단해 복합 인덱스를 생성하기로 결정

 

 

복합 인덱스 생성

-- 최신순
CREATE INDEX idx_product_brand_created_desc ON product (brand_id, created_at DESC, id DESC);
-- 좋아요순
CREATE INDEX idx_product_brand_likes_desc ON product (brand_id, like_count DESC, id DESC);
-- 가격 낮은 순
CREATE INDEX idx_product_brand_price_asc ON product (brand_id, price ASC, id ASC);

 

 

EXPLAIN 결과

select_type table type key key_len ref rows filtered Extra
SIMPLE p ref idx_product_brand_likes_desc 8 const 90720 10 Using where
SIMPLE b const PRIMARY 8 const 1 100  
SIMPLE s eq_ref UKkhabtqwr86p7x9mt2krib98tx 8 loopers.p.id 1 100  
-> Limit: 20 row(s)  (cost=16910 rows=20) (actual time=3.13..4.16 rows=20 loops=1)
    -> Nested loop left join  (cost=16910 rows=9072) (actual time=3.13..4.14 rows=20 loops=1)
        -> Nested loop left join  (cost=6970 rows=9072) (actual time=3.05..3.19 rows=20 loops=1)
            -> Filter: (p.deleted_at is null)  (cost=6062 rows=9072) (actual time=2.99..3.07 rows=20 loops=1)
                -> Index lookup on p using idx_product_brand_likes_desc (brand_id=1)  (cost=6062 rows=90720) (actual time=2.99..3.04 rows=20 loops=1)
            -> Constant row from b  (cost=93.7e-6..93.7e-6 rows=1) (actual time=0.00367..0.00392 rows=1 loops=20)
        -> Single-row index lookup on s using UKkhabtqwr86p7x9mt2krib98tx (product_id=p.id)  (cost=0.996 rows=1) (actual time=0.0451..0.0454 rows=1 loops=20)

분석 및 비교

항목 인덱스 전 인덱스 후
정렬 처리 Using filesort 발생 filesort 제거
사용 인덱스 fk_product_brand idx_product_brand_likes_desc
조회 row 수 rows=87952 → 필터 후 43292 rows=90720 → 바로 20개
실행 시간 (actual time) 전체 558~559ms 전체 3.13~4.16ms
Extra Using where; Using filesort Using where만

 

 

성능테스트 결과 ( VUSER:100 )

성능 지표

  • 총 요청 수: 890 건
  • 평균 응답 시간: 3.67 s
  • P95 응답 시간: 10.37 s
  • TPS: 13.39 req/s
  • 에러율: 3.03 %

 

결과 분석

  • TPS가 6.7 → 13.39로 약 2배 증가하고, 총 요청 수도 2배 가까이 확대됨
  • 평균 응답 시간은 7.56초 → 3.67초로 절반 가까이 단축
  • 에러율이 11.25% → 3.03%로 감소하며 안정성이 크게 향상됨
  • 정렬 및 필터 조건에 맞춘 복합 인덱스 적용으로 filesort가 제거되고 쿼리 실행 시간이 밀리초 수준으로 단축됨
  • 다만 초 단위 응답 속도 자체는 여전히 느려 추가적으로 캐시를 활용한 구조 개선 필요

 

 

 

4. Redis 캐시 적용

목표: 자주 조회되는 데이터의 DB 접근을 캐시로 대체하여 지연 제거 및 처리량 극대화

 

개선 내용

  • 상품 개수 상위 50개인 브랜드 대상으로 기본 정렬인 최신 순으로 최초 조회한다고 가정해 최신순 LATEST 정렬에서 상위 100개 상품에 대해 캐싱
  • 나머지는 기존 DB 조회로 폴백

 

캐시 선택시 고려한 점

  • 로컬 캐시를 사용할 것인지, 글로벌 캐시를 사용할 것인지
  • 어떤 캐시 전략을 사용할 것인지

 

로컬 캐시 vs 글로벌 캐시

구분 로컬 캐시 글로벌 캐시
장점 매우 빠른 속도 (네트워크 통신 불필요)
캐시 서버 장애로부터 자유로움
서버 간 데이터 공유 용이
데이터 분산 저장 가능
수평 확장성 높음
단점 서버 재시작 시 데이터 유실(휘발성)
서버 간 데이터 불일치
애플리케이션 메모리 사용량 증가
로컬캐시에 비해 느린 속도
캐시 서버 장애 가능성
  • 여러 서버 인스턴스에서 동일한 데이터를 조회해야 함

→ 글로벌 캐시 선택

 

캐시 시스템 선택

→ Redis 선택

  • 메모리 기반 저장소로 읽기 속도 매우 빠름
  • 데이터 영속성 옵션 제공해 장애 시 복구 가능
  • Pub/Sub, Keyspace Notifications 등 캐시 무효화 처리에 유용
  • 클러스터링 및 샤딩을 통한 수평 확장성 확보
  • 많이 사용하는 캐시 시스템 중 하나로 운영 사례가 풍부해 유지보수 용이

 

캐시 전략 선택

1. 쓰기 전략

  • Look Aside 패턴 적용
    • 상품 목록 API는 변경 빈도가 낮고 조회 요청이 대부분임
    • 조회 요청이 동일 조건으로 반복 조회되는 경우가 많기 때문에 선택
    • 요청 시 캐시 확인 후 없으면 DB 조회 후 캐시에 저장
    • 불필요한 캐시 쓰기 방지, 초기 캐시 부하 완화
  • 캐시 워밍(Cache Warming)
    • 캐시 미스로 인한 성능 저하를 최소화하고자 선택
    • 배포 직후 또는 트래픽 피크 전 주요 데이터를 캐시에 선반영
    • 초기 요청 지연 최소화

2. 읽기 전략

  • Write Around 패턴 적용
    • 상품 정보 변경 시마다 캐시에 저장하지 않아 캐시 서버에 불필요한 부하를 주지 않으려 선택
    • DB에 직접 쓰고, 읽기 시에만 캐시 갱신
    • 쓰기 부하가 많은 경우 캐시 오염 최소화
    • 최초 조회 시 캐시 미스 발생 가능

 

 

성능테스트 결과 ( VUSER:100 )

캐시워밍 X

성능 지표

  • 총 요청 수: 133,853 건
  • 평균 응답 시간: 22.34 ms
  • P95 응답 시간: 43.75 ms
  • TPS: 2,229 req/s
  • 에러율: 0 %

 

캐시워밍 O

성능 지표

  • 총 요청 수: 148,216 건
  • 평균 응답 시간: 20.16 ms
  • P95 응답 시간: 39.82 ms
  • TPS: 2,469 req/s
  • 에러율: 0 %

 

결과 분석

  • 인덱스 적용 대비 TPS가 13.39 → 2,229로 약 166배 증가하며 처리량이 비약적으로 향상됨
  • 평균 응답 속도는 3.67초 → 22.34ms로 전환되어 지연이 사실상 해소됨
  • 에러율이 3.03%에서 0%로 감소해 안정성이 완전히 확보됨
  • 캐시워밍 적용 시 초기 요청에서도 캐시 미스가 제거되어 TPS가 2,469로 추가 향상됨

 

 

 

종합 결론

단계 총 요청 수 (건) 평균 응답 시간 P95 응답 시간 TPS (req/s) 에러율
개선 전 471 7.56 s 19.20 s 6.7 11.25%
조회 로직 개선 507 6.91 s 19.01 s 7.27 12.62%
DB 인덱스 추가 890 3.67 s 10.37 s 13.39 3.03%
Redis 캐시 적용 (워밍 X) 133,853 22.34 ms 43.75 ms 2,229 0%
Redis 캐시 적용 (워밍 O) 148,216 20.16 ms 39.82 ms 2,469 0%
  • 최종적으로 총 요청 수가 471건 → 148,216건으로 약 314배 증가
  • TPS가 6.7 → 2,469로 약 368배 향상
  • 평균 응답 시간은 7.56초 → 20.16ms, p95 응답 시간은 19.20초 → 39.82ms로 단축되어 응답 속도가 99% 이상 개선
  • 에러율이 11.25% → 0%로 감소하여 안정성이 완전히 확보됨
  • 캐시워밍 적용으로 초기 요청에서도 캐시 미스 없이 안정적인 성능 유지

 

이번 성능 개선 과정은 상품 목록 조회 API의 읽기 병목을 제거하고 처리량과 안정성을 극대화하는 것을 목표로 진행하였다.

각 단계를 요약하면 다음과 같다.

  1. 개선 전
    • DB 부하와 커넥션 풀 고갈로 인한 지연과 높은 에러율(11.25%)이 발생
    • 평균 응답 시간은 7.56초, TPS는 6.7로 실서비스 대응이 어려운 수준
  2. 조회 로직 개선
    • 불필요한 재고 조회를 제거했으나, 응답 시간과 TPS 개선 폭은 미미
    • 병목 원인이 애플리케이션 로직이 아님을 확인
  3. DB 인덱스 추가
    • 정렬·필터 조건에 맞춘 복합 인덱스 적용으로 쿼리 실행 시간이 크게 단축
    • TPS가 약 2배 증가하고, 에러율이 70% 이상 감소
    • 그러나 여전히 초 단위 응답 속도가 유지되어 구조적 한계 존재
  4. Redis 캐시 적용
    • DB 조회를 캐시로 대체하여 평균 응답 속도가 밀리초 단위로 전환
    • TPS가 약 166배 증가하고(13.39 → 2,229) 에러율 0%로 안정성 완전 확보
    • 캐시워밍으로 초기 요청 성능까지 안정화

 

앞으로의 개선 계획

  1. TTL 재설계
    • 현재 TTL이 다소 길어 변경 데이터가 오래 반영되지 않는 문제 가능성 있음
    • 정보 변경 주기를 고려해 TTL을 단축하고 다른 쓰기 전략을 고민해 볼 필요가 있음
  2. 쓰기 전략 개선
    • 현재 Write Around 방식은 변경 시 캐시를 바로 갱신하지 않아 정합성 지연이 발생 가능
    • 데이터 수정 시 Write Through 전략 또는 무효화로 캐시와 DB를 동시에 갱신하는 방식 고려

 

마무리

이번 성능 개선 작업은 상품 목록 조회 API에 DB 인덱스 적용과 Redis 캐시 도입으로 성능 안정화 및 응답속도 향상을 통한 읽기 병목 제거를 목표로 작업했다. 그 결과 응답 속도와 에러율이 크게 줄어들고, TPS는 늘어나는 성과를 달성했다. 단일 조회 API 여도 구조 개선과 캐시 전략 설계 만으로 서비스 속도와 처리량을 크게 증가시킬 수 있음을 확인했다.

다만 의외였던 점은 애플리케이션 로직 개선이 읽기 성능 향상에 미치는 영향이 거의 없었다는 것이다. 인덱스 최적화는 DB 부하 완화와 안정성 향상에 뚜렷한 효과가 있었지만 완전한 병목 해소에는 한계가 있어서 결국 캐시로 보완해야 했다.

캐시 전략에 대해 더 깊게 고민해 보아야할 필요성도 느꼈다. 현재 TTL이 다소 길어 데이터 변경이 제때 반영되지 않을 가능성이 있고 데이터 수정 시 데이터 불일치가 발생할 수 있다. 앞으로 TTL 재설계와 쓰기 전략 보완을 통해 정합성도 만족시킬 수 있는 구조로 개선해봐야겠다. 캐시는 한 번 적용하고 끝나는 것이 아니라 트래픽 패턴과 데이터 변경 주기에 맞춰 끊임없이 조정하는 운영 전략이 필요하다는 점을 다시금 깨달았다.

 

 

참고

https://khdscor.tistory.com/51

https://bcp0109.tistory.com/384

https://kk-programming.tistory.com/83

https://toss.tech/article/cache-traffic-tip

https://inpa.tistory.com/entry/REDIS-%F0%9F%93%9A-%EC%BA%90%EC%8B%9CCache-%EC%84%A4%EA%B3%84-%EC%A0%84%EB%9E%B5-%EC%A7%80%EC%B9%A8-%EC%B4%9D%EC%A0%95%EB%A6%AC

https://medium.com/garimoo/%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%A0%88%EB%94%94%EC%8A%A4-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-01-92aaa24ca8cc

반응형

'Study > Architecture' 카테고리의 다른 글

주문과 결제, 어디까지 한 트랜잭션으로 묶어야 할까?  (2) 2025.08.29
장애 대응 시스템 구축하기  (0) 2025.08.22
캐시 구조 개선  (0) 2025.08.22
멱등성을 고려한 좋아요 기능 설계  (5) 2025.08.01
Validation, 어디에 어떻게 두어야 할까?  (3) 2025.07.18
'Study/Architecture' 카테고리의 다른 글
  • 장애 대응 시스템 구축하기
  • 캐시 구조 개선
  • 멱등성을 고려한 좋아요 기능 설계
  • Validation, 어디에 어떻게 두어야 할까?
haylee
haylee
개발하면서 보고 듣고 느낀 것들을 정리하는중
  • haylee
    haylee
    haylee
    • 홈
    • GitHub
    • LinkedId
    • 분류 전체보기 (19)
      • Project (6)
        • 회고 (6)
      • Study (13)
        • Database (3)
        • Test (1)
        • Architecture (9)
  • hELLO· Designed By정상우.v4.10.1
haylee
읽기 성능 개선 보고서
상단으로

티스토리툴바