Skip to content

Commit

Permalink
docs: DB Cache
Browse files Browse the repository at this point in the history
  • Loading branch information
birdieHyun committed Sep 25, 2023
1 parent ded7bf3 commit f14f769
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 3 deletions.
2 changes: 1 addition & 1 deletion _posts/2023-09-20-refactor-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: JPA Repository는 Entity만을 조회해야할까?
layout: post
author: 임동현
subtitle: 'jpa repository'
date: 2023-09-14 00:00:00 +0900
date: 2023-09-20 00:00:00 +0900
categories:
- JPA
tags:
Expand Down
2 changes: 1 addition & 1 deletion _posts/2023-09-21-nGrinder-auto.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: nGrinder 자동화
layout: post
author: 임동현
subtitle: 'nGrinder 자동화'
date: 2023-09-14 00:00:00 +0900
date: 2023-09-21 00:00:00 +0900
categories:
- nGrinder
tags:
Expand Down
2 changes: 1 addition & 1 deletion _posts/2023-09-22-Logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Loki를 통한 로그 모니터링
layout: post
author: 임동현
subtitle: 'Grafana Loki'
date: 2023-09-14 00:00:00 +0900
date: 2023-09-22 00:00:00 +0900
categories:
- Log
tags:
Expand Down
129 changes: 129 additions & 0 deletions _posts/2023-09-25-db-cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
title: DB Cache
layout: post
author: 임동현
subtitle: 'Redis Cache'
date: 2023-09-25 00:00:00 +0900
categories:
- Redis
tags:
- Redis
---
## 1. 문제 정의

업브렐라 서비스의 특성 상 협업지점의 CUD는 자주 일어나지 않지만, 조회 API는 자주 호출되고 있습니다.

협업지점 조회 API는 여러 테이블이 조인을 하고, 자주 호출되는데 이것이 매번 호출되면 DB의 부하 증가로 이어지게 됩니다. 따라서 업브렐라 개발팀은 Redis를 이용하여 DB 데이터를 캐싱하기로 결정하였습니다.

## 2. Redis

### 2 - 1. Redis 설치

- 아래의 명령어를 통해 redis를 설치해줍니다.

```
sudo apt install redis-server
```

- redis 설정 변경

```
sudo vim /etc/redis/redis.conf
```

여기서 bind 옵션을 허용하고 싶은 ip로 변경해줍니다.

### 2 - 2. Spring Boot Redis 설정

- build.gradle 의존성 추가해줍니다.

```
// Spring Data Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// Spring Session Data Redis
implementation 'org.springframework.session:spring-session-data-redis'
```

## 3. Cache를 왜 써야할까?

### 3 - 1. 파레토 법칙

<img width="675" alt="파레토 법칙" src="https://user-images.githubusercontent.com/115435784/270359884-54ca6bdb-1128-421c-be17-23b9417940bf.png">

이미지 출처 : https://www.briantracy.com/blog/personal-success/how-to-use-the-80-20-rule-pareto-principle/

파레토 법칙은 업브렐라 서비스에 그대로 적용할 수 있습니다. 자주 사용되는 20% 데이터에 캐싱을 적용하면 80% 결과에 대해서 성능 향상을 기대할 수 있을 것입니다.

### 3 - 2. 주의점

캐싱을 적용하고 데이터 변경에 대한 설정을 하지 않는다면, 사용자는 데이터가 변경되었음에도 캐싱된 데이터를 반환받게 됩니다. 즉, 변경된 DB 데이터를 반영하지 못하게 됩니다.

이러한 문제를 해결하기 위한 여러 전략은 다음과 같습니다.

1. TTL (Time To Live) 설정 : 캐시에 저장된 데이터에는 일정 시간 동안만 유지되게 만들 수 있는 TTL 값을 설정합니다. TTL이 만료되면 해당 캐시 데이터는 자동으로 삭제되며, 다음 요청 시 DB에서 다시 데이터를 가져와 캐시합니다. 하지만, 이 방법은 최신 데이터를 항상 반영한다는 보장은 없습니다.
2. **DB 변경 감지**: DB의 데이터 변경을 감지할 수 있는 트리거나 이벤트를 사용하여 데이터가 변경될 때 캐시를 업데이트하거나 삭제하는 방법이 있습니다. 예를 들어, DB 트리거를 사용하여 데이터 변경 시 외부 서비스를 호출하여 캐시를 업데이트할 수 있습니다.
3. **캐시 무효화 (Cache Invalidation)**: 데이터가 변경될 때마다 관련된 캐시를 수동으로 무효화하는 방법입니다. 예를 들어, 사용자 정보를 수정하는 서비스가 있다면, 해당 서비스 로직 내에서 관련된 캐시 데이터를 삭제하거나 업데이트 합니다.

여러가지 방법 중, 업브렐라 개발팀은 캐시 무효화 방법을 통해 캐시를 관리하도록 결정하였습니다.

## 4. 캐시 적용

### 4 - 1. 조회 Service에 캐시 적용

```java
@Transactional
@Cacheable(value = "stores", key = "'allStores'")
public List<SingleStoreResponse> findAllStores() {

List<StoreDetail> storeDetails = storeDetailRepository.findAllStores();
return storeDetails.stream()
.map(this::createSingleStoreResponse)
.collect(Collectors.toList());
}
```

@Cacheable 어노테이션을 사용해서 필요한 데이터에 캐시를 적용할 수 있습니다.
<img width="895" alt="cache 삭제 전" src="https://user-images.githubusercontent.com/115435784/270360051-06fe6104-72c3-4f61-b91e-89542919e66f.png">

key : stores

value : allStores

session에 적용된 모습을 확인할 수 있습니다.

### 4 - 2. CUD 캐시 무효화 설정

```java
@Transactional
@CacheEvict(value = "stores", key = "'allStores'")
public void deleteStoreMeta(long storeMetaId) {

findStoreMetaById(storeMetaId).delete();
}
```

@CacheEvict 어노테이션을 통해 적용된 캐시를 무효화 합니다.

<img width="897" alt="cache 삭제" src="https://user-images.githubusercontent.com/115435784/270360248-9f24468c-f132-4d00-9b00-86cb894c70e6.png">

@CacheEvict 를 통해 stores:allStores 가 삭제된 것을 확인할 수 있습니다.

## 5. 성능 비교

**Cache 적용 전**
<img width="1244" alt="before cache" src="https://user-images.githubusercontent.com/115435784/270360332-e290c895-7e28-4361-8a36-4be2b2b811a8.png">

vUser 50을 기준으로 평균 TPS는 117.1이었습니다.

**Cache 적용 후**
<img width="1248" alt="after cache" src="https://user-images.githubusercontent.com/115435784/270360450-3da9a727-8065-4e9b-bdc0-67c2945192f6.png">

동일한 조건에서 캐시를 적용해보니, 평균 TPS 2386.1로 상승한 것을 확인할 수 있습니다.

TPS를 비교해보면, 117 TPS -> 2386 TPS로 1943.59%의 성능 향상이 이루어졌습니다.

## 6. 마무리

이번 포스팅을 통해 DB에 캐시를 적용하는 방법을 알아보았습니다.

DB 데이터를 적절하게 캐싱한다면 API 성능을 더욱 끌어올릴 수 있을 것입니다.

0 comments on commit f14f769

Please sign in to comment.