麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 數(shù)據(jù)庫 > MongoDB > 正文

MongoDB游標(biāo)超時問題的4種解決方法

2020-10-29 18:41:12
字體:
供稿:網(wǎng)友

當(dāng)我們使用Python從MongoDB里面讀取數(shù)據(jù)時,可能會這樣寫代碼:

import pymongohandler = pymongo.MongoClient().db.colfor row in handler.find(): parse_data(row)

短短4行代碼,讀取MongoDB里面的每一行數(shù)據(jù),然后傳入parse_data做處理。處理完成以后再讀取下一行。邏輯清晰而簡單,能有什么問題?只要parse_data(row)不報錯,這一段代碼就完美無缺。

但事實并非這樣。

你的代碼可能會在for row in handler.find()這一行報錯。它的原因,說來話長。

要解釋這個問題,我們首先就需要知道,handler.find()返回的并不是數(shù)據(jù)庫里面的數(shù)據(jù),而是一個游標(biāo)(cursor)對象。如下圖所示:

只有當(dāng)你使用for循環(huán)開始迭代它的時候,游標(biāo)才會真正去數(shù)據(jù)庫里面讀取數(shù)據(jù)。

但是,如果每一次循環(huán)都連接數(shù)據(jù)庫,那么網(wǎng)絡(luò)連接會浪費大量時間。

所以pymongo會一次性獲取100行,for row in handler.find()循環(huán)第一次的時候,它會連上MongoDB,讀取一百條數(shù)據(jù),緩存到內(nèi)存中。于是第2-100次循環(huán),數(shù)據(jù)都是直接從內(nèi)存里面獲取,不會再連接數(shù)據(jù)庫。

當(dāng)循環(huán)進(jìn)行到底101次的時候,再一次連接數(shù)據(jù)庫,再讀取第101-200行內(nèi)容……

這個邏輯非常有效地降低了網(wǎng)絡(luò)I/O耗時。

但是,MongoDB默認(rèn)游標(biāo)的超時時間是10分鐘。10分鐘之內(nèi),必需再次連接MongoDB讀取內(nèi)容刷新游標(biāo)時間,否則,就會導(dǎo)

致游標(biāo)超時報錯:

pymongo.errors.CursorNotFound: cursor id 211526444773 not found

如下圖所示:

所以,回到最開始的代碼中來,如果parse_data每次執(zhí)行的時間超過6秒鐘,那么它執(zhí)行100次的時間就會超過10分鐘。此時,當(dāng)程序想讀取第101行數(shù)據(jù)的時候,程序就會報錯。

為了解決這個問題,我們有4種辦法:

  1. 修改MongoDB的配置,延長游標(biāo)超時時間,并重啟MongoDB。由于生產(chǎn)環(huán)境的MongoDB不能隨便重啟,所以這個方案雖然有用,但是排除。
  2. 一次性把數(shù)據(jù)全部讀取下來,再做處理:
all_data = [row for row in handler.find()]for row in all_data: parse(row)

這種方案的弊端也很明顯,如果數(shù)據(jù)量非常大,你不一定能全部放到內(nèi)存里面。即使能夠全部放到內(nèi)存中,但是列表推導(dǎo)式遍歷了所有數(shù)據(jù),緊接著for循環(huán)又遍歷一次,浪費時間。

  3.讓游標(biāo)每次返回的數(shù)據(jù)小于100條,這樣消費完這一批數(shù)據(jù)的時間就會小于10分鐘:

# 每次連接數(shù)據(jù)庫,只返回50行數(shù)據(jù)for row in handler.find().batch_size(50):  parse_data(row)

但這種方案會增加數(shù)據(jù)庫的連接次數(shù),從而增加I/O耗時。

  4.讓游標(biāo)永不超時。通過設(shè)定參數(shù)no_cursor_timeout=True,讓游標(biāo)永不超時:

cursor = handler.find(no_cursor_timeout=True)for row in cursor: parse_data(row)cursor.close() # 一定要手動關(guān)閉游標(biāo)

然而這個操作非常危險,因為如果你的Python程序因為某種原因意外停止了,這個游標(biāo)就再也無法關(guān)閉了!除非重啟MongoDB,否則這些游標(biāo)會一直留在MongoDB上,占用資源。

當(dāng)然可能有人會說,使用try...except把讀取數(shù)據(jù)的地方包住,只要拋出了異常,在處理異常的時候關(guān)閉游標(biāo)即可:

cursor = handler.find(no_cursor_timeout=True)try: for row in cursor: parse_data(row)except Exception: parse_exception()finally: cursor.close() # 一定要手動關(guān)閉游標(biāo)

其中finally里面的代碼,無論有沒有異常,都會執(zhí)行。

但這樣寫會讓代碼非常難看。為了解決這個問題,我們可以使用游標(biāo)的上下文管理器:

with handler.find(no_cursor_timeout=True) as cursor: for row in cursor:  parse_data(row)

只要程序退出了with的縮進(jìn),游標(biāo)自動就會關(guān)閉。如果程序中途報錯,游標(biāo)也會關(guān)閉。

它的原理可以用下面兩段代碼來解釋:

class Test: def __init__(self):  self.x = 1 def echo(self):  print(self.x) def __enter__(self):  print('進(jìn)入上下文')  return self def __exit__(self, *args):  print('退出上下文')  with Test() as t: t.echo()print('退出縮進(jìn)')

運行效果如下圖所示:

接下來在with的縮進(jìn)里面人為制造異常:

class Test: def __init__(self):  self.x = 1 def echo(self):  print(self.x) def __enter__(self):  print('進(jìn)入上下文')  return self def __exit__(self, *args):  print('退出上下文')  with Test() as t: t.echo() 1 + 'a' # 這里一定會報錯print('退出縮進(jìn)')

運行效果如下圖所示:

無論在with的縮進(jìn)里面發(fā)生了什么,Test這個類中的__exit__里面的代碼始終都會運行。

我們來看看pymongo的游標(biāo)對象里面,__exit__是怎么寫的,如下圖所示:

可以看到,這里正是關(guān)閉游標(biāo)的操作。

因此,如果我們使用上下文管理器,就可以放心大膽地使用no_cursor_timeout=True參數(shù)了。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對武林網(wǎng)的支持。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 一级大片在线观看 | 激情视频免费观看 | 精品一区二区三区在线观看国产 | 黄色片在线免费播放 | 国产人成免费爽爽爽视频 | 伊人久久国产精品 | 成人在线观看网 | 日韩精品中文字幕一区二区三区 | 91精品国产91久久久久久吃药 | 一区二区三区四区视频在线观看 | 九九热精品在线播放 | 精品亚洲成a人在线观看 | 欧日韩| 国产精品剧情一区二区三区 | 黄色网址免费进入 | 99精彩视频在线观看 | 久啪视频| 黄视频网站免费在线观看 | 毛片在线免费观看视频 | 日本a∨精品中文字幕在线 狠狠干精品视频 | 欧美一级做一级爱a做片性 久久久资源网 | 嫩草91在线 | 亚洲精品午夜视频 | 免费视频一区 | 久久久久久久久久久影视 | 久久91精品久久久久清纯 | 青草久久网 | 一级国产精品一级国产精品片 | 性爱网站 | 国产91免费看 | 一级免费特黄视频 | 国产午夜亚洲精品午夜鲁丝片 | 性猛交ⅹxxx乱巴西 欧美日韩1区2区3区 | 草久在线 | 亚洲精品一区二区三区大胸 | 精品国产久 | 一区二区三区四区高清视频 | 手机免费看一级片 | 成年人在线视频 | 久久久久国产成人精品亚洲午夜 | 高清在线国产 |