MongoDB Generic Index
몽고디비의 장점이 많지만, 그 중에서도 몽고디비를 사용하는 가장 큰 이유는 NoSQL 이라는 점일 것이다. 그런데, 로그성 데이터 외에는 실제로 Schema Free한 형태로 DB를 이용하는 경우는 거의 본 적이 없었다. 물론 스키마를 작성하지 않아도 프로그램 소스만 수정하면 바로 데이터 구조를 변경할 수 있지만, 뭔가 2% 부족한 느낌적인 느낌? 또 많은 경우 MongoDB 라이브러리로 Mongoose 같은 ORM 라이브러리를 사용하는데, 이 경우 소스코드 안에서 Schema를 셋팅해야함으로 완전한 Schema Free한 형식이 아니라고 볼 수 있지 않을까 싶다.
특히 인덱스의 경우가 더 그런데, 스키마가 변경되는 것에 맞춰서 인덱스도 새로 걸어줘야하는등, 뭔가 NoSQL 이라는 장점을 제대로 쓰고 있는 것인가? 정말 Schema Free 스럽게(?) 사용할 수 있는 방법은 없나? 라는 생각을 하게 되었다.
그러던 중 다음과 같은 글을 찾았는데, 이 글에서는 예측 불가능한(?) 여러 필드에 인덱스를 거는 방법을 소개해 주고 있다.
MongoDB Indexing tip #3: too many fields to index? Use a generic index
여기서는 Generic Index 라고 명명했는데, 실제로 MongoDB에 그런 인덱스 형태가 있는 것은 아니고 Subdocument Index와 Multikey Index를 조합한 형태의 인덱스를 사용하는 방법이다.
해당 글에서는 두 가지 방법을 소개하고 있는데, 두 번째 방법이 글에서 소개한대로 인덱스 사이즈가 많이 커지는 단점은 있지만, 속도는 확실히 더 빨랐다. 있단 디비는 뭐니뭐니해도 속도!-_- 라는 생각을 가진지라 ㅎㅎ 두 번째 방법을 사용하기로 했다.
하지만 아무리 자유로운 스키마가 좋다고 해도, 어느정도 가이드(?)는 있어야 하기 때문에 고정 필드, 인덱스를 사용해야하는 필드와 일반적인 데이터를 가져야하는 필드들을 의미 기반으로 나누어 보았다. (아래는 샘플 데이터와 테스트 코드들)
다만, 두 가지 문제가 있었는데, 하나는 Range 쿼리는 인덱스 스캔 플랜 문제로 인덱스를 걸지 않은 것 보다 훨씬 느리다(!)는 것. 아직 간편한 방법을 찾지 못했는데, 이 부분은 한 발 물러서서 Range 쿼리가 필요한 데이터는 필드를 명시적으로 따로 나누는 방식을 쓰는 것이 좋지 않을까 싶다. (이 문제는 MongoDB 커뮤니티에 이슈로 올라와는 있다. 3.0이 곧 나온다는데 거기에서는 해결되어있었으면 좋겠다. +_+)
두 번째 문제는 이렇게 임베디드 엘리먼트를 검색하는 경우 find로는 임베디드 엘리먼트로는 소팅이 되지 않았다. 이 경우 aggregate를 사용하면 되는데, 경험이 미천하다보니 익숙해지는데 시간이 좀 걸렸다.
이렇게 하니 어느정도 내가 원하는 형태에 근접한 데이터 형식을 가지게 되었다. 다만, 인덱스 사이즈가 상당히 커지는 문제가 있기 때문에 실제 서비스에 적용할지, 그리고 적용시 쿼리 최적화에 대한 문제는 조금 더 생각해봐야 할 것 같다.