기업협업을 진행하면서 프로젝트에 다양한 검색이 생기면서 여러요청이오면 디비에 과부하가 올거같아 부트캠프 진행중에 써봤던
In-Memory DB
중 하나인Redis
가 생각나 적용시켜보기로 했다
데이터나 값을 저장하는 임시저장소
- 원본 데이터 접근보다 빠름
- 같은 데이터를 반복적으로 접근하는 상황에 사용하기 알맞음
- 잘 변하지 않는 데이터일수록 효과적
- 클라이언트가 웹서버에 접속한다
- 웹 서버가 DB에 데이터를 읽거나 쓰기를 요청
- DB에서 웹서버로 전송
- 웹서버가 클라이언트로 전송
이 과정중 DB의 내부 캐시용량이 Memory
보다 크다면 Disk
를 써야하는데 속도가 굉장히 느려질 수도 있다
Cache-Aside는 Redis를 캐시로 사용할때 보편적으로 사용하는 방법인데,
그 과정은
1. App이 데이터를 찾을때 먼저 캐시를 찾는다
2. 캐시에 데이터가 있다면 데이터를 읽는다
3. 만약 2에서 없다면 DB에 접근해서 데이터를 가져온뒤 캐시에 저장하는 과정을 거친다
검색기능이 있는 API에서 사용하는 공통 Redis
import { CACHE_MANAGER, Inject, Injectable } from '@nestjs/common';
import { Cache } from 'cache-manager';
@Injectable()
export class RedisService {
constructor(
// Redis
@Inject(CACHE_MANAGER)
private readonly cacheManager: Cache,
) {}
async findRedis(condition: string) {
const redisRes = await this.cacheManager.get(condition);
return redisRes;
}
async setRedis(condition, result) {
await this.cacheManager.set(condition, result, {
ttl: 2,
});
}
}
사용하는곳의 코드
@Injectable()
export class CourseService {
constructor(
@InjectRepository(Course)
private readonly courseRepository: Repository<Course>, //
private readonly categoryService: CategoryService, //
private readonly discountCourseService: DiscountCourseService,
@InjectRepository(UserCourse)
private readonly userCourseRepository: Repository<UserCourse>, //
private readonly connection: Connection,
private readonly redisService: RedisService,
) {}
위처럼 주입받은 후
const searchCondition = `${tagName}${status}${searchInputType}${inputString}${take}${page}`;
const redisRes = await this.redisService.findRedis(searchCondition);
if (redisRes) {
return redisRes;
}
검색한 결과가 있다면 Return 시키고 없다면
await this.redisService.setRedis(searchCondition, searchResult);
return searchResult;
Redis
에 Set을 시킨후 DB에 긁어온 데이터를 Return 시킨다.
TTL을 초기에는 10초정도로 길게 설정했는데 프론트엔드분들이 아우성을 치고 실제로 나도 불편함이 많아져 2초정도로 줄였다.
이런 구조를 사용한다면
Redis
가 다운되더라도 바로 장애가 나오는게 아니라 DB에서 데이터를 긁어올수있다.
하지만 단점으로는 그 2초동안의 정보가 갱신이 되지않아 항상 최신 정보가 나오지 않는다는거다 이점은 Cache를 쓰면 생기게되는 Trade Off라고 생각함..