캐시 압력(Cache Pressure)
캐시 압력이란?
캐시 압력은 캐시 메모리가 부족해지면서 발생하는 문제를 말한다.
캐시가 가득 차서 더 이상 새로운 데이터를 저장할 수 없고, 이로 인해 기존 데이터를 삭제해야 하는 상황이 발생할 수 있다.
캐시 압력이 증가하면, 캐시 항목을 삭제하거나 새 데이터를 캐시에 저장하는 동안 대기 상태가 발생할 수 있다.
캐시 압력 예시
Spring에서 사용자별 페이징 데이터를 캐시에 저장하는 경우, 특히 대규모 사용자와 많은 데이터를 처리하는 시스템에서는 캐시 메모리가 빠르게 소진될 수 있으며, 이는 “캐시 쓰로틀링(Cache Throttling)” 또는 “캐시 압력(Cache Pressure)” 상황을 초래할 수 있다.
이러한 상황은 캐시가 가득 차서 새로운 데이터를 저장할 수 없게 되어 요청이 지연되거나, 대기 상태로 빠지는 문제를 일으킬 수 있다.
데이터량 증가
- 각 사용자가 고유한 페이징 데이터를 요청할 경우, 캐시에 저장되는 데이터의 양이 빠르게 증가할 수 있다.
- 다양한 페이징 요청(예: 페이지 번호, 페이지 크기, 필터링 조건 등)에 따라 각기 다른 캐시 항목이 생성되어, 캐시 공간을 많이 차지하게 된다.
캐시 용량 초과
- 캐시는 일반적으로 제한된 메모리 공간을 사용한다. 사용자별로 많은 페이징 데이터를 캐시에 저장하면 캐시 용량이 초과될 수 있다.
- 용량 초과로 인해 캐시가 새로운 항목을 수용할 수 없게 되면, 기존 항목을 제거하거나 요청이 대기 상태로 전환될 수 있다.
캐시 히트율 저하
- 페이징 데이터를 캐시하는 경우, 사용자가 특정 페이지를 반복적으로 요청하지 않는 한 캐시 히트율이 낮아질 수 있다.
- 낮은 캐시 히트율은 캐시의 효율성을 떨어뜨리며, 메모리 낭비로 이어질 수 있다.
사용자별 캐시 공간 경쟁
- 다수의 사용자가 동일한 캐시 리소스를 공유할 때, 사용자별로 많은 데이터를 캐시에 저장하면 캐시 공간이 경쟁 상태가 되어, 중요한 데이터가 자주 제거될 수 있다.
- 이로 인해 필요한 데이터가 캐시에 존재하지 않아, 다시 데이터베이스에서 가져오는 상황이 빈번해질 수 있다.
캐시 압력 해결방법
캐시 제한 설정
- TTL(Time-To-Live): 캐시 항목에 TTL을 설정하여 일정 시간 후에 자동으로 만료되도록 설정할 수 있다. 이를 통해 오래된 캐시 데이터를 자동으로 제거하고, 새로운 항목을 저장할 공간을 확보할 수 있다.
- 최대 캐시 크기 설정: 사용자별 페이징 데이터에 대해 캐시의 최대 크기를 설정하여, 용량이 초과되면 오래된 데이터를 자동으로 제거하도록 할 수 있다.
페이징 전략 최적화
- 부분적 캐싱: 모든 페이지를 캐싱하기보다는 자주 요청되는 특정 페이지만을 캐싱하여, 캐시 공간을 효율적으로 사용할 수 있다.
- 데이터베이스 인덱스 최적화: 캐시를 사용하는 대신, 데이터베이스 인덱스를 최적화하여 페이징 성능을 향상시킬 수 있다. 이렇게 하면 캐시 의존도를 줄이고, 데이터베이스에서 더 빠르게 결과를 가져올 수 있다.
분산 캐시 사용
- Redis와 같은 분산 캐시 시스템을 사용하여, 여러 서버에 캐시 데이터를 분산시킬 수 있다. 이를 통해 한 서버의 메모리 용량 한계를 극복하고, 캐시 성능을 높일 수 있다.
캐시 우회 전략
- 특정 조건에서 캐시를 우회하고 직접 데이터베이스에서 데이터를 가져오는 전략을 사용할 수 있다. 예를 들어, 페이징 요청이 매우 세분화된 경우 캐시를 우회하도록 설정할 수 있다.
캐시 압력 문제 예시 - 여러 사용자의 페이징 데이터
- Redis 캐시에서는 다음과 같은 구조를 사용할 수 있다.
{
"user123_page_1": {
"userId": "user123",
"page": 1,
"pageSize": 10,
"totalPages": 5,
"data": [
{
"id": "item1",
"name": "Item One",
"description": "Description for item one."
},
{
"id": "item2",
"name": "Item Two",
"description": "Description for item two."
},
...
]
},
"user123_page_2": {
"userId": "user123",
"page": 2,
"pageSize": 10,
"totalPages": 5,
"data": [
{
"id": "item11",
"name": "Item Eleven",
"description": "Description for item eleven."
},
{
"id": "item12",
"name": "Item Twelve",
"description": "Description for item twelve."
},
...
]
},
"user456_page_1": {
"userId": "user456",
"page": 1,
"pageSize": 10,
"totalPages": 3,
"data": [
{
"id": "item21",
"name": "Item Twenty-One",
"description": "Description for item twenty-one."
},
{
"id": "item22",
"name": "Item Twenty-Two",
"description": "Description for item twenty-two."
},
...
]
}
}
캐시 압력 문제 해결 예시
페이징 데이터 캐싱
사용자가 요청한 페이지에 해당하는 항목들의 ID 리스트를 캐시에 저장한다. 이 데이터는 상대적으로 작고, 페이징이 변경되지 않는 한 자주 재사용될 수 있다.
{
"user123_page_1": ["item1", "item2", "item3"],
"user123_page_2": ["item4", "item5", "item6"]
}
아이템 데이터 캐싱
각 아이템에 대한 상세 데이터를 별도로 캐싱한다. 이렇게 하면 페이지를 요청할 때 페이지에 포함된 각 아이템의 상세 데이터를 개별적으로 가져올 수 있다.
{
"item1": {
"id": "item1",
"name": "Item One",
"description": "Description for item one."
},
"item2": {
"id": "item2",
"name": "Item Two",
"description": "Description for item two."
},
"item3": {
"id": "item3",
"name": "Item Three",
"description": "Description for item three."
}
}
데이터 조회 및 조합
사용자가 특정 페이지를 요청하면 다음 단계로 데이터를 처리한다.
- 페이지 ID 리스트 가져오기
- 먼저, 요청된 페이지에 해당하는 페이징 데이터를 가져온다.
- 예를 들어, user123_page_1 요청 하여 페이징 데이터를 가져다.
- 아이템 상세 데이터 가져오기
- 위에서 가져온 각 아이템 ID에 대해 캐시된 상세 데이터를 조회한다.
- 예를 들어, item1, item2, item3 각각의 ID로 캐시에서 상세 정보를 가져온다.
- 데이터 조합
- 가져온 아이템 상세 데이터를 조합하여 최종 결과를 생성한다. 이 데이터를 사용자에게 응답으로 반환한다.
캐시 백오프 전략
일반적으로 캐시에 접근할 때 캐시 미스(cache miss)가 발생하면, 시스템은 데이터를 캐시에 다시 적재하거나 원본 데이터베이스에서 데이터를 가져와야 한다. 이 과정에서 여러 클라이언트가 동시에 캐시에 접근하거나 요청을 보내면, 캐시 서버나 원본 데이터베이스에 과부하가 걸릴 수 있다.
캐시 백오프는 이러한 과부하를 방지하기 위해, 캐시 미스가 발생했을 때 재시도 요청의 빈도를 줄이거나 일정한 대기 시간을 두는 전략이다. 이렇게 하면 캐시 미스 발생 시 즉각적으로 원본 데이터베이스나 캐시 서버에 다량의 요청이 몰리지 않도록 하여, 시스템이 정상적으로 복구할 시간을 벌 수 있다.
캐시 백오프는 다음과 같은 전략이 있다.
- 고정 대기 시간(Fixed Backoff):
- 캐시 미스가 발생한 후 일정한 시간(예: 100ms) 동안 재시도를 지연시킨다.
- 지수 백오프(Exponential Backoff):
- 재시도 시간이 일정하지 않고, 시도할 때마다 대기 시간이 기하급수적으로 증가하는 방식이다. 예를 들어, 첫 번째 재시도는 100ms, 두 번째는 200ms, 세 번째는 400ms처럼 대기 시간을 점점 늘려간다.
- 랜덤 백오프(Randomized Backoff):
- 각 클라이언트가 캐시에 재시도할 때 랜덤하게 대기 시간을 설정하여 요청이 특정 시간에 몰리는 것을 방지한다.
참조
- 팀스파르타코딩클럽 강의자료
'ComputerScience > Database' 카테고리의 다른 글
[Database] PostgreSQL의 데드 튜플 문제 (0) | 2024.11.20 |
---|---|
[Database] 분산 트랜잭션과 2PC, SAGA 패턴 (0) | 2024.09.07 |
[Database] CQRS와 분산 읽기/쓰기 DB 구성, 데이터 일관성 모델 (최종 일관성, 강력한 일관성) (0) | 2024.08.13 |
[Database] DBCP (DB connection pool)과 hikariCP, MySQL을 기준으로 (0) | 2024.07.13 |
[Database] DB MVCC와 PostgreSQL, MySQL의 동작 비교 (0) | 2024.04.27 |