MongoDB 인덱스 생성을 위한 ESR Rule

Caesars·2023년 3월 5일
2

MongoDB

목록 보기
1/2

최근에 MongoDb 강의를 듣던 도중 중요한 내용이다 싶어 관련 글을 작성했습니다. 복합 인덱스를 생성해야 할 때 중요하다 싶은 필드 우선으로 생성해왔는데 기준이 되는 법칙이 있었습니다.


Compound Index

db.collections.createIndex({"item": 1, "stock" : 1, "price": 1})

// 위의 인덱스를 사용할 수 있는 쿼리
db.collections.find({item: "banana"})
db.collections.find({item: "banana", stock : {$gte : 5}})
db.collections.find({item: "banana", stock : {$gte : 5}, price: { $lte : 40000}})
}

대체적으로 단일 인덱스 보다는 복합 인덱스를 많이들 사용한다고 느낍니다. 위의 쿼리처럼 item, stock, price로 구성된 index를 만들어 두면 {item:1}, {item:1, stock:1} 같은 인덱스를 추가할 필요가 없으니까요.

db.collections.find({stock : {$gte : 5}, price: { $lte : 40000}})

하지만 item의 우선순위가 가장 높기 때문에 위의 stock과 price만을 검색하는 쿼리에는 적용되지 않습니다. 그렇다고 모든 필드에 인덱스를 거는 것도 비효율적입니다. 따라서 MongoDB에서는 인덱스의 최대 효율을 내기 위한 규칙으로 ESR Rule을 강조합니다. ESR Rule을 통해 대부분의 경우에 가장 효율적인 키 순서를 찾을 수 있습니다. "대부분"의 경우이기에 몇몇 케이스에는 이 규칙이 적용되지 않을 수 있습니다.

E-S-R Rule

E        Equality First
E->R     Equality Before Range
E->S     Equality Before Sort
S->R     Sort Before Range
E->S->R    Equality Sort Range

  • Equality 가 우선순위가 가장 높습니다.
  • Sort 는 Eqaulity 보다 뒤에 와야 합니다.
  • Range 는 Equality 및 Sort 보다 뒤에 와야 합니다.
find({ x: 123 })
find({ x: { $eq: 123 } })
aggregate([ { $match:{ "x.y": 123 } } ])

Equality 조건은 값을 정확히 일치시키려고 시도하는 모든 필터 조건입니다.

find().sort({ a: 1 })
find().sort({ b: -1, a: 1 })
aggregate([ { $sort: { b: 1 } } ])

Sort 은 결과의 순서를 결정합니다.

find({ z: { $gte: 5} })
find({ z: { $lt: 10 } })
find({ z: { $ne: null } })

Range 는 정확한 일치를 테스트 하지 않기 때문에 여러 키를 스캔할 수 있는 필터입니다.

말그대로 Equality, Sort, Range 순서대로 인덱스를 생성하는 규칙입니다. 마냥 이렇게 하라고만 하면 이해가 되지 않으니 예시를 들어 설명해보겠습니다.

설정

규칙을 설명하는 데 도움이 되는 다음 데이터로 작업하겠습니다.

{city: 'tokyo', pop: 40549, country: 'JP'},
{city: 'newyork', pop: 9058, country: 'US'},
{city: 'la', pop: 3205, country: 'US'},
{city: 'osaka', pop: 10616, country: 'JP'},
{city: 'seoul', pop: 13650, country: 'KR'},
{city: 'busan',pop: 14218, country: 'KR'},
{city: 'daegu', pop: 6227, country: 'KR'},
{city: 'chicago', pop: 17846, country: 'US'},
{city: 'incheon', pop: 1215, country: 'KR'},
{city: 'kobe', pop: 3782, country: 'JP'},
{city: 'kyoto', pop: 8294, country: 'JP'},
{city: 'washington',pop: 3479, country: 'US'},

Equality Before Range

db.city.createIndex({city:1,pop:1})
db.city.find({city:"seul", pop: {$gt: 7000}}).explain('executionStats')

Equality 조건을 만족한 후에 Range를 수행하니 하나의 키만 탐색 후에 결과를 반환했습니다.
만약에 키 순서를 바꾸게 되면 하단의 그림처럼 확인해야 하는 키가 훨씬 많아지게 됩니다.

Equality Before Sort

db.city.createIndex({city:1,country:1})
db.city.find({city:"seul"}).sort({country:1}).explain('executionStats')

위와 마찬가지로 Equality 조건을 만족한 후에 Sort를 수행하니 하나의 키만 탐색 후에 결과를 반환했습니다.
만약 키 순서를 바꾸면 Index 전체를 탐색하게 되는 결과가 나오게 됩니다.

Sort Before Range

db.city.createIndex({country:1,pop:1})
db.city.find({pop: {$gt: 7000}}).sort({country:1}).explain('executionStats')

위 그림만 보면 Range가 Sort보다 먼저 와야 되지 않나 하는 궁금증이 생깁니다. 실제로 인덱스 구성을 {pop:1, country:1}로 바꾼 후에 동일한 쿼리를 실행해 보니 executionStat의 totalKeysExamined가 오히려 감소했습니다. 그럼에도 불구하고 Sort를 더 우선시하는 이유는 Sort에도 걸리는 부하를 줄이기 위함입니다. 인덱스 키를 많이 탐색하는게 생각보다 공수가 크지 않아 차라리 Sort 작업을 줄이는게 더 유리한 경우가 많다고 여기는 것입니다.

executionStats

쿼리가 얼마나 효율적으로 수행됬는지를 판단하는데 사용됩니다. nReturned와 totalDocsExamined 의 차이가 클 수록 쿼리가
비효율적이라는 의미이므로 적절한 키 생성이 필요합니다.

  • nReturned : 반환된 document의 수
  • totalKeysExamined : 키를 몇개나 탐색했는지
  • totalDocsExamined : 몇 개의 document를 탐색했는지

마치며

참고

https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/
https://stackoverflow.com/questions/75462990/mongo-db-esr-rule-for-multiple-index-keys-in-a-compound-index

profile
잊기전에 저장

0개의 댓글