본문 바로가기

Spring Boot/Cache

캐시 아키텍처와 종류 : 캐시 계층 구조

캐시 계층 구조는 어떻게 설계해야 하는가

 

- 캐시 계층 구조(L1, L2, L3)의 개념

- 애플리케이션, DB CDN 등 다양한 수준에서 캐시가 어떻게 동작하는가

- 각 계층별 캐시의 목적과 역할 구분하기

- 실제 서비스 환경에서 여러 캐시 계층이 함께 동작하는 구조

- 대규모 트래픽 환경에서 캐시 계층 구조가 성능에 미치는 영향

 


캐시 계층 구조 개요

캐시는 시스템의 여러 위치에서 동작할 수 있음

캐시가 배치되는 위치에 따라 L1 / L2 / L3 캐시로 구분됨

각 계층은 접근 속도, 저장 용량, 유지 비용이 다르며 "가까운 곳일수록 빠르고, 비쌀수록 용량이 작다" 라는 특성을 가짐

 

계층 위치 속도 용량 주 용도
L1 CPU, 앱 메모리 매우 빠름 작음 즉시 접근 가능한 데이터 캐싱
L2 애플리케이션 외부(Redis, Memcached 등..) 빠름 중간 여러 서버간 공유 캐시
L3 CDN, DB 캐시, 프록시 서버 등등.. 상대적으로 느림 전역적 데이터 배포 및 캐시

L1 / L2 / L3에 따른 데이터 캐싱

 

캐시의 계층적 접근 흐름

 

사용자가 요청함

- > L1(CPU, 애플리케이션 메모리)에서 데이터를 찾음

- > 없으면 L2(애플리케이션 외부, Redis 등)에서 데이터를 찾음

- > 없으면 L3(CDN, DB캐시, 프록시 등)에서 데이터를 찾음

- > 없으면 DB로..

 

상위 캐시에서 하위 캐시로 이어짐

 


L1 캐시(애플리케이션 레벨 캐시, CPU)

 

1. 개념

L1은 가장 가까운 계층으로, 애플리케이션 내부 메모리에 존재함

CPU의 L1 캐시처럼 프로그램이 실행중인 프로세스에서 즉시 접근할 수 있어서 지연(레이턴시)이 거의 없음

 

예 : HashMap, ConcurrentHashMap, Caffeine, Guava Cache

 

2. 예제

import java.util.*;

public class AppLevelCache {
	private static final Map<String, String> cache = new HashMap<>();
	
	public static String getData(String key) {
		if(cache.containsKey(key)) { // cache에 key가 존재한다면, 검증
			return "[L1 Hit] " + cache.get(key); // Hit
		}
		// 없다면, DB에서 데이터를 가져오고 cache에 값을 넣음, Miss
		String data = "데이터 : " + key;
		cache.put(key, data);
		return "[L1 Miss]" + data;
	}
	
	public static void main(String[] args) {
		System.out.println(getData("user:1"));
		System.out.println(getData("user:1"));
	}
}

 

결과

[L1 Miss]데이터 : user:1
[L1 Hit] 데이터 : user:1

 

* 단일 서버나 스레드 기반 빠른 캐시 조회가 필요할 때 L1 캐시를 사용함

 

 

* L1 캐시는 요청이 많고 동일한 데이터를 반복 조회하는 경우 유용함

* 단, 서버가 여러대라면 각 인스턴스의 캐시가 서로 다른 데이터를 가질 수 있어서 일관성이 깨질 수 있음

 


L2 캐시(외부 캐시 서버)

1. 개념

L2 캐시는 서버간 공유 가능한 캐시 계층임

Redis나 Memcached같은 외부 캐시 서버가 이에 해당함

여러 애플리케이션 인스턴스가 같은 L2 캐시를 공유하기 때문에, 일관성을 유지하면서 분산 환경에서도 효율적 캐싱이 가능

 

2. 구조

Redis를 통한 캐싱

* 모든 서버가 동일한 캐시 서버(Reidis 등)에 접근해서 데이터를 공유함

* L1 캐시에서 캐시 미스가 발생하면, L2 캐시에서 데이터를 가져옴

 

3. 예제(redis.clients.jedis.Jedis import 필요)

import redis.clients.jedis.Jedis;

public class RedisCacheExample {
	public static void main(String[] args) {
    	// Redis 연결(기본 포트는 6379)
        try(Jedis jedis = new Jedis("localhost", 6379)) {
        	String key = "user:100";
            if(jedis.exists(key)) {
            	System.out.println("[L2 Hit] 캐시에서 데이터 조회" + jedis.get(key));
            } else {
            	System.out.println("[L2 Miss] 캐시 미스 발생 -> DB에서 조회 후 저장");
                // 실제 서비스에서는 DB 조회 결과를 가져와서 저장함
                String data = "김블러퍼";
                jedis.setex(key, 10, data); // TTL 10초 설정
                System.out.println("[L2 Update] Redis에 데이터 저장 완료");
            }
            
            // 데이터 확인
            System.out.println("현재 Redis 데이터 : " + jedis.get(key));
        }
    }
}

 

* L2 캐시는 여러 서버가 같은 데이터를 캐시하여 데이터 일관성을 유지함

 


 

L3 캐시(CDN 및 DB 캐시, 가장 느림)

1. 개념

L3 캐시는 글로벌 트래픽 분산 및 네트워크 지연 최소화를 위한 캐시 계층임

대표적으로 CDN(Content Delivery Network)나 DB Query Cache가 해당함

 

CDN 캐시 : 정적 자산(이미지, JS, CSS)을 사용자 근처 엣지 서버에 저장함

- > Cloudflare, Akamai

DB 캐시 : 반복되는 SQL 쿼리 결과를 캐싱함

- > MySQL Query cache, PostgreSQL Plan Cache

 

2. CDN 캐시 동작 흐름

CDN 캐시 동작 흐름

 

* CDN 캐시는 사용자의 지리적 위치에 따라 가장 가까운 엣지 서버에 데이터를 제공하여 지연시간(레이턴시)를 획기적으로 단축함

 

 

* CDN 캐시는 이미지, 동영상, 정적 HTML 등 변경이 적은 자산에 이상적임

* DB 캐시는 고정 쿼리(select * from category 등..)에 사용하면 서버 부하를 줄일 수 있음

 


캐시 계층 통합 구조

캐시 계층은 단일 수준으로 동작하지 않고, 여러 계층이 함께 작동하여 전체 성능을 극대화할 수 있음

캐시 계층 통합 구조, L1 + L2 + L3

 

L1 -> L2 -> L3 순으로 캐시를 구성하면, 데이터 접근 속도는 빨라지고 DB 부하가 감소한다고 함

 


정리

캐시 계층 주요 위치 예시 특징
L1 캐시 애플리케이션 내부 HashMap, Guava Cache 초고속, 단일 인스턴스 적용
L2 캐시 외부 캐시 서버 Redis, Memcached 다중 서버 공유, 확장성 우수
L3 캐시 CDN, DB 레벨 Cloudflare, MySQL Query Cache 전역 배포, 네트워크 최적화