LabHub
学习 学习路径 课程

MongoDB — 文档数据库的判断

慢的原因多半在索引

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

한 줄 요약

인덱스가 없으면 문서 DB 도 전부 훑는다. COLLSCAN 이라고 적힌다. RDB 의 Seq Scan 과 정확히 같은 것이고, 대책도 같다.

概念图: 앞쪽 필드부터 · 못 쓴다. · 결과가 적어도 정렬 전의 후보가 많으면 걸린다는 점 · 필요한 값이 전부 인덱스 안에 있으면 문서를 아예 열지 않는다.

왜 필요한가 — 느린 이유는 대개 하나다

"MongoDB 가 느리다" 는 신고의 대부분은 데이터베이스가 아니라 인덱스가 없는 조회다. 문서가 만 건일 때는 아무도 모른다. 백만 건이 되면 같은 코드가 갑자기 느려지고, 그때 사람들은 데이터베이스를 의심한다.

확인하는 방법은 RDB 와 같다. 계획을 물어본다.

db.items.find({ name: "빨간 배낭" }).explain()

계획 안에서 볼 단어는 둘뿐이다. COLLSCAN 이면 전부 훑은 것이고, IXSCAN 이면 인덱스를 탄 것이다.

숫자를 함께 본다

단계 이름만으로는 절반이다. explain('executionStats') 를 쓰면 실제로 몇 건을 훑었는지가 나온다.

nReturned 실제로 돌려준 문서 수
totalDocsExamined 그것을 찾으려고 열어 본 문서 수

이 둘의 비율이 인덱스의 성적표다. 한 건을 돌려주려고 백만 건을 열었다면 인덱스가 없거나 잘못 걸린 것이다. 반대로 둘이 비슷하면 잘 걸린 것이다.

복합 인덱스는 순서가 전부다

인덱스를 만들기로 했다면 다음 질문은 어떤 순서로 묶느냐다. 문서 DB 의 복합 인덱스도 값으로 정렬된 구조라 앞쪽 필드부터 쓸 수 있고, 그 규칙이 무엇을 쓸 수 있고 무엇을 못 쓰는지를 정한다.

{ status: 1, createdAt: -1 } 로 만든 인덱스는 이렇게 쓰인다.

status 오름차순 그다음 createdAt 내림차순으로 정렬된 복합 인덱스를 세 가지 조회로 훑는 그림. status 로 찾으면 맞는 줄이 붙어 있어 연속 구간 하나만 훑고, 정렬을 붙여도 이미 내림차순이라 공짜다. createdAt 만으로 찾으면 맞는 줄이 흩어져 연속 구간이 없다

그래서 실무의 순서 규칙이 나온다. 같음으로 거르는 필드를 앞에, 범위로 거르거나 정렬에 쓰는 필드를 뒤에 둔다. 범위 조건이 걸린 필드 뒤의 필드는 탐색에 쓰이지 못하고 걸러 내기에만 쓰인다.

정렬이 인덱스를 타지 못하면 조용히 비싸진다. 문서 DB 는 메모리 안에서 정렬하는데 그 양에 상한이 있어서, 넘으면 오류로 실패하거나 임시 파일을 쓴다. 결과가 적어도 정렬 전의 후보가 많으면 걸린다는 점이 특히 사람을 놀라게 한다. 계획에서 정렬 단계가 따로 보이면 그 순서를 인덱스로 옮길 수 있는지 본다.

배열 필드에 거는 인덱스는 성격이 다르다. 배열 원소마다 항목이 하나씩 생기므로 문서 하나가 인덱스에 여러 번 들어간다. 원소가 수백 개인 배열에 인덱스를 걸면 그만큼 커지고 쓰기도 느려진다. 그리고 배열 필드를 둘 이상 한 인덱스에 묶을 수 없다. 조합의 수가 곱해져 폭발하기 때문이다.

마지막으로 필요한 값이 전부 인덱스 안에 있으면 문서를 아예 열지 않는다. 앞에서 본 totalDocsExamined 가 0 이 되는 경우가 이것이고, 목록 화면처럼 몇 개 필드만 보여 주는 조회에서 특히 값이 크다.

현장에서

인덱스를 더 만드는 것이 늘 답은 아니다. 인덱스는 쓰기를 느리게 하고 공간을 먹는다. 문서를 하나 넣을 때마다 모든 인덱스가 갱신된다.

그래서 순서가 있다. 먼저 계획을 읽어 무엇이 느린지 확인하고, 그다음 그 조회 하나를 위한 인덱스를 만든다. "혹시 몰라서" 만든 인덱스는 쓰기 비용만 남기고 아무 조회도 빠르게 하지 않는 경우가 많다.

이 순서를 지키는지가 SQL 최적화 경험을 묻는 면접에서 갈리는 지점이다.