08. 인덱싱과 쿼리 최적화

주요 내용
  • 인덱싱의 기본 개념과 이론
  • 인덱스 관리를 위한 실용적인 조언
  • 복잡한 쿼리에 대한 복합 인덱스 사용
  • 쿼리 최적화
  • 모든 MongoDB 인덱싱 옵션

인덱싱의 기본 개념과 이론#

인덱스#

  • 책갈피로 설명 (없으면 full scan 해봐야 함)
  • 인덱스를 설정한 키로 검색하면 도큐먼트 가져오는 속도 증가
  • 한 쿼리에 하나의 인덱스를 사용

복합키 인덱스(compound-key index)#

  • 두가지 이상의 키를 동시에 사용하는 인덱스
  • 복합 인덱스에 사용되는 키 순서가 중요! (용도에따라 결정)
  • ingredient-name 복합 인덱스가 있다면 ingrendient 단일 인덱스는 필요 없음
    • 복합인덱스로 ingredient 단일 인덱스 기능도 가능하기 때문
    • a-b 복합 인덱스가 있따면 a 단일 인덱스는 필요없고, b 단일 인덱스는 필요하다
  • 복합키로 조회 성능을 향상 시켜야하는 경우에만 사용

인덱스 사용 시 유의사항#

  • 인덱스는 유지비용이 들어감
    • 삽입, 삭제, 인덱스 키 업데이트, 도큐먼트 재배치 마다 인덱스를 수정해야함
    • 인덱스 사용은 읽기 위주의 기능/서비스에 적합
  • 인덱스가 있더라도 읽기 쿼리를 빠르게 처리하지 못할 가능성이 존재
    • 인덱스와 작업 중 데이터를 램에서 다 처리하지 못할 때 발생 (페이지 폴트 + 스래싱)

B-트리#

  • 인덱스는 B-트리로 구성됨
  • 특징
    • 정확한 일치, 범위 조건, 정렬, 프리픽스 일치, 인덱스 전용 쿼리 등 효율적으로 해당 쿼리들을 처리해 주는 구조
    • 항시 balanced 상태 유지

인덱스 타입#

  • 고유 인덱스 (unique index)
    • unique 키
    • db.users.createIndex({username: 1}, {unique: true})
  • 희소 인덱스 (sparse index)
    • 3.2 부터 partial index 를 sparse index 상위 세트로 제공하니 이를 활용하자
    • 인덱스의 키가 널이 아닌 값만 존재
    • 기본적으로 밀집(dense) 인덱스
      • 밀집 엔덱스는 null 값이 여러개인 필드의 인덱스에선 사용 불가 (null 한개 이후로 생성이 안됨)
      • 그래서 null이 포함된 unique 인덱스, 즉 고유 희소 인덱스 를 만들때 사용
    • 고유 희소 인덱스
      • db.products.createIndex({sku: 1}, {unique: true, sparse: true})
    • 익명 리뷰와 user_id가 null이 많이 존재할 때도 사용
      • db.reviews.createIndex({user_id: 1}, {sparse: true, unique: false})
      • 대신 null에 대한 검색은 포기
  • 다중키 인덱스 (multikey index)
    • 인덱스 키의 여러개 엔트리가 동일한 도큐먼트를 가리킨다
    • tags: ["tools", "gardening", "soil"] 로 예를 들면,
      • tools 을 가지는 모든 다큐먼트는, tools 인덱스 키로 검색 가능하다
      • 즉 한 인덱스 키가 여러개의 다큐먼트를 가리킬 수 있고,
      • 여러 인덱스 키가 하나의 다큐먼트를 가리킬 수 있다
    • 단일 키보다 다중 키 인덱스가 생성, 업데이트, 삭제 비용이 크다
  • 해시 인덱스
    • 키에 순서가 없이 해시로 동작하는 인덱스
    • db.recipes.createIndex({recipe_name: 'hashed})
    • 제안 사항
      • 범위 쿼리 지원 X
      • 다중키 해시 인덱스 지원 X
      • 부동 소수점 값은 해시 전 정수로 변환됨
    • 인덱스의 지역성이 골고루 분포되므로 샤딩에 유리
    • 기본키(ObjectId, OID)가 해시 인덱스를 사용
  • 지리공간적 인덱스 (geospatial index)
    • 위도, 경도값에 따라 도큐먼트를 특정 위치에 가까이 배치하는 인덱스

인덱스 관리를 위한 실용적인 조언#

인덱스 명령어#

  • 인덱스 조회: db.users.getIndexes()
  • 인덱스 생성: db.users.createIndex({zip: 1})
  • 인덱스 삭제: db.users.dropIndex("zip_1") (인자로 조회한|지정한 인덱스 이름을 넣는다)

인덱스 구축#

  • 인덱스를 운영되고 있는 컬렉션 위에 만드는 이유
    1. 데이터 이전 시, 이전 완료 후 인덱스 구축 (이상적으로 균형잡히고 압축된 인덱스를 만들 수 있음)
    2. 새로운 쿼리 최적화
  • 인덱스 구축은 데이터 마이그레이션과 같이 취급 (오래 걸림)
  • 인덱스 구축 단계
    1. 인덱스할 값 정렬
    2. 정렬된 값 인덱스로 삽입
  • 인덱스 구축 진척상황 확인: db.currentOp()

백그라운드 인덱싱#

  • 실제 서비스 되고 있는 DB 에 대한 엑세스를 중지시킬 수 없을때 사용
  • 쓰기는 락이 걸리지만 조회는 가능함 (성능저하는 그래도 발생)
  • db.values.createIndex({open: 1, close: 1}, {background: true})

오프라인 인덱싱#

  • 복제노드를 오프라인으로 바꾸고 인덱스 구축
  • 마스터로부터 해당 인덱스를 업데이트 받음
  • 업데이트 완료 후, 해당 노드를 프라이머리 노드로 바꿈
  • 세컨더리 노드의 인덱스 업데이트를 같은 방식으로 진행

백업#

  • 백업에 인덱스를 포함해야 한다면 MongoDB 데이터 파일 자체를 백업해야함

defragmenting#

조각 모음

  • 업데이트 삭제가 빈번하다면 인덱스가 단편화 됨
  • 이때 db.values.reIndex() 를 통해 인덱스 재구축 가능
  • 마찬가지 락 주의

쿼리 최적화#

느린 쿼리 탐지#

  • bson 파일을 이용한 mongorestore
    • mongorestore -d stocks -c values <bsonFileName> -u '' -p '' --authenticationDatabase=<dbName>
  • 프로파일링 세팅
    • db.setProfilingLevel(2): level 2는 제일 상세한 수준
    • system.profile 에 capped 컬랙션 형태로 저장 (128kb)
    • 각 쿼리의 explain 결과물들이 저장

느린 쿼리 분석#

  • 쿼리의 마지막에 explain() 붙여 확인 가능
  • db.values.find({}).sort({close: -1}).limit(1).explain()
  • db.values.find({}).sort({close: -1}).limit(1).explain('executionStats')
  • 리턴된 문서 수 (n) 과 쿼리 시 살펴본 문서 (nscanned) 의 수가 거의 근접한지 확인
  • 인덱스 스캔되는지 확인 (COLLSCAN 풀 스캔, IXSCAN 인덱스 스캔)

커버링 인덱스#

Last updated on