相对于删除操作,更新操作复杂得多,因为其操作很多,mongodb提供了很多更新的操作符,另外还要考虑到更
新时如果原来的数据doc空间不够还得删除原来的doc再添加新的doc,相当于做了两次操作,这里的过程同样会影
响collection中所有的索引.下面来看代码吧,更新操作的入口为:
void receivedUpdate(Message& m, CurOp& op) {
DbMessage d(m);
const char *ns = d.getns();
op.debug().ns = ns;
int flags = d.pullInt();
BSONObj query = d.nextJsObj();
BSONObj toupdate = d.nextJsObj();
bool upsert = flags & UpdateOption_Upsert;
bool multi = flags & UpdateOption_Multi;
bool broadcast = flags & UpdateOption_Broadcast;
op.debug().query = query;
op.setQuery(query);
PageFaultRetryableSection s;
while ( 1 ) {
try {
Lock::DBWrite lk(ns);
// void ReplSetImpl::relinquish() uses big write lock so
// this is thus synchronized given our lock above.
uassert( 10054 , "not master", isMasterNs( ns ) );//不是master不能执行更新
// 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 );//实际的更新部分
UpdateResult res = updateObjects(ns, toupdate, query, upsert, multi, true, op.debug() );
lastError.getSafe()->recordUpdate( res.existing , res.num , res.upserted ); // for getlasterror
break;
}
catch ( PageFaultException& e ) {//要更新的数据不在内存,让操作系统加载其到内存
e.touch();
}
}
}
updateObjects做了基本的检查后直接调用了_updateObjects,所以跳过updateObjects函数
直接分析_updateObjects. receiveUpdate->updateObjects->_updateObjects.
UpdateResult _updateObjects( bool su,const char* ns,const BSONObj& updateobj,const BSONObj& patternOrig,bool upsert,
bool multi,bool logop ,OpDebug& debug,RemoveSaver* rs,bool fromMigrate,const QueryPlanSelectionPolicy& planPolicy ) {
Client& client = cc();
int profile = client.database()->profile;
debug.updateobj = updateobj;
// The idea with these here it to make them loop invariant for
// multi updates, and thus be a bit faster for that case. The
// pointers may be left invalid on a failed or terminal yield
// recovery.
NamespaceDetails* d = nsdetails(ns); // can be null if an upsert...
NamespaceDetailsTransient* nsdt = &NamespaceDetailsTransient::get(ns);
auto_ptr<ModSet> mods;
bool isOperatorUpdate = updateobj.firstElementFieldName()[0] == '$';
int modsIsIndexed = false; // really the # of indexes
if ( isOperatorUpdate ) {//根据更新操作提取具体的更新符以及数据建立更新操作结构,比如update={$set:{a:1},$inc{b:1}}
if( d && d->indexBuildInProgress ) {//那么建立两个mod,第一个操作符为$set,操作对象为{a:1},第二个操作符为$inc,
set<string> bgKeys; //操作对象为{b:1}
d->inProgIdx().keyPattern().getFieldNames(bgKeys);
mods.reset( new ModSet(updateobj, nsdt->indexKeys(), &bgKeys) );
}
else {
mods.reset( new ModSet(updateobj, nsdt->indexKeys()) );
}
modsIsIndexed = mods->isIndexed();//修改可能变更到索引,后面则需要同时更新索引才行
}
//单id查询的更新比如说pattern={_id:1},updateobj={$inc:{x:1}},并且这里x不为索引
if( planPolicy.permitOptimalIdPlan() && !multi && isSimpleIdQuery(patternOrig) && d &&
!modsIsIndexed ) {
int idxNo = d->findIdIndex();
if( idxNo >= 0 ) {
debug.idhack = true;//updateById函数和后面的操作流程差不多,这里就不分析了,看后面的分析
UpdateResult result = _updateById( isOperatorUpdate,idxNo,mods.get(),profile,d,nsdt,su,ns,
updateobj,patternOrig,logop,debug,fromMigrate);
if ( result.existing || ! upsert ) {
return result;
}
else if ( upsert && ! isOperatorUpdate && ! logop) {