MongoDB 위치기반 조회 성능 최적화

BlackBean99·2023년 5월 22일
5

DB

목록 보기
4/5
post-thumbnail

위치 기반 서비스를 개발하고 있습니다. GeoJSON 형태의 데이터를 가공하고 있습니다.
아래같은 데이터인데요 아.. 파싱하기 귀찮아.. ㅋㅋㅋ

{"type":"Feature","properties":{"OBJECTID":1,
"adm_nm":"서울특별시 종로구 사직동","adm_cd":"1101053","adm_cd2":"1111053000",
"sgg":"11110","sido":"11","sidonm":"서울특별시","sggnm":"종로구"},
"geometry":
{"type":"MultiPolygon","coordinates":[[[[126.97688884274817,37.575650779448786],[126.9770344988775,37.56919453005455],
[126.97597472821249,37.569336299425764],
[126.97537470991254,37.56931556702156],[126.97433193562325,37.56926180051753],[126.96904837001854,37.568194417708334],[126.96854493603384,37.56842767961276],[126.9666499598212,37.56949165520658],[126.96628175024485,37.5697007347987],[126.9660973270804,37.5698565097237],[126.96572852922577,37.570183936115114],[126.96592699822128,37.5703188056862],[126.96601094018429,37.571548395577466],[126.96365922052196,37.57517466066037],
[126.9630860043451,37.57648592001554],[126.96284099051198,37.57666158609274],[126.96281041047263,37.579448809656775],[126.96742431584332,37.57960153712449],[126.96742176302651,37.579263521441646],
[126.9674300601846,37.579192577998604],[126.9674570900956,37.57897525058544],[126.96806604699626,37.57824678046787],[126.96895511695477,37.57793526234028],
[126.96921284296906,37.57793529930939],[126.96941453886579,37.578121124142164],
[126.9696644266947,37.57853113668221],[126.96966721914872,37.57873620513493],[126.96966877353309,37.57899287900988],
[126.96966949910363,37.57911252674959],[126.96990457361626,37.579301753628734],[126.97135197544759,37.57951327793981],[126.97381925784454,37.57937214030263],
[126.97391736338342,37.57848707304101],[126.97393961998088,37.57824042997809],
[126.97433153835757,37.57574990629986],[126.97580378997804,37.5756494688242],
[126.97688884274817,37.575650779448786]]]]}}

이런 데이터에서 제가 위경도 데이터를 통해 법정동을 추출하고 싶습니다.

카카오 Developers 정책에 의하면 사용자의 정보를 저장할 수가 없어서 위경도로 조회해야하거든요

나쁜 춘식이..

이런 문제를 해결하기 위해서 국가에서 제공하는 법정상호명 위치 데이터가 있습니다. 우리는 이 데이터를 utf-8 -> euc-kr 로 인코딩을 바꾼 다음에 저장하여 데이터를 저장합니다.

이 법정동 구획 데이터를 통해 쿼리가 범위 안에 있으면 법정동을 제공하는 API 를 개발하는 과정에서 3초가 넘게 걸리는 문제가 있어서 최적화를 해야겠다는 생각을 했습니다.

그래서 오늘 포스팅은!! mongoDB 2dsphere index 를 통해 성능 개선을 하는 것인데요
그 전에 mongoDB의 인덱스 방식에 대한 이해먼저!

Single-Key Index

특정 키를 기준으로 오름차순, 내림차순 한다는 뜻입니다. 컬렉션 옆에 배열로 만들어진 또 다른 데이터가 있다고 가정했을 때 정렬되어 있기 때문에 범위 탐색도 상당한 효과가 있습니다.

Compound Index (복합 인덱스)

복합 인덱스는 검색에 여러 키가 사용될 때 사용하는 방법인데 이때 다른 쿼리에 각각의 키로 검색할 수 있다는 생각은 하지 말아야 합니다.
groupBy 된 결과 안에서 정렬을 수행했다고 이해해도 좋은데요


MongoDB Manual

이 그림에서는 userId 를 정렬해서 저장했는데 같은 userId 안에서는 score가 정렬되어 있습니다. 단독으로 score로 검색하게 된다면 이 인덱싱이 아무런 의미가 없을 것이에요!!
인덱스로 지정한 각각의 필드는 그 순서대로 쿼리문에 나타나야 한다는 것입니다. score로 단독으로 검색한다고 무조건 빨라진다는 것은 아니라는 뜻이죠

이 개념들을 익히고 나서 2dsphere 인덱스에 대해서 알아볼까요?

2.2dsphere 인덱스

지구 같은 구체에서 위치를 표현할 때 사용하는 쿼리를 지원합니다. 형상 계산, 포함, 교차, 근접에 대한 쿼리를 제공하는데 아주 속도가 좋습니다.

MongoDB 3.2 부터 도입되었으며 2dsphere 인덱스가 MongoDB. 3.2 버전 이상에서 기본으로 채택하는 버전입니다.

2.2 인덱스 생성시 유의사항

  • 버전 2 이상 2dsphere 인덱스는 항상 희박하며 sparse 옵션을 무시합니다. (3버전 써라.)

  • 문서에 2dsphere 인덱스 필드가 없거나 필드가 null이거나 빈 배열인 경우 MongoDB는 문서에 대한 항목을 인덱스에 추가하지 않습니다.

그래서 사전에 Null 처리를 해주고 인덱싱을 하는게 효과가 좋습니다. 제가 쓰는 대한민국 행정동 집적도 데이터같은 경우에 시작 좌표와 끝 좌표가 일치하지 않는 경우가 종종 있어서 Null 이 들어있는 경우가 있어서 저는 전처리를 해주었습니다.

  • 복합 인덱스의 경우 2dsphere 인덱스 필드만 인덱스가 문서를 참조하는지 여부를 결정합니다.

  • 버전 2 이상 2dsphere 인덱스는 MultiPoint, MultiLineString, MultiPolygon 및 GeometryCollection을 비롯한 추가 GeoJSON 객체를 지원합니다.

db.collection.createIndex({ <location field>: '2dshpere' })
db.collection.createIndex({ <location field>: '2d' })

이런 방식으로 인덱스를 생성하면 됩니다.

2.2.1 제한된 수의 인덱스 키

  • 2dsphere 색인에 대한 키를 생성하면 큰 값 배열이 생성될 수 있습니다.

  • 배열 필드에 대한 인덱스 키를 생성할 때 각 요소에 대한 인덱스 키가 생성됩니다.

  • indexMaxNumGeneratedKeysPerDocument 매개변수는 문서당 생성되는 최대 키 수를 제한합니다.

모든 각 필드마다의 인덱스가 생성되기 때문에 너무 많은 필드가 있는 경우 제한을 해줄 필요가 있습니다.

2dsphere인덱스 생성

  • 2dsphere 인덱스를 생성하려면 db.collection.createIndex() 메서드를 사용하고 인덱스 유형을 "2dsphere"로 지정합니다.
  • 위치 필드는 GeoJSON 개체 또는 레거시 좌표 쌍일 수 있습니다.
  • 복합 2dsphere 인덱스는 여러 위치 및 비위치 필드를 참조할 수 있습니다.

실제로 의미있는 결과가 나올까? (성능비교)

{
  geometry : {
      $geoIntersects : {
          $geometry: {
              type : "Point" ,
              coordinates : [127 ,36]  
              }
          }
      }
}

해당 쿼리를 날려보면

약 2.442초가 걸립니다 어제 했을 때는 3초가량 걸렸는데 ㅎ

MongoDB Compass 로 2dsphere를 만들어보겠습니다.

나중에 실시간 쿼리를 날려야 할 일이 있으면 아래 TTL 옵션을 키시면 됩니다.

인덱스를 생성하고 다시 쿼리를 날려보면?

아니 세상에 40ms 가 나왔습니다.

2442 -> 40 으로 약 61배 차이나는 검색 성능 차이입니다.

여러분도 NoSQL 을 개발하신다면 Index 가 엄청난 성능 향상이 있다는 사실을 알고 적극 도입하셨으면 좋겠습니다.

디프만 위치기반 서비스를 개발하고 있는데 실제 개발 기록은 추 후에 포스팅하도록 하겠습니다 ㅎㅎ

reference
https://www.mongodb.com/docs/manual/core/2dsphere/

profile
like_learning

1개의 댓글

comment-user-thumbnail
2023년 5월 30일

몽고디비 한번도 안써봤는데 좋은 정보 감사합니다

답글 달기