DB - MongoDB OperationFailed Sort operation used more than the maximum 33554432 bytes of Ram.

2019. 9. 19. 12:06Database/MongoDB

 

해당 예외는 정렬시 사용되는 메모리 크기에 관한 예외이다. find로 데이터를 조회한 후에 sort()를 통해서 정렬을 할때, 만약 인덱스를 이용해서 정렬을 수행할 수 있을 때는 메모리 크기와 크게 관계가 없지만, sort() 옵션이 인덱스를 사용할 수 없을 때는 MongoDB 서버가 쿼리를 실행하는 도중에 퀵소트를 실행해서 find 명령의 결과 도큐먼트를 정렬한 다음 클라이언트에게 응답한다. 이때 정렬을 위한 추가적인 큰 메모리 공간이 필요하다. 몽고디비 서버는 기본값으로 정렬을 수행할때 사용할 수 있는 메모리값이 대략 32MB이다. 즉, 아주 큰 결과 도큐먼트들을 정렬할 때는 해당 값(32MB)을 초과하여 위와 같은 예외를 발생시킬 수 있다.

 

이렇게 메모리 공간이 부족해서 정렬을 수행하지 못하는 경우에는 3가지 정도의 해결방법이 있다. 정확히는 해결방법이라기 보다는 우회방법이라고도 볼 수 있을 것 같다.

 

  1. 정렬 작업이 인덱스 필드를 활용할 수 있게 쿼리를 변경

  2. 정렬을 위한 메모리 공간을 더 크게 설정

  3. find() 대신 aggregate()를 사용하고 allowDiskUse 옵션을 true로 설정

> use admin

> db.runCommand({setParameter:1,internalQueryExecMaxBlockingSortBytes:1024*1024*1024})

 

> db.collection.aggregate([$sort:{field:1}],{allowDiskUse:true})

 

추가적으로 정렬을 위해 할당된 메모리는 쿼리가 완전히 완료되기 전까지는 운영체제에 자원을 반납하지 않는다. 즉, 클라이언트의 쿼리의 결과를 모두 가져가지 전까지는 정렬을 위해서 할당된 메모리 공간이 반납되지 못한다는 것이다. 만약 클라이언트가 나머지 결과가 더 필요하지 않아 커서에 도큐먼트가 남아 있는 상태로 방치한다면 커서 타임아웃(기본값 10분)이 나기전까지는 메모리에 그대로 남아있게 된다. 

 

그래서 쿼리의 실행 결과로 전달받은 커서는 반드시 모든 도큐먼트를 클라이언트로 가져오거나, 그렇지 않고 더 이상 필요하지 않아서 도큐먼트 패치를 중간에 멈추는 경우에는 커서를 반드시 닫아주는 게 좋다.