- Published on
mongodb structure 에 대해서
- Authors
- Name
- 길재훈
mongodb index
Index 는 간단하게 이야기하면 책의 색인 목록이다.
우리는 책에서 어떤 특정페이지를 찾을때, 이러한 색인 목록에서
페이지를 찾고, 그 페이지를 펼쳐서 한번에 원하는 주제를 찾는다.
mongodb 및 여러 DBs 들 역시 이러한 Index 를 제공한다.Index 를 만들면, 원하는 Query 를 더 효율적으로 검색할 수 있도록 만든다.
이때, Index 를 만들기 위해서 그 기준으로 삼은 필드의 값들을Index key 라고 부른다.
사실, Mongodb 는 모든 Document 마다 고유의 Index key 를
가지는데, 바로 _id 이다.
_id 는 Primary key 로써, 해당 Document 의 Unique key(고유한 키) 역할을 한다.
이러한 Primary key 를 사용하여, 우리는 원하는 Document 를
정확하게 찾아서, 읽고 쓰기 작업을 한다.
이말은, Mongodb 의 Document 생성시, _id 필드는 자동적으로 Index 로 생성된다는 것이다.
Index 를 이해하기 위해서는 B-Tree 구조에 대해서 이해하는것이 좋다.
B-Tree
B-Tree 는 자료구조중 하나이다.
이 구조는 루트 노드와 자식 노드로 나누어지는데, 각 노드가 마치
나뭇가지가 갈라져서 이루어져있는것 처럼 생겨서 Tree 구조라고 부른다.
이렇게 갈라져서 나누어져 있는 구조에서 하나의 노드당 2개 이상의 자식노드를 가지는것이 B-Tree 의 특징이다.
각 노드는 특정 data 의 범위를 가진다. 이때, 특정 Node 의 갯수에 따라서 child node 가 달라지는데,
만약, Node 의 data갯수가 1 이면, child node 는 2개가 된다. 만약, Node 의 data갯수가 2 이면, child node 는 3 개가 된다.
즉, Node 의 data + 1 개의 child node 를 가진다.
이때, child node 가 생성될때, 가장 왼편의 child node 는parent node 의 data 보다 작은 값들이며, 가장 오른쪽의 child ndoe 는 parent node 의 data 보다 큰 값이다.
만약 중간 child node 가 있다면, parent node 의 data 보다 크거나 작은 값이다.
이렇게 각 child node 는 parent node 보다 작거나 큰값을 가지므로,parent node 에서 child node 순으로 각 data 를 찾아서 내려가는 구조를 띄고 있다.
이는 마치 책의 원하는 page 를 찾기 위해, 책을 반으로 쪼개고, 원하는 페이지가 없다면, 쪼갠 부분에서 다시 반으로 쪼개면서,
원하는 페이지가 나올때까지 개속 쪼개서 찾아가는 방식과 비슷하다.
Mongodb 는 이러한 방식으로 값을 찾은 뒤 자료를 가리키는 포인터를 이용하여 도큐먼트를 찾아낸다.
구조상 보면 알겠지만, child node 의 수가 굉장히 많아지기 시작하거나,
책으로 비유하면,
목차를 어마어마한 수준으로 많이 만드는것이다.
목차는 특정주제에 맞는page를 알려주지만,
그주제가 너무 세분화되어목차를 구성하면,목차의 제대로된 역할을 하지 못한다.
그렇지않고, node의 수가 너무 적으면 그 효율이 떨어진다.
page수가 적은데,목차를 만들 이유가 없다.
그렇기에, query시 적은 횟수로 조회할 수 있도록 정해진 규칙을 만드는것이 중요하다.
Index 는 매번 빈번히 만들어지는것이 효율적인 방법은 아니다.
읽기 작업이 많은 data 라면 유용하게 사용가능하겠지만,
쓰기 작업이 많은 Document 라면, Index 에 맞지 않다.
그러므로 Index 를 생성시, 잘 생각하고 만드는것이 중요하다. Index 는 이러한 여러 사항을 고려하기에, 몇 가지 종류로 만들 수 있다.
Single-key Index
Index 생성시 한가지 종류의 Index 값을 갖는것을 Single-key Index 라고 부른다.
Mongodb 에서 default 로 Document 생성시, _id 를 Indexing 한다고 했는데 여기서 _id 를 Single-key Index 라고 부른다.
이렇게 _id 값을 가진 key 를 index 로 가지면, _id 의 값
범위에 해당하는 Document 를 불러올 수 있다.
특정 field 를 Single-key Index 로 등록한다고 가정해보자.
> db.posts.createIndex({ rating: 1 })
이Index 는 rating field 를 오름차 순으로 생성한다.
위의
1값은 오름차순,-1값은 내림차순이다.
이때, B-Tree 구조로 이해하면 대략적인 그림이 그려진다. rating 의 Node 구성시 작은값을 왼쪽으로, 그리고 큰값을 오른쪽으로 구성된 Tree 를 만든다.
즉, 작은값에서 큰값으로 구성된 트리가 생성된다는 것이다. 그리고, query 하는 data 를 이렇게 구성된 트리에서 분기되면서 찾아 나간다.
즉, Index 된 rating 을 사용하여 정렬 및 값 범위에 해당하는 값들을 query 할때 매우 유용하게 사용가능하다
그럼, 만약, rating 을 오름차순으로 하고, title 을 내림차순으로 정렬한다고 가정해보자.
이때, Single-key Index 특성상 Index 처리가 불가능하다. rating 은 당연히 Index 를 통해 처리하지만, title 은 index 로 처리되지 않는다.
title 이 Single-key Index 로 되어있다고 가정하더라도, rating 과 같은 Index 로 묶여 있지 않기에 의미가 없다.
생성한 Index 사용하여 한가지 field 의 Document 를 찾고, 다른 field 값은 하나씩 도큐먼트를 대조해서 검색하게 될것이다.
이러한 여러 field 를 query 할때 필요한것이 Compound Index 이다.
Compound Index
이는 Single-key Index 와는 다르게, 여러개의 field 를 사용하여 Index 를 생성한 방법이다.
Compound Index 설정시 필요한 부분은 각 field 의 정렬을 어떻게 할지 정해주어야 한다.
또한, 어떠한 field 를 우선할지도 같이 정해야 한다.
> db.posts.createIndex({ rating: 1, title: -1 })
rating 은 오름차순으로, title 은 내림차순으로 Index 를 생성했다.
이때, 정렬 에 대한 Index 탐색과, 값의 범위 에 대한 Index 탐색이 Single-key Index 와는 약간 다르다.
Compound Index 의 정렬에 대해서
Single-key Index 는 Compound Index 와는 다르게,
하나의 값만을 사용하여 query 한다.
하지만 Compound Index 같은 경우 field 가 여러개라서정렬 시 문제가 있을 수 있다.
위의 Index 는 rating, title 순으로 Index 를생성했으며, rating 은 오름차순, title 은 내림차순으로 되어있다.
이때, 만약 위의 정렬방식대로 원한다면, query 시 field 순서가 중요하다.
즉, rating, title 순으로 query 해야 각 정렬방식에 맞게 query 가능하다.
만약, rating 을 오름차순, title 을 오름차순으로 한다면, rating 은 index 를 사용하여 잘 정렬되지만, title 은 index 를 이용하지 못하고 일일이 순서를 맞춰서 정렬한다.
만약 이 순서에 맞게 하지 않고 title, rating 순으로 query 하면, 정렬 이 제대로 작동하지 못한다.
이렇게 정렬 은 field 순서와, 정렬 방향을 명확하게 해야만 잘 잘동한다.
값의 범위에 대한 Compound Index
값의 범위 에 대한 Index 는 다르다. 생각해보자, 값의 범위 는 정렬 할 필요가 전혀 없다.
정렬 은 오름차, 내림차 순이라는 부분이 존재하지만, 값의 범위 는 이러한 순서가 존재하지 않는다.
앞에서 Compound Index 에서 두개의 field 인 rating 과 title 을 index 로 생성했다.
title 이 공지 이고, rating 이 3 라는 포스트를 조회한다고 가정해보자.
이때 우선순위는 index 적용순서와 다르게 title 로 한다. 이는 제대로 동작한다.
title 에서 공지 를 찾고, rating 을 찾는다.
이때, rating 을 먼저 조회하든 title 을 먼저 조회하든 사실 둘다 index 를 사용하여 조회하게된다.
하지만, 그렇다고 아무런 생각없이 field 우선순위를 지정하는것은 좋지 않다.
Index 를 더 효율적으로 검색하기 위해서는, 중복되는 조회가 많은 field 를 우선으로 두어야 좋다.
Compound Index 는 field 가 여러개이므로, 두개의 field 를 이용한다.
이때, 중복이 적은 field 를 우선에 두어 검색한다면, 나중에 중복이 많은 field 를 또다시 검색해서 처리하므로, 이러한 동작이 일어나지 않도록 하기 위해서는 처음부터 중복이 많은 field 를 우선순위로 검색하는것이 좋다.
어차피 조건에 부합하는 query 에 의해 중복이 적은 field 는 중복이 많은 field 에 포함관계를 형성하고 있기 때문이다.
간단하게 말하면, 불필요한 검색을 하지 않는다는 것이다.
Multikey Index
Mongodb 는 Array 를 지원한다.
이때, Array 는 Object 와는 다르게, Property 가 아니라value(값) 를 가진다.
Mongodb 는 실제로 Array 의 값을 query 할수 있다.
> db.posts.find({ tags: '잡담' })
위의 tags 가 Array 라 가정하고, tags 내부에 잡담 이라는 값을 가진 모든 Document 를 검색한다.
이말은 Array 의 값 을 Index 로 저장할 수 있다는 것이다.
> db.posts.createIndex({ "review.title": 1 })
위는 review 라는 Array 에 있는 Document 의 title 값을 인덱싱한것이다.
이러한 값 을 이용한 Index 를 Multikey Index 라 부른다.
그외
이외에 Text Index 및 Hash Index 가 존재한다.
실제로 Text Index 는 언어의 형태소를 기반으로 해서 어원을 분석하여 단어 검색이 이루어지도록 해주는것 같은데, 한국어는 제대로 지원하지 않는것으로 알고 있다.
사용하려면, Elastic search 를 사용하라고 하는것 같다.
Hash Index 는 Hash 함수 를 이용해서 작은 크기로 변형된 B-Tree 를 만든다고 한다.
# title 에 아무런 Index 가 없다고 가정한다.
> db.posts.createIndex({ title: "hashed" })
이 Index 는 Compound Index, Array 를 값으로 가진 field 에 설정할 수 없다고 한다.
기존의 값의 범위 및 정렬 에 대한 인덱싱은 사용하지 못하고, 오직 검색을 위한 방식으로 사용한다고 한다.
Index Properties
인덱스 생성시 여러가지 index type 들을 지원한다.
name
Index 의 이름을 지정할 수 있다
> db.posts.createIndex(
{ title: "hashed" },
{ name: "hashed title"}
)
TTL
TTL(Time To Live) Index 생성역시 가능하다.
TTL Index 는 기간을 정해놓고 해당 기간이 지나면 자동적으로 삭제되는 Index 를 말한다.
> db.posts.createIndex(
{ expire: 1 },
{ expireAfterSeconds: 60 * 60 * 24}
)
주의할 점은, field 의 값이 시간값이어야 하며, key 를 하나만 갖는 Single-key Index 이어야한다.
Unique Index
field 의 값을 고유하게 만들기 위한 Index 를 생성한다.
Mongodb 는 Unique 할수 있도록 각 field 를 비교하지 않는다. Index 를 통해 Unique key 를 생성하도록 만든다.
> db.users.createIndex(
{ email: 1 },
{ unique: true }
)
위는 등록된 email 을 오름차순으로 설정하며, 같은 email 이 없도록 고유한 값으로 만들어준다.
Sparse Index
흔히, Javascript 에서 undefined 나 null 값이 포함된 Array 를 Sparse Array 라고 부른다.
Sparse 의 뜻을 희소 라고 하는데, 다른 뜻으로는 부족한, 드문드문난 으로 해석가능하다.
희소배열이라고 했을때, 와 닿지 않는 부분이지만, 다른 뜻인드문드문난 배열이라고 하면 그 뜻이 명확히 이해가 된다.한자어인거 같은데, 이러한 용어들이 정말 별로다...
이말은 배열의 요소가 연속적으로 이어지지 않고, 중간이 비어있는(undefined) 값을 가진 배열이라고 생각하면 된다.
const sparseArr = [1, 2, undefined, 3, 4, undefined]
Index 역시 field 값이 null 일때, Index 에서 제외하게 만드는 기능을 제공한다.
> db.images.createIndex(
{ title: 1 },
{ sparse: true, unique: true }
)
위는 images 의 title 값을 unique 하게 만들고,title 의 값이 null 일때, index 생성이 안된다.
Partial Index
일부 Document 에만 Index 처리한다.
> db.users.createIndex(
{ username: 1 },
{ unique: true, partialFilterExpression: { age: { $gte: 21 } } }
)
이는 age 값이 21 보다 크거나 같다면 unique index를 생성 하지 않는다.
> db.users.insertMany( [
{ username: "david", age: 27 },
{ username: "amanda", age: 25 },
{ username: "rajiv", age: 32 }
] )
위는 index 생성되지 않는다.
db.users.insertMany( [
{ username: "david", age: 20 },
{ username: "amanda" },
{ username: "rajiv", age: null }
] )
이는, age 값이 21 작으므로 qniuqe index 가 생성된다.
Partial Index 는 특정 field value 를 조회할때만 index 를 사용하고자 할때 유용하게 사용가능하다고 한다.
getIndexes
어떠한 인덱스들이 있는지 확인 가능한 함수이다.
> db.movies.getIndexes()
dropIndex
Index 를 제거한다. Index 의 이름 및 설정을 같이 넣어주어야 한다.
> db.movies.dropIndex({ title: 1 })
dropIndexes
해당 collection 의 모든 Index 를 제거한다.
> db.movies.dropIndexes()
Index 는 DB 공부하면서 그 개념에 대해서 알아야 겠다는 생각에 내용을 정리한 것이다.
현재는 MongoDB 관련해서 공부하고 있지만, 추후 SQL 에 대한 공부할때 이러한 개념은 좋은 공부가 될것이다.