2016-04-21 56 views
6

我們對最終發生的cursor not found exceptions感到困擾,對於一些Morphia查詢asList,我發現hint on SO,這可能是相當消耗記憶。什麼是MongoDB中的光標?

現在我想更多地瞭解一下背景:可以用sombody解釋(用英文),Cursor(在MongoDB中)實際上是什麼?爲什麼它可以保持開放或找不到?


文檔defines一個光標爲:

的指針到結果集的查詢的。客戶端可以迭代遊標來檢索結果。默認情況下,遊標在閒置10分鐘後超時

但這不是很明顯。也許這可能是有幫助的定義查詢結果的batch,因爲docu also states

MongoDB服務器返回批量查詢結果。批量大小不會超過最大BSON文檔大小。對於大多數查詢,第一批返回101個文檔或只是足夠的文檔超過1兆字節。後續的批量大小是4兆字節。 [...]對於包含沒有索引的排序操作的查詢,服務器必須在返回任何結果之前加載內存中的所有文檔以執行排序。

注意:在我們的查詢中,我們根本不使用排序語句,也沒有limitoffset

+0

我覺得你的問題是在你應該問的話題上徘徊。如果你確實收到*「光標找不到」*異常,那麼故障問題將是你實施的代碼。關於遊標「實際是什麼」的更廣泛的討論更多的是「更廣泛的設計問題」,而不是解決特定編程問題的東西,比如本網站的用途,因此*「對遊標過度咆哮,代碼不夠這可能會導致問題「*。就目前而言,你的問題看起來像是在要求一篇論文解釋遊標是什麼。只需顯示一些代碼。 –

+0

問題是,這些例外的發生取決於環境。我們目前不知道,哪個參數(內存,CPU,什麼)是至關重要的參數。所以我對一些**背景感興趣**。我們的代碼看起來像'ds.find(Translation.class).asList()'(ds是Morphia.Datastrore)。 – BairDev

+2

如果您在副本集中運行mongo,則如果您的服務器決定在另一個主節點上,則光標將會丟失。 – froderik

回答

2

我並不是指mongodb專家,但我只是想補充一些在去年工作在中型mongo系統中的觀察結果。還要感謝@xameeramir提供了關於遊標如何工作的出色步驟。

「光標丟失」異常的原因可能有幾種。這個答案解釋了我注意到的一點。

光標位於服務器端。它不是分佈在副本集上,而是存在於創建時主要的實例上。這意味着如果另一個實例接管爲主,光標將丟失給客戶端。如果舊的小學仍然在上面,它可能仍然在那裏,但沒有用。我想這是垃圾收集了一段時間後。因此,如果您的mongo副本集不穩定,或者您的前面有不穩定的網絡,那麼在執行任何長時間運行的查詢時,您都會失去運氣。

如果光標想要返回的內容的全部內容不適合服務器上的內存,查詢可能會非常緩慢。您服務器上的RAM需要大於您運行的最大查詢。

所有這一切都可以通過設計得更好來部分避免。對於大型長時間運行查詢的用例,您可能更適合使用幾個較小的數據庫集合,而不是較大的數據庫集合。

6

find()方法和Node.js的驅動程序遊標

 

var MongoClient = require('mongodb').MongoClient, 
assert = require('assert'); 

MongoClient.connect('mongodb://localhost:27017/crunchbase', function(err, db) { 

    assert.equal(err, null); 
    console.log("Successfully connected to MongoDB."); 

    var query = { 
    "category_code": "biotech" 
    }; 

    db.collection('companies').find(query).toArray(function(err, docs) { 

    assert.equal(err, null); 
    assert.notEqual(docs.length, 0); 

    docs.forEach(function(doc) { 
     console.log(doc.name + " is a " + doc.category_code + " company."); 
     }); 

     db.close(); 

     }); 

     }); 
     
    

注意,通話.toArray正在爲獲取整個數據集的應用程序。

 

var MongoClient = require('mongodb').MongoClient, 
    assert = require('assert'); 


MongoClient.connect('mongodb://localhost:27017/crunchbase', function(err, db) { 

    assert.equal(err, null); 
    console.log("Successfully connected to MongoDB."); 

    var query = {"category_code": "biotech"}; 

    var cursor = db.collection('companies').find(query); 

     function(doc) { 
      cursor.forEach(
      console.log(doc.name + " is a " + doc.category_code + " company."); 
     }, 
     function(err) { 
      assert.equal(err, null); 
      return db.close(); 
     } 
    ); 

}); 
 

注意,光標find()返回分配給var cursor。採用這種方法,我們不是一次性獲取memort中的所有數據並使用數據,而是將數據傳輸到我們的應用程序。 find()可以立即創建遊標,因爲它實際上並沒有向數據庫發出請求,除非我們嘗試使用它將提供的某些文檔。 cursor的要點是描述我們的查詢。 cursor.forEach的第二個參數顯示驅動程序耗盡或發生錯誤時要執行的操作。

在上述代碼的初始版本中,它是toArray(),它強制數據庫調用。這意味着我們需要所有的文件,並希望他們在array

另外,MongoDB以批處理格式返回數據。下面顯示的圖像,從遊標請求(從應用程序)MongoDB

MongoDB cursor requests

forEachtoArray更好,因爲我們可以處理文檔,因爲他們進來,直到我們到達終點。將其與toArray對比 - 我們在那裏等待全部要檢索的文件和整個數組已建成。這意味着我們沒有從驅動程序和數據庫系統一起工作將結果批量分發到應用程序的事實中獲得任何優勢。批處理意味着提供內存開銷和執行時間方面的效率。 充分利用它,如果你可以在你的應用

+0

+1這正是我想知道的。我一直認爲find()正在執行查詢,並且.limit和.toArray正在對該查詢的結果進行操作。很好的解釋! – coblr