成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

有坑勿踩(二)——關(guān)于游標(biāo)

bawn / 3396人閱讀

摘要:本質(zhì)上所有查詢的數(shù)據(jù)都是從游標(biāo)來的。的作用是從游標(biāo)中提取一批數(shù)據(jù),具體提取多少則是由決定。同時(shí)注意我們已經(jīng)有了一個(gè)游標(biāo)。為了便于理解,我們下面還是稱之為游標(biāo)超時(shí)。

前言

聊一聊一個(gè)最基本的問題,游標(biāo)的使用。可能你從來沒有注意過它,但其實(shí)它在MongoDB的使用中是普遍存在的,也存在一些常見的坑需要引起我們的注意。

在寫這個(gè)系列文章時(shí),我會(huì)假設(shè)讀者已經(jīng)對(duì)MongoDB有了最基礎(chǔ)的了解,因此一些基本名詞和概念就不做過多的解釋,請(qǐng)自己查閱相關(guān)資料。

使用場(chǎng)景

可能你以為你并沒有經(jīng)常在使用游標(biāo),但是其實(shí)只要在做查詢,幾乎時(shí)時(shí)刻刻都在用它。本質(zhì)上所有查詢的數(shù)據(jù)都是從游標(biāo)來的。你說你用toArray()?不存在的,它也是在遍歷游標(biāo)然后返回給你一個(gè)數(shù)組而已。正是因?yàn)檫@樣,就出現(xiàn)了第一個(gè)問題:除非你確定返回?cái)?shù)據(jù)量有限,否則不要隨便toArray()。
這里說的toArray()包括:

shell中的toArray()。例如: var result = db.coll.find().toArray();

node中的toArray()。例如:var result = await db.collection("coll").find().toArray();

python中的list()。例如:result = list(db.coll.find());

Java中的toArray()。例如:DBCursor.toArray();

因?yàn)闊o(wú)論游標(biāo)里有多少數(shù)據(jù),toArray()都會(huì)給你挖出來放到內(nèi)存里,變成數(shù)組返回給你。慢不說,內(nèi)存也占用了很多。所以在可能的情況下,還是盡可能使用hasNext()/next()來得更好。

游標(biāo)主要來自兩個(gè)地方:

find

aggregation

注意二者返回的雖然都是“游標(biāo)”,但又是兩種不同的游標(biāo),使用上API也不完全相同,使用的時(shí)候請(qǐng)先查閱API(特別是使用NodeJS之類的動(dòng)態(tài)語(yǔ)言的時(shí)候不要想當(dāng)然)。

batchSize與getmore

說完從哪里來,下面就該說說怎么用的問題。
可能你已經(jīng)從什么地方看到過getmore,比如mongostat的結(jié)果中。getmore的作用是從游標(biāo)中提取一批數(shù)據(jù),具體提取多少則是由batchSize決定。
所以當(dāng)程序進(jìn)行查詢的時(shí)候,實(shí)際上在后臺(tái)發(fā)生的事情包括:

驅(qū)動(dòng)在后臺(tái)獲取batchSize條數(shù)據(jù)并自己緩存起來;

每次程序調(diào)用游標(biāo)的next()方法時(shí),從這些緩存中提取一條并返回;

當(dāng)batchSize條數(shù)據(jù)都返回完之后,驅(qū)動(dòng)再次通過getmore獲取batchSize條數(shù)據(jù)。

我們可以通過shell來觀察這一過程:

先插入一批數(shù)據(jù):

use foo
for(var i = 0; i < 1000; i++) {
    db.bar.insert({i: i});
}

強(qiáng)制日志記錄所有操作:

db.setProfilingLevel(0, 0)

跟蹤日志:

tail -f mongod.log

現(xiàn)在執(zhí)行一條find語(yǔ)句:

replset:PRIMARY> db.bar.find().batchSize(50);
2018-12-29T16:01:29.587+0800 I COMMAND  [conn12] command test.bar appName: "MongoDB Shell" command: find { find: "bar", filter: {}, batchSize: 50.0, $clusterTime: { clusterTime: Timestamp(1546070474, 1), signature: { hash: BinData(0, 0000000000000000000000000000000000000000), keyId: 0 } }, $db: "test" } planSummary: COLLSCAN cursorid:77199395767 keysExamined:0 docsExamined:50 numYields:0 nreturned:50 reslen:2062 locks:{ Global: { acquireCount: { r: 1 } }, Database: { acquireCount: { r: 1 } }, Collection: { acquireCount: { r: 1 } } } protocol:op_msg 0ms

雖然我們?cè)趕hell中只輸出了20條結(jié)果,但實(shí)際上我們已經(jīng)從這個(gè)游標(biāo)中獲取了50條數(shù)據(jù)(日志中的黑體部分)。所以當(dāng)我們繼續(xù)遍歷這個(gè)游標(biāo)時(shí)是暫時(shí)不需要再次從數(shù)據(jù)庫(kù)中取數(shù)據(jù)的。同時(shí)注意我們已經(jīng)有了一個(gè)游標(biāo)cursor:77199395767
但當(dāng)我們第三次遍歷20條數(shù)據(jù)時(shí),則會(huì)出現(xiàn)getmore日志:

replset:PRIMARY> it
2018-12-29T16:03:46.007+0800 I COMMAND  [conn12] command test.bar appName: "MongoDB Shell" command: getMore { getMore: 77199395767, collection: "bar", batchSize: 50.0, $clusterTime: { clusterTime: Timestamp(1546070594, 1), signature: { hash: BinData(0, 0000000000000000000000000000000000000000), keyId: 0 } }, $db: "test" } originatingCommand: { find: "bar", filter: {}, batchSize: 50.0, $clusterTime: { clusterTime: Timestamp(1546070474, 1), signature: { hash: BinData(0, 0000000000000000000000000000000000000000), keyId: 0 } }, $db: "test" } planSummary: COLLSCAN cursorid:77199395767 keysExamined:0 docsExamined:50 numYields:0 nreturned:50 reslen:2061 locks:{ Global: { acquireCount: { r: 1 } }, Database: { acquireCount: { r: 1 } }, Collection: { acquireCount: { r: 1 } } } protocol:op_msg 0ms
2018-12-29T16:03:46.010+0800 I COMMAND [conn12] command admin.$cmd appName: "MongoDB Shell" command: replSetGetStatus { replSetGetStatus: 1.0, forShell: 1.0, $clusterTime: { clusterTime: Timestamp(1546070624, 1), signature: { hash: BinData(0, 0000000000000000000000000000000000000000), keyId: 0 } }, $db: "admin" } numYields:0 reslen:896 locks:{} protocol:op_msg 0ms

它通過同一個(gè)游標(biāo)再次提取了50條數(shù)據(jù)供使用。當(dāng)我們用完緩存中的數(shù)據(jù)之前都是不會(huì)再看到新的getmore指令的。

游標(biāo)超時(shí)

上面已經(jīng)了解了游標(biāo)與驅(qū)動(dòng)是如何配合工作的,那么游標(biāo)超時(shí)是怎么發(fā)生的呢?條件很簡(jiǎn)單,2次getmore之間間隔了超過10分鐘,即一個(gè)游標(biāo)在服務(wù)端超過10分鐘無(wú)人訪問,則會(huì)被回收掉。這時(shí)候如果你再針對(duì)這個(gè)游標(biāo)進(jìn)行getmore,就會(huì)得到游標(biāo)不存在的錯(cuò)誤(是的,超時(shí)的游標(biāo)在數(shù)據(jù)庫(kù)中是不存在的,你得到的錯(cuò)誤不會(huì)是超時(shí),而是游標(biāo)不存在。為了便于理解,我們下面還是稱之為“游標(biāo)超時(shí)”)。
那么假設(shè)你通過游標(biāo)讀取數(shù)據(jù)的時(shí)候是為了進(jìn)行一系列分析處理,那么下一次getmore在什么時(shí)候發(fā)生將取決于你的應(yīng)用在多長(zhǎng)時(shí)間內(nèi)消耗完了當(dāng)前緩存中的數(shù)據(jù)。換句話說,你的應(yīng)用處理得越慢,下一次getmore發(fā)生的時(shí)間就越晚。很多驅(qū)動(dòng)中batchSize的默認(rèn)值是1000,這也代表著你的應(yīng)用必須至少能夠在10分鐘內(nèi)處理1000條數(shù)據(jù),否則就會(huì)得到游標(biāo)超時(shí)錯(cuò)誤。所以諸如每一條數(shù)據(jù)需要查詢其他數(shù)據(jù)庫(kù)1次,需要通過RESTful API到互聯(lián)網(wǎng)上獲取相關(guān)的數(shù)據(jù),或者需要進(jìn)行一系列復(fù)雜的運(yùn)算,這樣的場(chǎng)景下,問題的關(guān)鍵其實(shí)不在于MongoDB怎么樣,而在于你的應(yīng)用到底能夠處理多快。
假設(shè)問題還是發(fā)生了,你的應(yīng)用遇到了游標(biāo)超時(shí)錯(cuò)誤,怎么辦呢?你至少可以有以下一些選擇:

延長(zhǎng)游標(biāo)超時(shí)時(shí)間,請(qǐng)參考cursorTimeoutMillis;

加速應(yīng)用的處理速度,處理得快了,下一次getmore自然就發(fā)生得更早;

不是那么直觀,但是減小batchSize也可以達(dá)到同樣的目的;

禁用超時(shí)時(shí)間(noCursorTimeout)——絕對(duì)不推薦使用。雖然可以達(dá)到目的,你也可以說我會(huì)在最后主動(dòng)關(guān)閉游標(biāo)的,但事實(shí)上總會(huì)發(fā)生這樣那樣的意外,導(dǎo)致你最終沒有正確關(guān)閉游標(biāo),最后服務(wù)器上塞滿了游標(biāo)的情況也是很常見的。

例外情況

上面已經(jīng)解釋過,在游標(biāo)超時(shí)的時(shí)候你得到的實(shí)際是“游標(biāo)不存在”錯(cuò)誤,而不是超時(shí)。那么反過來是不是也成立呢,“游標(biāo)不存在”一定是超時(shí)了嗎?離散數(shù)學(xué)告訴我們,一個(gè)命題的逆命題不一定成立。事實(shí)上也是如此。“游標(biāo)不存在”的另一種可能性是有些用戶熱衷于在MongoDB前面加上負(fù)載均衡/自動(dòng)故障恢復(fù)的軟/硬件。我們已經(jīng)知道游標(biāo)是存在于一臺(tái)服務(wù)器上的,如果你的負(fù)載均衡毫無(wú)原則地將請(qǐng)求轉(zhuǎn)發(fā)到任意服務(wù)器上,getmore同時(shí)會(huì)因?yàn)檎也坏接螛?biāo)而出現(xiàn)“游標(biāo)不存在”的錯(cuò)誤。
事實(shí)上MongoDB和其驅(qū)動(dòng)本身就已經(jīng)能夠完成高可用和負(fù)載均衡,并不需要額外畫蛇添足。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/19441.html

相關(guān)文章

  • 有坑勿踩(三)——關(guān)于數(shù)據(jù)更新

    摘要:前言數(shù)據(jù)更新,中的,對(duì)任何數(shù)據(jù)庫(kù)而言都是最基本的操作。你并不能保證數(shù)據(jù)在被你讀出來到寫回去期間是否有別人已經(jīng)改了數(shù)據(jù)庫(kù)中的記錄,這就是第一個(gè)風(fēng)險(xiǎn),操作存在潛在的可能性會(huì)覆蓋掉別人更新過的數(shù)據(jù)。 前言 數(shù)據(jù)更新,CRUD中的U,對(duì)任何數(shù)據(jù)庫(kù)而言都是最基本的操作??此坪?jiǎn)單的更新操作中會(huì)藏著哪些坑?今天聊一聊這個(gè)話題。 在寫這個(gè)系列文章時(shí),我會(huì)假設(shè)讀者已經(jīng)對(duì)MongoDB有了最基礎(chǔ)的了解,因...

    mengera88 評(píng)論0 收藏0
  • 有坑勿踩(一):MongoDB PSS vs PSA

    摘要:注意記住的作用始終是把集群中具有投票權(quán)的節(jié)點(diǎn)總數(shù)湊成奇數(shù)用,防止腦裂。其代表的意義是集群中必須有大多數(shù)節(jié)點(diǎn)收到并確認(rèn)了一個(gè)寫操作,這個(gè)寫操作才算成功。無(wú)論源或者目標(biāo)片中不能夠滿足大多數(shù)時(shí),遷移都會(huì)失敗。在有可能的情況下,應(yīng)盡量使用代替。 前言 在技術(shù)社區(qū)混了這么長(zhǎng)時(shí)間,因?yàn)橐恍┏R姷募夹g(shù)問題反復(fù)被問到,總是想寫寫文章把它們講清楚。無(wú)奈很多時(shí)候看似基礎(chǔ)的技術(shù)問題背后都隱藏著很深的原因,想...

    Freelander 評(píng)論0 收藏0
  • python中操作mysql的pymysql模塊詳解

    摘要:簡(jiǎn)述是中操作的模塊,其使用方法和幾乎相同。但目前支持而后者不支持版本。因此要避免這種情況需使用提供的參數(shù)化查詢。使用存儲(chǔ)過程動(dòng)態(tài)執(zhí)行防注入使用存儲(chǔ)過程自動(dòng)提供防注入,動(dòng)態(tài)傳入到存儲(chǔ)過程執(zhí)行語(yǔ)句。 簡(jiǎn)述 pymsql是Python中操作MySQL的模塊,其使用方法和MySQLdb幾乎相同。但目前pymysql支持python3.x而后者不支持3.x版本。本文測(cè)試python版本:3.5....

    shiweifu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

bawn

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<