你在用 db.collection.find() 的時候,它返回的不是所有的數據,而實際上是一個“cursor”。它的默認行為是:第一次向數據庫查詢 101 個文檔,或 1 MB 的文檔,取決于哪個條件先滿足;之后每次 cursor 中的文檔用盡后,查詢 4 MB 的文檔。另外,find() 的默認行為是返回一個 10 分鐘無操作后超時的 cursor。如果我一個 batch 的文檔十分鐘內沒處理完,過后再處理完了,再用同一個 cursor id 向服務器取下一個 batch,這時候 cursor id 當然已經過期了,這也就能解釋為啥我得到 cursor id 無效的錯誤了。
Stack Overflow 上有人提出過解決方法,是在 find() 時傳入 timeout=False 來禁用 10 分鐘超時的保護措施。但是我覺得這是非常差的辦法,因為如果你循環時產生異常,甚至斷電或斷網,都會導致 MongoDB 服務器資源永遠無法被釋放。而更好的辦法是(我也發在了 Stack Overflow 上),估計一個 batch 大小,讓 MongoDB 客戶端每次抓取的文檔在 10 分鐘內能用完,這樣客戶端就不得不 10 分鐘內至少聯系服務器一次,保證 cursor 不超時。
具體用法:
for document in db.collection.find().batch_size(30):