在之前的一篇文章 中,介绍了assembleResponse函数(位于instance.cpp第224行),它会根据op操作枚举类型来调用相应的crud操作,枚举类型定义如下:
enum
Operations {
opReply = 1 , /* reply. responseTo is set. */
dbMsg = 1000 , /* generic msg command followed by a string */
dbUpdate = 2001 , /* update object */
dbInsert = 2002 ,
// dbGetByOID = 2003,
dbQuery = 2004 ,
dbGetMore = 2005 ,
dbDelete = 2006 ,
dbKillCursors = 2007
};
opReply = 1 , /* reply. responseTo is set. */
dbMsg = 1000 , /* generic msg command followed by a string */
dbUpdate = 2001 , /* update object */
dbInsert = 2002 ,
// dbGetByOID = 2003,
dbQuery = 2004 ,
dbGetMore = 2005 ,
dbDelete = 2006 ,
dbKillCursors = 2007
};
可以看到dbDelete = 2002 为删除操作枚举值。当客户端将要删除的记录(或条件的document)发到服务端之后,mongodb通过消息封装方式将数据包中的字节流解析转成 message类型,并进一步转换成dbmessage之后,mongodb就会根据消息类型进行判断,以决定接下来执行的操作),下面我们看一下 assembleResponse在确定是删除操作时调用的方法,如下:
assembleResponse( Message
&
m, DbResponse
&
dbresponse,
const
SockAddr
&
client ) {
.....
try {
if ( op == dbInsert ) { // 添加记录操作
receivedInsert(m, currentOp);
}
else if ( op == dbUpdate ) { // 更新记录
receivedUpdate(m, currentOp);
}
else if ( op == dbDelete ) { // 删除记录
receivedDelete(m, currentOp);
}
else if ( op == dbKillCursors ) { // 删除Cursors(游标)对象
currentOp.ensureStarted();
logThreshold = 10 ;
ss << " killcursors " ;
receivedKillCursors(m);
}
else {
mongo::log() << " operation isn't supported: " << op << endl;
currentOp.done();
log = true ;
}
}
.....
}
}
.....
try {
if ( op == dbInsert ) { // 添加记录操作
receivedInsert(m, currentOp);
}
else if ( op == dbUpdate ) { // 更新记录
receivedUpdate(m, currentOp);
}
else if ( op == dbDelete ) { // 删除记录
receivedDelete(m, currentOp);
}
else if ( op == dbKillCursors ) { // 删除Cursors(游标)对象
currentOp.ensureStarted();
logThreshold = 10 ;
ss << " killcursors " ;
receivedKillCursors(m);
}
else {
mongo::log() << " operation isn't supported: " << op << endl;
currentOp.done();
log = true ;
}
}
.....
}
}
从上面代码可以看出,系统在确定dbDelete操作时,调用了receivedDelete()方法(位于instance.cpp文件第323行),下面是该方法的定义:
void
receivedDelete(Message
&
m, CurOp
&
op) {
DbMessage d(m); // 将Message消息转换成数据库消息格式
const char * ns = d.getns(); // 获取相应名空间信息
assert( * ns);
uassert( 10056 , " not master " , isMasterNs( ns ) ); // 因为CUD操作在主库中操作,所以这里断言名空间包含的db信息中是不是主库,即"master"
op.debug().str <<
ns << ' ' ;
// 获取"删除消息"结构体中的flags 标识位,如设置了该位,则仅删除查找到的第一条记录(document),否则删除所有匹配记录.
// 关于消息结构体,参见我的这篇文章: http://www.cnblogs.com/daizhj/archive/2011/04/02/2003335.html
int flags = d.pullInt(); //
bool justOne = flags & RemoveOption_JustOne;
bool broadcast = flags & RemoveOption_Broadcast;
assert( d.moreJSObjs() );
BSONObj pattern = d.nextJsObj(); // 获取"删除消息"结构体中的selector(也就是要删数据条件where)
{
string s = pattern.toString();
op.debug().str << " query: " << s;
op.setQuery(pattern);
}
writelock lk(ns);
// 如果不更新所有节点(sharding)且当前物理结点是shard 状态时
if ( ! broadcast & handlePossibleShardedMessage( m , 0 ) )
return ;
// if this ever moves to outside of lock, need to adjust check Client::Context::_finishInit
Client::Context ctx(ns);
long long n = deleteObjects(ns, pattern, justOne, true ); // 删除对象信息
lastError.getSafe() -> recordDelete( n );
}
DbMessage d(m); // 将Message消息转换成数据库消息格式
const char * ns = d.getns(); // 获取相应名空间信息
assert( * ns);
uassert( 10056 , " not master " , isMasterNs( ns ) ); // 因为CUD操作在主库中操作,所以这里断言名空间包含的db信息中是不是主库,即"master"
op.debug().str <<
ns << ' ' ;
// 获取"删除消息"结构体中的flags 标识位,如设置了该位,则仅删除查找到的第一条记录(document),否则删除所有匹配记录.
// 关于消息结构体,参见我的这篇文章: http://www.cnblogs.com/daizhj/archive/2011/04/02/2003335.html
int flags = d.pullInt(); //
bool justOne = flags & RemoveOption_JustOne;
bool broadcast = flags & RemoveOption_Broadcast;
assert( d.moreJSObjs() );
BSONObj pattern = d.nextJsObj(); // 获取"删除消息"结构体中的selector(也就是要删数据条件where)
{
string s = pattern.toString();
op.debug().str << " query: " << s;
op.setQuery(pattern);
}
writelock lk(ns);
// 如果不更新所有节点(sharding)且当前物理结点是shard 状态时
if ( ! broadcast & handlePossibleShardedMessage( m , 0 ) )
return ;
// if this ever moves to outside of lock, need to adjust check Client::Context::_finishInit
Client::Context ctx(ns);
long long n = deleteObjects(ns, pattern, justOne, true ); // 删除对象信息
lastError.getSafe() -> recordDelete( n );
}
上面方法主要是对消息中的flag信息进行解析,以获取消息中的删除条件等信息,并最终调用 deleteObjects方法,该方法位于query.cpp文件中,如下:
//
query.cpp文件 128行
/* ns: 要删除的表集合(namespace, e.g. <database>.<collection>)
pattern: 删除条件,相当于 "where" 字语(clause / criteria)
justOne: 是否仅删除第一个匹配对象信息
god: 是否允许访问系统名空间(system namespaces)
*/
long long deleteObjects( const char * ns, BSONObj pattern, bool justOneOrig, bool logop, bool god, RemoveSaver * rs ) {
if ( ! god ) { // 如果不能访问system空间,但却删除该空间信息时
if ( strstr(ns, " .system. " ) ) {
/* note a delete from system.indexes would corrupt the db. if done here, as there are pointers into those objects in NamespaceDetails.
*/
uassert( 12050 , " cannot delete from system namespace
/* ns: 要删除的表集合(namespace, e.g. <database>.<collection>)
pattern: 删除条件,相当于 "where" 字语(clause / criteria)
justOne: 是否仅删除第一个匹配对象信息
god: 是否允许访问系统名空间(system namespaces)
*/
long long deleteObjects( const char * ns, BSONObj pattern, bool justOneOrig, bool logop, bool god, RemoveSaver * rs ) {
if ( ! god ) { // 如果不能访问system空间,但却删除该空间信息时
if ( strstr(ns, " .system. " ) ) {
/* note a delete from system.indexes would corrupt the db. if done here, as there are pointers into those objects in NamespaceDetails.
*/
uassert( 12050 , " cannot delete from system namespace