MongoDB中ObjectId的误区,以及引起的一系列问题
近期对两个应用进行改造,在上线过程中出现一系列问题(其中一部分是由于ObjectId误区导致的)
先来了解下ObjectId:
TimeStamp
前 4位是一个unix的时间戳,是一个int类别,我们将上面的例子中的objectid的前4位进行提取“4df2dcec”,然后再将他们安装十六进制 专为十进制:“1307761900”,这个数字就是一个时间戳,为了让效果更佳明显,我们将这个时间戳转换成我们习惯的时间格式(精确到秒)2011年 06月 11日 星期六 03:11:40 UTC
Machine
接下来的三个字节,就是 2cdcd2 ,这三个字节是所在主机的唯一标识符,一般是机器主机名的散列值,这样就确保了不同主机生成不同的机器hash值,确保在分布式中不造成冲突,这也就是在同一台机器生成的objectid中间的字符串都是一模一样的原因。pid
上面的Machine是为了确保在不同机器产生的objectid不冲突,而pid就是为了在同一台机器不同的mongodb进程产生了objectid不冲突,接下来的0936两位就是产生objectid的进程标识符。increment
前面的九个字节是保证了一秒内不同机器不同进程生成objectid不冲突,这后面的三个字节a8b817,是一个自动增加的计数器,用来确保在同一秒内产生的objectid也不会发现冲突,允许256的3次方等于16777216条记录的唯一性。ObjectId唯一性
误区 一 、文档顺序和插入顺序一致?
单线程情况
多线程、多机器或多进程情况
解决办法:
Query query = new Query(); if (id != null) { query.addCriteria(Criteria.where("_id").gt(id)); } query.with(new Sort(Sort.Direction.ASC, "_id"));
Comparator<DBObject> comparator = new Comparator<DBObject>() { @Override public int compare(DBObject o1, DBObject o2) { return ((ObjectId)o1.get("_id")).compareTo((ObjectId)o2.get("_id")); } }; PriorityQueue<DBObject> queue = new PriorityQueue<DBObject>(200,comparator);
误区 二 、多客户端高并发时,是否可以保证顺序(sort之后)?
4df2dcec aaaa eeee 36a8b813
4df2dcec bbbb 1111 36a8b814
4df2dcec aaaa ffff 36a8b814
4df2dcec aaaa eeee 36a8b814
解决办法:
误区 三 、不在DBObject设置_id使用mongoDB设置ObjectId?
mongoDB插入操作时,new DBBasicObject()时,大家看到_id是没有被填值的,除非手工的设置_id。那么是否是服务端设置的呢?public WriteResult insert(List<DBObject> list, com.mongodb.WriteConcern concern, DBEncoder encoder ){ if (concern == null) { throw new IllegalArgumentException("Write concern can not be null"); } return insert(list, true, concern, encoder); }
可以看到需要添加,默认都为添加
protected WriteResult insert(List<DBObject> list, boolean shouldApply , com.mongodb.WriteConcern concern, DBEncoder encoder ){ if (encoder == null) encoder = DefaultDBEncoder.FACTORY.create(); if ( willTrace() ) { for (DBObject o : list) { trace( "save: " + _fullNameSpace + " " + JSON.serialize( o ) ); } } if ( shouldApply ){ for (DBObject o : list) { apply(o); _checkObject(o, false, false); Object id = o.get("_id"); if (id instanceof ObjectId) { ((ObjectId) id).notNew(); } } } WriteResult last = null; int cur = 0; int maxsize = _mongo.getMaxBsonObjectSize(); while ( cur < list.size() ) { OutMessage om = OutMessage.insert( this , encoder, concern ); for ( ; cur < list.size(); cur++ ){ DBObject o = list.get(cur); om.putObject( o ); // limit for batch insert is 4 x maxbson on server, use 2 x to be safe if ( om.size() > 2 * maxsize ){ cur++; break; } } last = _connector.say( _db , om , concern ); } return last; }自动添加ObjectId的操作
/** * calls {@link DBCollection#apply(com.mongodb.DBObject, boolean)} with ensureID=true * @param o <code>DBObject</code> to which to add fields * @return the modified parameter object */ public Object apply( DBObject o ){ return apply( o , true ); } /** * calls {@link DBCollection#doapply(com.mongodb.DBObject)}, optionally adding an automatic _id field * @param jo object to add fields to * @param ensureID whether to add an <code>_id</code> field * @return the modified object <code>o</code> */ public Object apply( DBObject jo , boolean ensureID ){ Object id = jo.get( "_id" ); if ( ensureID && id == null ){ id = ObjectId.get(); jo.put( "_id" , id ); } doapply( jo ); return id; }可以看到,mongoDB的驱动包中是会自动添加ObjectId的。
public WriteResult save( DBObject jo, WriteConcern concern ){ if ( checkReadOnly( true ) ) return null; _checkObject( jo , false , false ); Object id = jo.get( "_id" ); if ( id == null || ( id instanceof ObjectId && ((ObjectId)id).isNew() ) ){ if ( id != null && id instanceof ObjectId ) ((ObjectId)id).notNew(); if ( concern == null ) return insert( jo ); else return insert( jo, concern ); } DBObject q = new BasicDBObject(); q.put( "_id" , id ); if ( concern == null ) return update( q , jo , true , false ); else return update( q , jo , true , false , concern ); }
误区 四 、findAndModify是否真的可以获取到自增变量?
DBObject update = new BasicDBObject("$inc", new BasicDBObject("counter", 1)); DBObject query = new BasicDBObject("_id", key); DBObject result = getMongoTemplate().getCollection(collectionName).findAndModify(query, update); if (result == null) { DBObject doc = new BasicDBObject(); doc.put("counter", 1L); doc.put("_id", key); // insert(collectionName, doc); getMongoTemplate().save(doc, collectionName); return 1L; } return (Long) result.get("counter");
获取自增变量会使用这种方法编写,但是,我们执行完成后会发现。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。