mongodb源码分析(十二)数据的更新

        相对于删除操作,更新操作复杂得多,因为其操作很多,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) {
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值