[Spring] 좋아요 기능 구현 시 테이블 설계 고민과 성능 테스트 결과
좋아요 기능 구현 : RDB vs Redis
- redis로 구현 시 실시간성이나 동시성에 대해 처리가 쉽고 구조가 간단하다는 장점이 있지만 데이터 유실과 용량에 대한 부분이 우려스럽고 또 rdb는 데이터가 안정적이나 속도가 느린 것이 걱정되어 멘토님들께 의견을 구하였습니다.
현실적인 답변드리자면, 현재 유저의 규모가 크지 않기 때문에 RDB만 사용해서 구현하는게 좋습니다.
만약 데이터가 많다면, redis를 쓰더라도 데이터 정책 설정하기 나름이긴한데, 저라면 좋아요 데이터자체는 RDB에 저장하고, 게시글마다 좋아요 수 같은 통계데이터는 redis에 캐싱할듯하네요.
말씀하신 것처럼 각각의 장단점을 명확합니다! 잘 파악해주셨네요!
사실 트래픽이 크지도 않고, 데이터가 많지도 않을 것이기 때문에 rdb도 절대 느리지 않습니다! 하지만 그전에 동시성문제를 처리할 수 있도록 jpa 레벨에서 처리가 조금 필요하겠네요 ㅎㅎ 저희 회사는 현재 rdb로 구현을 하고 있고, 인프라 관리 비용까지 생각하면 여러분도 rdb로 구현하시면 좋지 않을까 생각이듭니다! 나중에 취업을 생각해서 redis 해보시는 것도 좋지만, 굳이 둘중하나 선택해야한다면 rdb에서 처리하는 것 추천!
멘토님들께 의견에 따라 기존 redis로 구현하고자 했던 좋아요 기능을 RDB로 구현하는 것으로 수정하였습니다.
좋아요 테이블 설계
좋아요 테이블 설계 시 2가지 방안을 놓고 고민이 있었습니다. 저희 서비스에서 유저는 포트폴리오,아이템,플래너 3가지 테이블을 좋아요 할 수 있습니다.
첫번재 방안
첫번째 방안은 각각의 테이블에 맞는 좋아요 테이블을 두는 것입니다. 이렇게 된다면 데이터의 무결성은 보장되지만 불필요하게 테이블이 많이 생성되고 조회할 시 여러 테이블을 거쳐야한다는 단점이 있습니다.
두번째 방안
두번째 방안은 좋아요 테이블을 하나로 통합하고 likeType 칼럼으로 어떤 Type에 대한 좋아요인지를 구분하는 것입니다. 이 방법은 테이블이 하나로 통합되어 개발 시에 편리하지만 데이터 무결성 관리가 어렵고 조회 시 쿼리가 복잡해진다는 단점이 있었습니다.
두번째 방안이 더 낫다는 의견으로 모아졌지만 저는 두번째 방안이 조회 시 칼럼 3개를 참조해야 하는데 이것이 성능 저하로 이어지지 않을까하는 우려가 되었습니다.
따라서 간단한 테스트로 이를 검증해보았습니다.
Integer count = 100;
@BeforeEach
void setup() {
dataSetting();
}
@Test
public void testLikeRepositoryFindByUsersAndLikeTypeIn() {
Users users = usersRepository.findByNickname("test0").get();
Collection<LikeEnum> likeTypes = Arrays.asList(LikeEnum.PORTFOLIO, LikeEnum.ITEM);
long startTime = System.currentTimeMillis();
likeRepository.findByUsersAndLikeTypeIn(users, likeTypes);
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
System.out.println("Execution time: " + executionTime + " milliseconds");
}
@Test
public void testItemLikeRepositoryTwice() {
Users users = usersRepository.findByNickname("test0").get();
long startTime = System.currentTimeMillis();
itemLikeRepository.findByUsers(users);
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
long startTime2 = System.currentTimeMillis();
portfolioLikeRepository.findByUsers(users);
long endTime2 = System.currentTimeMillis();
long executionTime2 = endTime2 - startTime2;
System.out.println("Execution time: " + executionTime + " milliseconds");
System.out.println("Execution time: " + executionTime2 + " milliseconds");
}
@Test
public void testLikeRepositoryFindByUsersAndLikeTypeAndLikedId() {
Users users = usersRepository.findByNickname("test0").get();
long startTime = System.currentTimeMillis();
likeRepository.findByUsersAndLikeTypeAndLikedId(users,LikeEnum.ITEM, Long.valueOf(1));
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
System.out.println("첫번째 Execution time: " + executionTime + " milliseconds");
}
Test 결과
- 저희 서비스 로직으로 인해 User가 좋아요 한 포트폴리오와 아이템을 한꺼번에 가져와야하는 상황에서 첫번째와 두번째 방안을 비교해보았습니다. 결과적으로 하나의 테이블에서 조회하는 것이 훨씬 성능이 빠르다는 것을 확인하였습니다. 앞으로 JPA 성능 최적화를 진행할 때 테이블로의 조회 횟수 자체를 줄이는 것이 훨씬 유의미할 것 같습니다.
- 두번째로 최초로 우려했던 칼럼 3개를 조회하는 것의 성능은 오히려 칼럼 1개로 조회할 때보다 더 빠르다는 것을 확인할 수 있었습니다. 정확한 이유를 파악할 수는 없었지만 지피티의 말에 따르면 일반적으로는 조회 범위가 작을 수록 성능이 빠르지만 캐싱 혹은 복합 인덱스의 활용 등으로 결과가 달라질 수 있다고 합니다.
만일 test결과가 캐싱에 의한 것이라고 해도 조회 범위가 3개인 것에 따른 유의미한 성능 차이는 없을 것이라고 생각되어 두번째 방안을 사용하여 개발하였습니다.
'Spring Boot' 카테고리의 다른 글
[Spring] serviceImpl 패턴을 활용한 likeService 리팩토링(객체지향원칙) (0) | 2023.11.10 |
---|---|
[Spring] @Transactional(readOnly = true)를 왜 붙여야 하나요? (0) | 2023.11.10 |
[Spring] Redis Redisson과 AOP를 이용하여 분산락 구현하기 (0) | 2023.10.13 |
[Spring] AOP, Custom Annotation으로 코드 성능 측정하기 (0) | 2023.10.11 |
[Spring] random List를 Page로 바꾸는 방법(JPA Pageable) (0) | 2023.08.03 |