skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

비정규화를 통한 문제 해결 #63

Open skarltjr opened 2 years ago

skarltjr commented 2 years ago

상황:

게시글 조회(검색)시 게시글과 관련된 정보를 함께 전달해야했다.
문제는 no-offset기반 페이징에서 한 번에 40개씩 게시글을 가져오는데 그때마다 각각의 게시글은 아래 정보들을 함께 필요로했다.
최대한 단방향을 지향했으며 아래 모든 엔티티 n -> 1 게시글의 관계.
단순하게 40개의 글 * 5개의 쿼리가 발생할 수 있다고 생각했다.
무조건 이 문제를 해결해야했고 해결하고 싶었다.

1. 댓글 개수
2. 리액션 종류 및 종류별 클릭된 개수
3. text본문 존재 시 첫 text
4. image존재 시 첫 image
5. 게시글에 사용된 image총 개수

방법:

반정규화(비정규화)를 수행. 
새로운 article_preview라는 테이블 도입

위 5가지 정보를 미리 수집하여 게시글 리스트 조회 시 게시글 당 article_preview 하나만 가져옴으로써 필요한 모든 정보를 
얻을 수 있다고 생각.

기존이라면 하나의 article에 대한 댓글 개수, article에 사용된 이미지 총 개수 .. 등 n개의 정보를 가져오기위해 
게시글 하나당 n번의 쿼리 발생

비정규화/ article_preview테이블을 생성함으로써 article_preview하나만 가져와도 위 모든 정보를 얻을 수 있도록 변경
40*n -> 40*1까지 해결할 수 있었다.

비정규화 사용 이유:

우리의 서비스는 커뮤니티 서비스였다.
여기서 가장 빈번하게 사용될 기능은 무엇보다 검색 (게시글 리스트 조회)라고 생각

그럼 게시글을 작성, 댓글 작성 등에서 아주 아주 약간 더 시간을 사용하여 추가적으로 articlePreview 테이블을 채운다고 해도
사실상 사용자에게 체감될 수 있는 시간 지연이 아니며 
반대로 이를 통해서 게시글 리스트 조회 시 현재 상황보다 체감할 수 있는 성능개선이 일어날것이라고 생각.

주의: 비정규화를 통해 발생할 수 있는 문제점. 비정규화의 장-단점은 알고 사용하고자 한다.

장점:
빠른 데이터 조회 및 조회 쿼리 단순화
- 현재 빠르고 단순하게 데이터 조회가 필요했다

단점:
1. 데이터 갱신 및 삽입 비용이 높다
- 분명하게 느꼈다. 
- article에 대한 댓글 개수를 저장해놓기위해 댓글,대댓글 생성 시 aritlce_preview의 total comment count값을 변경시키는 등을 추가적으로 수행해야했다.

2. 더 많은 저장공간이 필요해짐
3. 데이터의 일관성이 깨질 수 있다.
- ex)article_preview의 댓글 개수 필드의 값이 과연 commentRepository.findByArticle()로 가져온 값과 항상 동일함을 보장할 수 있는가?
- 어려운 부분이 많은 것 같다.
skarltjr commented 2 years ago
스크린샷 2022-02-02 오후 2 58 47
skarltjr commented 2 years ago

더 고민해보기

- 게시글 리스트 조회 시 한 번에 40개씩가져온다
- 현재 검색 조건에 맞는 게시글을 40개씩 가져오고 각 게시글당 1번씩 추가쿼리를 통해 미리보기 정보를 불러온다
- ★즉. 아직 40*1 방의 쿼리가 발생한다는것이고 분명히 더 줄여볼 수 있다고 생각.

물론 여기서 무조건 쿼리를 줄이는게 맞을까?라는 고민도 해봤다.
그런데 내가 지금 만드는 "커뮤니티 서비스"며 가장 빈번하게 사용될 기능은 "검색(게시글 리스트 조회)"이라고 생각하기에
만약 매 번 검색 or 아래로 스크롤하여 게시글 더 보기를 할 때마다 40방의 쿼리가 발생하는건 막아야한다고 생각했다.

또한 이 경우는 단순히 조회(select)라서 적절한 방식이라고 생각.
---
만약 조회가 아닌 update등의 문제라면 대량의 데이터를 처리하는 "그 시간동안" 트랜잭션이 유지되어야하기에 문제가 발생할 수 있다는 
말을 본 적이 있다.

해결

1. 검색 매서드를 통해 검색 조건에 해당하는 게시글 List를 가져온다
2. join fetch를 통한 게시글 미리보기 load 쿼리 최적화
3.  1번에서 가져온 게시글의 아이디와 in절을 활용하여 한 번에 40개에 대한 게시글 미리보기 40개를 가져온다.

추가적인 고민상황

정말 열심히 고민해서 쿼리 횟수를 줄이고 줄였지만 이 방법 자체는 한 방의 쿼리가 너무 클 수 있다는 생각이 들었다. 
지금 상황에선 적절한 방법일 수 있지만 비슷하게 다른 상황( 여러번의 쿼리 & 각 쿼리가 매우 큰 상황 )에서는 올바르지 못한 것 같다. 
왜냐하면 한 방 한 방이 거대한 쿼리를 한 번에 가져오겠다고 하는것은
- 결국 방대한 한 방 쿼리를 수행할 수 있는 성능이 뒷받침돼야하기 때문이라고 생각.
- 또한 그 방대한 양의 데이터를 갖고오기위한 쿼리가 과연 남이 이해할 수 있는 쿼리일까?라는 생각

그럼 그 땐 어떻게해야할까? 적절하게 쪼개며 나와 모두가 이해할 수 있는 과정의 쿼리도 하나의 방법일 수 있다고 생각.