🙈 Prologue
MongoDB 최적화에 대해 CTO님께서 해주신 강연(?)을 듣고 정리해보았습니다.
1. 잘못 설계된 사례
- 온코파 2018 시즌2) lecture 콜렉션의 student필드에 배열 무한대로 늘리는 식으로 설계 → 10만명이 넘어가자 터짐
해결책
- 배열의 크기가 제한된 범위가 아니라면 무조건 document에서 분리하는 식으로 해결
- 새로운 콜렉션 user_lecture / user_lecture 이런 식으로 분리해서 user_id를 uniqueKey로 해서 lecture index를 저장
why?
- 서비스에 맞지 않는 데이터 구조
- 터질 것을 예상하지 못함 (어디까지 버틸지)
2. 몽고디비 최적화
2-1. 용량 최적화
- 필요한 데이터만 저장 : noSQL의 장점 ) field의 default 설정도 신중하게 → 도큐먼트에 들어가는 값 하나하나가 용량이다
- 사용하지 않는(않을) 데이터들은 제거
- TTL index : 특정 field값을 기준으로 일정 기간이 지나면 자동으로 삭제 ) 알림 기능에 적합
- Atlas Online Archive (몽고 디비 자체는 아니고 Atlas라는 플랫폼에서 제공해줌) : 자주 접근하지 않는 데이터를 Object Storage로 이전 (Data Lake) → 읽기 쿼리만 가능해서 로그 타입의 데이터에 적합
- compact command
- 일종의 디스크 조각 모음 : 데이터를 지우면 논리적 빈 공간이 생성되지만 즉시 물리 공간이 반환되지는 않음
- 몽고디비에서
db.runCommand({compact: "collection"})
하면 빈 공간이 물리적 공간으로 반환됨. 그렇지만 너무 자주 남발하는 것은 좋지 않음.
2-2. Schema 최적화(=설계)
- user 콜렉션에서 contact, access 정보를 따로 콜렉션으로 빼내어 같은 objectId 공유. → 최적화 가능, 그렇지만 쿼리 요청을 두 번 보내야 하므로 무조건 좋은 것은 아님 (→index를 고려해 결정해야 한다)
- schema 최적화의 방점: document의 크기가 2mb를 넘지 않게. size limit 16mb, depth limit 100이지만 꽉 채우진 말자..
- 어떤 필드를 index로 사용해 쿼리를 날릴지 고려 (굳이 인덱스가 아니더라도 이 데이터가 불리는 시나리오상 어떤 필드를 가지고 주로 데이터를 조회할 것인지)
2-3.Query 최적화
select
: 꼭 필요한 필드만 필터링 해서 들고 오기(트래픽, 메모리 → 디비가 메모리를 올려서 얘를 서빙하므로 내 쿼리가 메모리를 적게 쓰면 그만큼 다른 쿼리를 처리할 수 있는 능력이 늘어난다)
limit
: 데이터를 가져오는 개수를 제한 - 한번에 다 필요하지도 않은데 막 만개씩 데이터를 들고온다든지. 사용자가 보통 만개의 데이터를 한 번에 보는 경우는 없으므로. 데이터가 많은 경우 limit을 걸어서 잘라서 가져오는 것이 좋다
index
를 활용: index를 활용해 콜렉션 내의 모든 도큐먼트들을 검토하지 않고 데이터를 디비가 찾을 수 있게 도와줘야 함.
3. index
3-1. index란?
- 콜렉션 데이터의 일부를 저장해 탐색을 쉽게 해주는 특별한 자료구조 (-공식문서)
- 인덱스가 아닌 다른 것을 기준으로 정렬하거나 하려고 하면 전체를 탐색해야해서 인덱스를 최대한 활용하는 것이 좋다 !
- B-Tree : 데이터를 항상 정렬된 상태로 유지
- 우리 서비스에서 주로 사용하는 index
- single index : 하나의 인덱스를 기준으로 데이터를 정렬
- compound index : 필드 두 개를 기준으로 데이터를 정렬 . 필드 순서와 정렬 순서가 매우 중요
{userid : 1 , score: -1}
→ userid는 오름차순, score은 내림차순 / userid 가 먼저 정렬, score은 후순위로. 그래서 순서가 중요함
- userid로는 찾거나 정렬 할 수 있지만 score로는 힘듦
- prefix : 다른 필드를 주지 않더라도 그 자체로 찾거나 정렬 가능한 필드
우리의 목표: 최대한 적은 수의 인덱스로 최대한 많은 종류의 query를 커버하는 것
3-2. 인덱스 설계 전략
1. Query의 selectivity를 높인다
- 값이 최대한 유니크한 필드를 index로 선정
- index 기준으로 정렬했을 때 값이 골고루 분포되어야 있어야 함.
- 데이터가 뭉쳐있는 부분이 적어야 좋다.
Ex. user.id
, user.signup_date
- 거의 겹치지 않는 값들, index로 삼기에 좋다.
- 사용자 시나리오에서 어떤 필드를 중심으로 데이터가 자라날 지에 대한 것 고려하기
2. 인덱스가 만능은 아니다
- index의 단점은 쓰기 성능 저하, 저장공간 차지
- index가 많다고 무조건 좋은 것은 아니다
- 쓰기가 많이 일어나면 복잡한 index는 만들지 않는다.
- 나의 query가 전혀 의도하지 않은 index를 탈 수도 있다.
- 몽고 디비가 index를 선택하는 방식 때문: 모든 index에 query를 날려 100개(정확 x)의 document를 가장 빨리 가져오는 index를 선택한다고 한다
- 찝찝하면 테스트 필수
3. 지속적으로 관리해야 한다
- 기능이 추가, 변경되면 데이터 모델과 쿼리도 추가, 변경
- 변경된 상황에 맞게 Index를 추가하거나, 사용하지 않게된 index는 제거한다.
.explain('exexuteTime어쩌구').. 수행시간 측정 가능
아악 뭔소리야 한글번역이 제대로 안됬는데요?