- 프로젝트의 중요기능 중 검색 부분이 Community-Server 프로젝트의 핵심 기능이라고 생각했습니다.
- 검색 성능을 높이기 위해 Redis(NO-SQL)을 적용하였습니다.
(RDB와 NO-SQL 차이가 궁금하신 분은 RDB와 No-SQL의 차이 을 참고 부탁드립니다.) - Spring-boot에서 제공해 주는 Spring boot redis 라이브러리를 사용했습니다.
설치 과정은 Redis 설치 및 GUI 적용 글을 보고 설치 후 적용하시면 됩니다.
- build.gradle에 Redis 의존성 추가
- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
- application.properties에 적용
//build.gradle
spring.redis.host=localhost
spring.redis.port=6379
expire.defaultTime=60
redis를 연결하기 위한 Host IP, Port를 설정
expire.deaultTime 은 redis 쿠키를 유지하는 시간(TTL, Time To Live)으로 값이 1당 1초입니다.
만료시간은 후에 대용량 트래픽에 대한 성능테스트를 진행 후 적절한 시간을 설정할 예정입니다.
Spring Date Redis를 통해 Lettuce, Jedis라는 두 가지 오픈소스 Java 라이브러리를 사용합니다.
Jedis
- 장점
- Jedis는 사용하기 쉬운 API를 제공하며, 러닝커브가 낮습니다.
- 단점
- Jedis는 동기식 API만을 제공하기 때문에, 비동기 처리가 필요한 환경에서는 부적합할 수 있습니다.
- Jedis는 connection pool 관리에 대한 기능이 상대적으로 떨어질 수 있습니다.
Lettuce
- 장점
- Lettuce는 비동기 API를 지원하여 높은 성능과 확장성을 제공합니다.
- Lettuce는 connection pool을 효과적으로 관리하여 성능을 최적화합니다.
- Lettuce는 Reactive 프로그래밍을 지원하므로, Reactor나 RxJava와 같은 라이브러리와 통합이 용이합니다.
- 단점
- Jedis에 비해 러닝커브가 높습니다.
두 개의 비교를 찾으며 성능적으로 차이가 있다고 조사하여 Lettuce가 Jedis보다 성능적으로 TPS및 응답속도 등이 높으며, 비동기를 지원하여 lettuce을 채택하여 사용하였습니다.
또한, Spring Data Redis는 Redis에 두 가지 접근 방식을 제공합니다.
Redis Template
- Redis Template을 사용하면 특정 Entity 뿐만 아니라 여러 가지 원하는 타입을 넣을 수 있습니다.(List, Map, Hash 등)
Redis Repository
- Spring Data Redis의 Redis Repository를 이용하면 간단하게 Domain Entity를 Redis Hash로 만들 수 있습니다.
확장성을 위해 RedisTemplate을 사용하였습니다.
이는 레디스에서 제공해 주는 자료구조(List, Hash, Map, Object 등)를 구체적으로 명시하여 사용하기 때문에 채택하였습니다.
- RedisTemlate Key-Value 자료구조가 <String, Object> 인 이유
- 검색 API 의 성능을 위해 Redis의 캐시를 사용하고 있습니다.
- 이를 동일한 검색시 동일한 결과값을 주기 위해 Key는 검색조건들을 String 형태고 넣었으며, 결과가 항상 동일한 객체로 반환되지 않아 이를 고려하여 Object 형태로 처리하였습니다.
- cacheManger가 필요한 이유
- 스프링에서 제공해 주는 cacheMange는 interface 이기 때문에 실제 구현
ex) redisCacheMager를 구현해야만 사용할 수 있어서. - 캐시 기술을 지원하는 캐시 매니저 빈으로 등록해야 spring에서 Cache를 사용할 수 있음
- RedisCacheManager를 Bean으로 등록하면 기본 CacheManager를 RedisCacheManager로 사용함.
- 참고 : https://docs.spring.io/spring-framework/docs/6.0.0/javadoc-api/org/springframework/cache/CacheManager.html
- 스프링에서 제공해 주는 cacheMange는 interface 이기 때문에 실제 구현
@Cacheable
- 캐시 기능 수행
- 캐시가 존재하면 캐시의 정보를 가져오고 존재하지 않을 시 캐시 등록
- 스프링은 AOP 기반으로 캐시가 작동
- 어노테이션으로 AOP설정을 할 수 있어 간편하게 사용
@CacheEvict
- 캐시를 적절한 시점에 제거
- 키에 해당하는 캐시만 제거
- 캐시에 저장된 값을 모두 제거 시 allEntires = true 설정
동일한 검색시 Cacheable을 통해 캐시을 등록하였으며 검색 결과가 변하는 게시글의 CUD가 발생할 경우 CaheEvict을 통해 삭제하도록 구성하였습니다.
- Redis를 사용하기 위해서는 Key-Value 형식의 설계를 잘 구성해야합니다.
- 검색시 검색단어를 통해 검색 내용을 알 수 있듯이 Key에 검색 단어를 String 문자열으로 넣어 구성하였습니다.
- 구체적인 내용은 검색 API에 저장되는 유니크한 Key를 만들기 위해 검색 한 카테고리 번호, 게시글 제목, 게시글 내용, 게시글 생성날짜, 추천수 등의 값을 조합하여 만들었습니다.
//PostDTO.java
@Override
public String toString() {
return "PostDTO" + getCategoryNumber() + getPostName() + getContents() + getCreateTime() + getSuggestionCount();
}
- Mybatis의 검색 기능을 위해서 쿼리문을 동적 쿼리로 구성하기 위한 방법으로 <if> 절을 이용하여 쿼리문을 구성하였더니 쿼리의 and 조건으로 인해 여러 개의 Mapper가 만들어지는 문제가 발생했습니다.
- 이는 가독성이 떨어지며 하드코딩이라 느껴 다른 방안을 찾아봤습니다.
- MyBatis 공식문서를 참조하여 해결 방안을 찾았습니다.
- <where> ~ <if > ~ 문법들을 이용하여 하나의 mapper로 리팩토링 시켰습니다.
- 문법을 사용하게 되면 첫 Where 절의 "and"를 제외하기 때문에 문제없이 사용이 가능하였습니다.
전체 코드가 궁금하신 분은 community-server 프로젝트를 참고해 주세요.
Issue/10 ADMIN Controller 개발 by gamsayeon · Pull Request #13 · j-lab-edu/community-server
github.com
결과
- 데이터가 디스크에 저장하지 않고 메모리에 저장하여 읽기 성능이 향상되었습니다.
- 실제 동일한 검색시 Postman기준 응답시간이 3.58s -> 51ms 으로 향상됨을 확인하였습니다.
- Redis를 적용하기 전 No-Sql개념을 탐구함으로써 가변적인 구조로 데이터 저장, 데이터 설계 비용 감소 등의 개념을 알게 되었습니다.
느낀점
- No-SQL 중 Redis를 사용해보았습니다. 다른 NoSQL 데이터베이스가 무엇인지에 대한 궁금증이 생겼습니다. 향후 다른 No-SQL을 활용한 프로젝트를 진행할 계획입니다.
- 또한, RDB와 No-SQL의 정확한 차이점에 대해 자세히 알아보고 정리할 예정입니다.
- Redis를 사용할 때 Lettuce라는 오픈 라이브러리를 선택하여 활용했습니다. Jedis를 사용하는 경우와의 성능의 차이를 직접 확인하고자 합니다.
- 향후 서버를 증설시 일관성을 유지하는 과정으로써 master-slave 환경에 대해 자세히 조사하여 정리할 예정입니다.
참고
- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis
- https://jojoldu.tistory.com/418
- https://docs.spring.io/spring-framework/docs/6.0.0/javadoc-api/org/springframework/cache/CacheManager.html
- https://bcp0109.tistory.com/328
- https://stackoverflow.com/questions/60246490/whats-means-spi-on-spring-cache-cachemanager
'Toy Project > Community-Server' 카테고리의 다른 글
ISSUE 6: Junit5 테스트 코드 작성 (0) | 2022.12.29 |
---|---|
ISSUE 5: Log4j2 연동(로깅 정의 및 로깅 전략) (0) | 2022.12.29 |
ISSUE 3: 로그인 AOP 생성 (0) | 2022.12.29 |
ISSUE 2: JDBC, MyBatis를 이용한 Mysql 연동 (0) | 2022.12.29 |
ISSUE 1: Spring Boot 프로젝트 생성 (0) | 2022.12.29 |