欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MongoDB中ObjectId的誤區(qū)及引起的一系列問題

 更新時間:2016年12月25日 16:59:51   作者:大齊NJ  
這篇文章主要介紹了MongoDB中ObjectId的誤區(qū)及引起的一系列問題,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

近期對兩個應用進行改造,在上線過程中出現一系列問題(其中一部分是由于ObjectId誤區(qū)導致的)

先來了解下ObjectId:

TimeStamp

前 4位是一個unix的時間戳,是一個int類別,我們將上面的例子中的objectid的前4位進行提取“4df2dcec”,然后再將他們安裝十六進制 專為十進制:“1307761900”,這個數字就是一個時間戳,為了讓效果更佳明顯,我們將這個時間戳轉換成我們習慣的時間格式(精確到秒)

$ date -d '1970-01-01 UTC 1307761900 sec' -u

2011年 06月 11日 星期六 03:11:40 UTC

前 4個字節(jié)其實隱藏了文檔創(chuàng)建的時間,并且時間戳處在于字符的最前面,這就意味著ObjectId大致會按照插入進行排序,這對于某些方面起到很大作用,如 作為索引提高搜索效率等等。使用時間戳還有一個好處是,某些客戶端驅動可以通過ObjectId解析出該記錄是何時插入的,這也解答了我們平時快速連續(xù)創(chuàng) 建多個Objectid時,會發(fā)現前幾位數字很少發(fā)現變化的現實,因為使用的是當前時間,很多用戶擔心要對服務器進行時間同步,其實這個時間戳的真實值并 不重要,只要其總不停增加就好。

Machine

接下來的三個字節(jié),就是 2cdcd2 ,這三個字節(jié)是所在主機的唯一標識符,一般是機器主機名的散列值,這樣就確保了不同主機生成不同的機器hash值,確保在分布式中不造成沖突,這也就是在同一臺機器生成的objectid中間的字符串都是一模一樣的原因。

pid

上面的Machine是為了確保在不同機器產生的objectid不沖突,而pid就是為了在同一臺機器不同的mongodb進程產生了objectid不沖突,接下來的0936兩位就是產生objectid的進程標識符。

increment

前面的九個字節(jié)是保證了一秒內不同機器不同進程生成objectid不沖突,這后面的三個字節(jié)a8b817,是一個自動增加的計數器,用來確保在同一秒內產生的objectid也不會發(fā)現沖突,允許256的3次方等于16777216條記錄的唯一性。

ObjectId唯一性

大家可能會覺得,在某種程度上已經可以保證唯一了,不管在客戶端還是在服務端。

誤區(qū) 一 、文檔順序和插入順序一致?

單線程情況

ObjectId中的timestamp、machine、pid、inc都可以保證唯一,因為在同一臺機器,同一個進程。

這里有一個問題,mongodb的操作時多線程的。a、b、c...幾個線程進行入庫操作時,不能保證哪一條可以在另外一條之前,所以會是亂序的。

多線程、多機器或多進程情況

再看下ObjectId中mache、pid不能保證唯一。那么則數據更加會是亂序的。

解決辦法:

由于collection集合中數據是無序的(包括capped collection),那么,最簡單的辦法是對ObjectId進行排序。

可以使用兩種方法排序,

1.mongoDB查詢語句

jQuery query = new Query(); 
if (id != null) 
{ 
  jquery.addCriteria(Criteria.where("_id").gt(id)); 
} 
jquery.with(new Sort(Sort.Direction.ASC, "_id")); 

2.java.util.PriorityQueue

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); 

誤區(qū) 二 、多客戶端高并發(fā)時,是否可以保證順序(sort之后)?

如果一直保證寫入遠遠大于讀出(間隔一秒以上),這樣是永遠不會出現亂序的情況。

我們來看下樣例

現在看到圖中,取出數據兩次

第一次

4df2dcec aaaa  ffff 36a8b813
4df2dcec aaaa  eeee 36a8b813
4df2dcec bbbb  1111 36a8b814

第二次

4df2dcec bbbb  1111 36a8b813
4df2dcec aaaa  ffff 36a8b814
4df2dcec aaaa  eeee 36a8b814

現在如果取第一次的最大值(4df2dcec bbbb  1111 36a8b814)做下次查詢的結果,那么就會漏掉

第二次的三條,因為(4df2dcec bbbb  1111 36a8b814)大于第二次取的所有記錄。

所以會導致丟數據的情況。

解決辦法:

由于ObjectId的時間戳截止到秒,而counter算子前四位又為機器與進程號。

1.處理一定時間間隔前的記錄(一秒以上),這樣即使機器和進程號導致亂序,間隔前也不會出現亂序情況。

2.單點插入,原來分布到幾個點的插入操作,現在統(tǒng)一由一個點查詢,保證機器與進程號相同,使用counter算子使記錄有序。

這里,我們用到了第一種辦法。

誤區(qū) 三 、不在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的。

save的方法

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 ); 
  } 

綜上所述,默認情況下ObjectId是由客戶端生成的,并不是不設置就由服務端生成的。

誤區(qū) 四 、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"); 

獲取自增變量會使用這種方法編寫,但是,我們執(zhí)行完成后會發(fā)現。

findAndModify操作,是先執(zhí)行了find,再執(zhí)行了modify,所以當result為null時,應該新增并返回0

以上所述是小編給大家介紹的MongoDB中ObjectId的誤區(qū)及引起的一系列問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!

相關文章

  • 解決java調用python代碼返回值中文亂碼問題

    解決java調用python代碼返回值中文亂碼問題

    這篇文章主要介紹了解決java調用python代碼返回值中文亂碼問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • JavaCV與FFmpeg音視頻流處理技巧總結大全

    JavaCV與FFmpeg音視頻流處理技巧總結大全

    JavaCV是一個開源的Java接口,它為幾個著名的計算機視覺庫(如OpenCV、FFmpeg)提供了Java封裝,這篇文章主要給大家介紹了關于JavaCV與FFmpeg音視頻流處理技巧總結的相關資料,需要的朋友可以參考下
    2024-05-05
  • java開發(fā)https請求ssl不受信任問題解決方法

    java開發(fā)https請求ssl不受信任問題解決方法

    這篇文章主要介紹了java開發(fā)https請求ssl不受信任問題解決方法,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • 淺談讓@Value更方便的Spring自定義轉換類

    淺談讓@Value更方便的Spring自定義轉換類

    Spring為大家內置了不少開箱即用的轉換類,如字符串轉數字、字符串轉時間等,但有時候需要使用自定義的屬性,則需要自定義轉換類了
    2021-06-06
  • Windows10安裝IDEA 2020.1.2的方法步驟

    Windows10安裝IDEA 2020.1.2的方法步驟

    這篇文章主要介紹了Windows10安裝IDEA 2020.1.2的方法步驟,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • Java四種線程池的使用詳解

    Java四種線程池的使用詳解

    本篇文章主要介紹了Java四種線程池的使用詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • 深入剖析Java工廠模式讓你的代碼更靈活

    深入剖析Java工廠模式讓你的代碼更靈活

    Java工廠模式是一種創(chuàng)建對象的設計模式,它可以在不暴露對象創(chuàng)建邏輯的情況下,將對象的創(chuàng)建委托給子類或者其他對象。本文就來深入剖析一下Java工廠模式是如何讓你的代碼更靈活、可擴展、易維護的
    2023-05-05
  • Java使用jmeter進行壓力測試

    Java使用jmeter進行壓力測試

    本篇文章簡單講一下使用jmeter進行壓力測試。其壓測思想就是 通過創(chuàng)建指定數量的線程,同時請求指定接口,來模擬指定數量用戶同時進行某個操作的場景,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Spring?Cloud中Sentinel的兩種限流模式介紹

    Spring?Cloud中Sentinel的兩種限流模式介紹

    如何使用Sentinel做流量控制呢?這篇文章就來為大家詳細介紹了Spring?Cloud中Sentinel的兩種限流模式,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-05-05
  • Spring Boot的properties配置文件讀取

    Spring Boot的properties配置文件讀取

    這篇文章主要介紹了Spring Boot的properties配置文件讀取,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06

最新評論