Node.js

node>검색기능 만들기 2 (index 설명)

연습노트 2024. 9. 19. 09:27

Database가 게시물을 찾는 방법 

 

어떤 컬렉션에 { like : 어쩌구 } 가 기재된 document가 되게 많다고 가정해봅시다.

갑자기 코드짜서 like : 100이랑 일치하는 document를 가져오라고 코드짜면 컴퓨터는 어떻게 하는지 아십니까.

컬렉션의 모든 document를 하나하나 전부 다 검사해봅니다. 

document가 1억개 있으면 1억개 전부 검사해봅니다.

당연히 컴퓨터는 document에 뭐가 적혀있는지 모르기 때문에 당연히 모든 document를 까볼 수 밖에 없는 것입니다.

그럼 document가 많아질 수록 되게 느리게 동작하겠죠?

 

근데 다행히 이걸 해결할 수 있는 방법도 있는데 

index라는걸 미리 만들어두면 document가 1억개 있어도 원하는 것만 빠르게 찾아올 수 있습니다.

 

 

 

 

index 동작원리

 

 

 

제가 1부터 100까지 적어놓은 숫자카드를 책상에 진열해놨습니다. 

이제 여러분은 저한테 질문을 던져서 제가 생각하는 카드가 뭔지 맞추면 됩니다.

어떻게 질문해야하죠?

 

멍청한 놈 :

"1입니까"

"2입니까"

이렇게 하나하나 물어봅니다.

 

똑똑한 놈 :

"50 이상입니까?"

"그럼 75 이상입니까?"

이렇게 반을 잘라가면서 물어봅니다.

 

이러면 매우 빠르게 원하는 숫자를 찾아낼 수 있습니다. 

아마 카드가 100만개라도 20번만 물어보면 찾을 수 있을걸요?

반을 잘라가며 찾는걸 멋있는 말로 binary search라고 부릅니다. 

 

그래서 거의 모든 DB들은 기본적으로 binary search 또는 그거랑 비슷한 짓으로 게시물을 빠르게 찾아주는데

근데 binary search를 쓰려면 조건이 있습니다.

카드가 1부터 100까지 미리 정렬이 되어있어야합니다.

그래야 "50 이상입니까~" 라고 물어보고 필요없는 카드들을 치울 것 아닙니까

 

그래서 데이터베이스에 있던 document들도 미리 정렬이 되어있어야 binary search를 할 수 있습니다.

그래서 여러분도 컬렉션에 있던 document들을 복사해서 미리 정렬해두면 되는데

정렬된 컬렉션 복사본을 index라고 부릅니다. 

 

 

 

 

 

index 만들기 

 

지금 글 document들이 있는데

원하는 글제목을 빠르게 찾고 싶은데 어떻게 하죠? 

- document 들을 글제목 순으로 미리 정렬해두면 되는 것 아닐까요?

그러면 됩니다. 멋있는 말로 index를 만들어두면 됩니다.

참고로 숫자 뿐만 아니라 글자도 가나다 순으로 정렬가능합니다.

 

 

 

 

 

▲ 실제로 index를 하나 만들어두고 싶으면 MongoDB 들어가서 

원하는 collection 안에서 index 어쩌구 버튼 누르면 됩니다.

 

아마 collection마다 index 하나가 이미 기본으로 있는데 

_id 순으로 정렬해둔 index가 하나 있다는군요.

그래서 항상 _id로 뭔가 찾는건 매우 빠른 이유가 여기있었습니다.

index를 새로 만들고 싶으면 create index 버튼이 어딘가 있을텐데 눌러봅시다.

 

 

 

 

▲ 그 다음에 index 만들 필드명 : 데이터타입을 기재합니다.

데이터타입은 문자의 경우 "text", 숫자는 1을 기입합시다.

 

예를 들어 title 필드를 정렬해두고 싶으면

"title" : "text" 적으면 됩니다.

문자가 아니라 숫자가 그 필드에 저장되어있으면 

"title" : 1 적으면 되겠군요.

 

 

 

db.collection().find( { $text : { $search: '안녕' } } ).toArray()

방금 만든 index를 사용해서 검색하려면 좀 문법이 달라져야합니다.

원래는 .find( {title : '안녕'} ) 이렇게 찾았는데 $text 연산자를 이용해야 index를 활용해서 빠르게 찾아줍니다.

(참고) 숫자를 찾는 경우엔 $text 필요없이 그냥 평소에 쓰던 문법 그대로 써도 index를 알아서 사용해줍니다.

 

 

 

 

 

성능평가

 

index 있나 없나 성능적으로 별 차이 없어보입니까

성능을 정확히 판단하고 싶으면 .explain('executionStats') 를 사용해봅시다.

 

db.collection().find( { title : '안녕' } ).explain('executionStats')
db.collection().find( { $text : { $search: '안녕' } } ).explain('executionStats')

위 2개 결과를 변수에 저장해서 출력해보면 이상한 데이터들이 주루룩 나옵니다.

 

 

 

 

▲ 이런거 살펴보면 됩니다.

- 지금 document가 13개 있는데 13개의 document를 까봤다는군요.

- 스캔방법은 COLLSCAN을 사용했다고 나오는데 이게 뭐냐면 collection 전체를 스캔했다는 뜻입니다.

그래서 모든 document를 다 까봐서 느리게 동작한다는 뜻입니다.

 

 

 

 

▲ 근데 index를 활용해서 검색하면 이런게 나옵니다.

- document를 1개 밖에 안까봤고

- COLLSCAN같은 전체 스캔을 안했다고 나오는군요.

COLLSCAN만 안뜨면 빠르게 동작한다고 생각하셔도 됩니다.

 

 

 

 

 

 

index 단점

 

(단점1) index는 진짜로 컬렉션을 통째로 복사해서 정렬해두는 것이라서 용량을 그만큼 차지하게 됩니다.

그래서 꼭 검색작업이 많은 필드들만 index 만들어두는게 비용적으로 이득입니다.

 

(단점2) document 추가/수정/삭제시 index를 똑같이 수정해줘야 되니까

document 추가/수정/삭제시 느려질 수도 있기 때문에 꼭 필요한 필드만 index 만들어두는 것이 좋습니다. 

 

(단점3) 심각한 단점이 하나 더 있는데

문자로 정렬한 index는 만들면 빠르긴 한데 정확한 단어 밖에 검색을 못합니다. 

왜냐면 인덱스를 만들 때 띄어쓰기로 단어들을 구분하기 때문에 

 

'안녕'이라고 검색어를 입력하면 

'안녕' 단어는 정확하게 검색은 가능한데 

 

- 안녕을

- 안녕이라는

- 안녕하세요

 

이런 단어들은 검색을 못하는겁니다. 

그래서 뭐 영어 검색할 땐 이런걸로도 충분할 수 있는데

단어에 조사 같은게 많이 붇는 한국어 같은 언어들은 text index 만들어봤자 아무 쓸데가 없음

그래서 그냥 index 만드는건 숫자 검색시에나 유용하니까 그럴 때 사용하면 되고 

우리가 지금 원하는 건 네이버같은 검색기능 아닙니까.

'안녕'을 검색하면 '안녕하십니까'도 나와야합니다.

 

그런 식으로 검색이 잘 되게 index를 만들고 싶으면

full text index 아니면 search index 라고 부르는걸 만들어두면 됩니다. 

다음 시간에 만들어봅시다.