몽고디비 autoIncrement가 이해가 안됩니다
조회수 3983회
몽고DB에 AutoIncrement 기능이 없다길래 찾아보니 레퍼런스 문서에서 Collection을 하나 더 만들어서 함수로 구현하는 방법을 제안하더군요
그래서 한번 코드를 살펴보고 있는데 잘 이해가 안되서 질문올려봅니다.
function insertDocument(doc, targetCollection) {
while (1) {
var cursor = targetCollection.find( {}, { _id: 1 } ).sort( { _id: -1 } ).limit(1);
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
doc._id = seq;
var results = targetCollection.insert(doc);
if( results.hasWriteError() ) {
if( results.writeError.code == 11000 /* dup key */ )
continue;
else
print( "unexpected error inserting data: " + tojson( results ) );
}
break;
}
}
cursor가 제일 마지막에 있는 document를 찾는다는건 알겠는데 그렇다면 hasNext를 쓰는 이유를 모르겠습니다. 그냥 +1해서 _id로 사용하면 AI가 되는거 아닌가요? 그리고 Counter용 Collection이 필요한 이유도 모르겠고요.
한가지 더 질문드리자면 document 중에서 일부가 삭제되었을때 삭제된 id값부터 채워넣는 방식으로 구현하는 방법은 없을까요? 처음부터 탐색해나가거나 삭제될때 인덱싱을 하도록 하는 방법을 생각해보고는 있는데 다른 방법이 있는지 궁금합니다.
1 답변
-
첫번째 질문에 대한 답변
우선 위의 코드에 대한 설명을 하면, Cursor는 일종의 Iterator 입니다.
문제는 해당 컬렉션에 (가)값이 전혀 없는 경우와 (나)하나의 이상의 값이 있는 경우로 나눠볼 수 있습니다.
일단 (나) 의 경우는 cursor에 절대적으로 컬렉션의 마지막 값 한개가 무조건 들어있습니다. 따라서 iterator인 cursor에서 next() 연산을 통해서 가져올 수 있습니다.
그러나 (가) 의 경우에는 값이 없으므로, next() 연산을 에러를 발생 시킵니다.
iterator에서 next()할 수 있는 값이 있는지 없는 지 알 수 있는 방법이 hasNext() 가 되겠죠?
따라서, 위의
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
구문은 마지막 id 값을 얻는 경우(hasNext가 true)인 경우와 최초에 컬렉션에 등록하는 경우(hasNext가 false) 인 경우로 나눠서 최초인 경우는 id를 1로 만듭니다.
보통의 트랜잭션을 지원하는 DB의 경우는 트랜잭션 안에서 한 컬럼의 값을 증가하는 방식을 사용합니다. (똑같은 ID가 사용될 가능성을 배제하기 위해서).
Seq NextKey User 1 새로운 User가 등록되면 트랜잭션 안에서 Seq가 User인 NextKey를 읽어와 사용하고, 이에 1 증가한 값을 다시 저장하는 방식을 사용합니다.
Seq NextKey User 2 .
두번째 질문에 대한 답변
삭제된 id 값을 찾으려면, 전체 문서를 다 찾아봐야되는데, 그렇게 되면 Insertion 비용에 검색 비용이 들어갑니다. 전체적으로 유지되는 데이터셋(컬렉션)의 크기가 일정한 규모로 유지되는 것이 확실하다면 시도해봄직 합니다만, 점점 커질 가능성이 높은 경우라면, 검색 비용이 계속 증가하게 될텐데, 이는 곧 새로운 데이터를 추가하는 비용(시간)도 점점 커지게됨으로 상당히 비효율적이게 됩니다.
차선으로 삭제될 때, 삭제된 id 풀을 만들어 재활용을 고민해볼 수도 있겠습니다.
다만 삭제된 id를 재활용해야 할 정도로 데이터가 매우 많이 발생하는 것인지 혹은 데이터가 빈번히 삭제되는 시스템이어서 id의 소모가 빠른 경우인지 등, id가 증가하여 앞으로 사용할 수 있는(가용) id의 부족 상황이 도래하는지 고민할 필요가 있어보입니다.
댓글 입력