本文我们将删除,删除操作概括起来就是遍历将collection中数据对应的索引删除,然后是删除数据,最后将删除
的空间加入到之前文章描述的deletelist中.下面我们来看具体的代码吧.删除的入口是receiveDelete函数.
void receivedDelete(Message& m, CurOp& op) {
DbMessage d(m);
const char *ns = d.getns();
op.debug().ns = ns;
int flags = d.pullInt();
bool justOne = flags & RemoveOption_JustOne;//值删除单条doc
bool broadcast = flags & RemoveOption_Broadcast;
verify( d.moreJSObjs() );
BSONObj pattern = d.nextJsObj();
op.debug().query = pattern;
op.setQuery(pattern);
PageFaultRetryableSection s;
while ( 1 ) {
try {
Lock::DBWrite lk(ns);
// writelock is used to synchronize stepdowns w/ writes
uassert( 10056 , "not master", isMasterNs( ns ) );
// if this ever moves to outside of lock, need to adjust check Client::Context::_finishInit
if ( ! broadcast && handlePossibleShardedMessage( m , 0 ) )
return;
Client::Context ctx(ns);//具体的删除函数
long long n = deleteObjects(ns, pattern, justOne, true);
lastError.getSafe()->recordDelete( n );
break;
}
catch ( PageFaultException& e ) {
LOG(2) << "recordDelete got a PageFaultException" << endl;
e.touch();
}
}
}
receiveDelete->deleteObjects
long long deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool logop, bool god, RemoveSaver * rs ) {
long long nDeleted = 0;//根据查询条件得到游标
shared_ptr< Cursor > creal = NamespaceDetailsTransient::getCursor( ns, pattern );
if( !creal->ok() )
return nDeleted;
shared_ptr< Cursor > cPtr = creal;
auto_ptr<ClientCursor> cc( new ClientCursor( QueryOption_NoCursorTimeout, cPtr, ns) );
cc->setDoingDeletes( true );
CursorId id = cc->cursorid();
bool canYield = !god && !(creal->matcher() && creal->matcher()->docMatcher().atomic());
do {
// TODO: we can generalize this I believe
//
bool willNeedRecord = (creal->matcher() && creal->matcher()->needRecord()) || pattern.isEmpty() || isSimpleIdQuery( pattern );
if ( ! willNeedRecord ) {
// TODO: this is a total hack right now
// check if the index full encompasses query
if ( pattern.nFields() == 1 &&
str::equals( pattern.firstElement().fieldName() , creal->indexKeyPattern().firstElement().fieldName() ) )
willNeedRecord = true;
}
if ( canYield && ! cc->yieldSometimes( willNeedRecord ? ClientCursor::WillNeed : ClientCursor::MaybeCovered ) ) {
cc.release(); // has already been deleted elsewhere
// TODO should we assert or something?
break;
}
if ( !cc->ok() ) {
break; // if we yielded, could have hit the end
}
// this way we can avoid calling prepareToYield() every time (expensive)
// as well as some other nuances handled
cc->setDoingDeletes( true );
DiskLoc rloc = cc->currLoc();
BSONObj key = cc->currKey();
bool match = creal->currentMatches();
cc->advance();
if ( ! match )
continue;
// SERVER-5198 Advance past the document to be modified, but see SERVER-5725.
while( cc->ok() && rloc == cc->currLoc() ) {//多值索引就会出现这种状况,需要跳过同一条记录
cc->advance();
}
bool foundAllResults = ( justOne || !cc->ok() );
if ( !foundAllResults ) {
// NOTE: Saving and restoring a btree cursor's position was historically described
// as slow here.
cc->c()->prepareToTouchEarlierIterate();
}
if ( logop ) {//记录删除动作
BSONElement e;
if( BSONObj::make( rloc.rec() ).getObjectID( e ) ) {
BSONObjBuilder b;
b.append( e );
bool replJustOne = true;
logOp( "d", ns, b.done(), 0, &replJustOne );
}
else {
problem() << "deleted object without id, not logging" << endl;
}
}
if ( rs )//将要删除的doc记录下来
rs->goingToDelete( rloc.obj() /*cc->c->current()*/ );
theDataFileMgr.deleteRecord(ns, rloc.rec(), rloc);//记录的删除
nDeleted++;
if ( foundAllResults ) {
break;
}
cc->c()->recoverFromTouchingEarlierIterate();
if( !god )
getDur().commitIfNeeded();
if( debug && god && nDeleted == 100 )
log() << "warning high number of deletes with god=true which could use significant memory" << endl;
}
while ( cc->ok() );
if ( cc.get() && ClientCursor::find( id , false ) == 0 ) {
// TODO: remove this and the id declaration above if this doesn't trigger
// if it does, then i'm very confused (ERH 06/2011)
error() << "this should be impossible" << endl;
printStackTrace();
cc.release();
}
return nDeleted;
}
receiveDelete->deleteObjects->deleteRecord
void DataFileMgr::deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK, bool noWarn, bool doLog ) {
NamespaceDetails* d = nsdetails(ns);
if ( d->isCapped() && !cappedOK ) {
out() << "failing remove on a capped ns " << ns << endl;
uassert( 10089 , "can't remove from a capped collection" , 0 );
return;
}
BSONObj toDelete;
if ( doLog ) {
BSONElement e = dl.obj()["_id"];
if ( e.type() ) {
toDelete = e.wrap();
}
}
/* check if any cursors point to us. if so, advance them. */
ClientCursor::aboutToDelete(dl);
unindexRecord(d, todelete, dl, noWarn);//删除索引,循环删除所有索引中记录todelete的部分.
_deleteRecord(d, ns, todelete, dl);//实际的数据删除并将其空间添加到deletedList中
NamespaceDetailsTransient::get( ns ).notifyOfWriteOp();
if ( ! toDelete.isEmpty() ) {
logOp( "d" , ns , toDelete );
}
}
到这里删除过程结束,可见这部分流程是很简单的.
原文链接: mongodb源码分析(十一)数据的删除
作者: yhjj0108,杨浩