您好,登錄后才能下訂單哦!
MongoDB的應(yīng)用是怎樣的,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
偶然機(jī)會(huì)看到mongo中文社區(qū)辦了場(chǎng)征文活動(dòng),覺(jué)得挺有意思的,雖說(shuō)自己還在成為大佬的路上,但參與一下未嘗不可。于是就有了這篇文章。
活動(dòng)已規(guī)定了選題框架,我思索了小會(huì)兒,覺(jué)得從0到1+
web app
這個(gè)范圍太廣了。不過(guò)確實(shí)MongoDB在web應(yīng)用里應(yīng)用挺多,web應(yīng)用特點(diǎn)本要求擴(kuò)展性高,靈活豐富的查詢(xún),動(dòng)態(tài)地添加字段等
敏捷開(kāi)發(fā)
這里主要強(qiáng)調(diào)的是由于沒(méi)有固定的schema的優(yōu)勢(shì)使得很適用敏捷開(kāi)發(fā)方法論
分析型和logging
capped collection適合于放日志數(shù)據(jù),至于分析型我倒見(jiàn)不多,莫非還可比dedicated OLAP db更好?
caching
可變schema
個(gè)人建議呢,自己寫(xiě)一個(gè)搭建集群的腳本,方便一鍵生成,非常方便,而不要每次一個(gè)命令一個(gè)命令地敲。我的腳本供參考[7]
后面我會(huì)談到在快速創(chuàng)建定制化集群的基礎(chǔ)上用gdb調(diào)試內(nèi)核。
分布式概念及原理
這一塊領(lǐng)域太大了!
MongoDB屬于分布式數(shù)據(jù)庫(kù),相比于單機(jī)數(shù)據(jù)庫(kù),節(jié)點(diǎn)之間有了網(wǎng)絡(luò)距離,于是乎,各種不靠譜的事就會(huì)發(fā)生了(google一下"分布式系統(tǒng)里常見(jiàn)的8個(gè)fallacies")。我根據(jù)mongo實(shí)際情況講點(diǎn)皮毛 _^^_ 更多有趣的資料務(wù)必參考DDIA[6],這個(gè)是迄今最通俗的版本。
我非常簡(jiǎn)單地用自己的語(yǔ)言從背景、為什么需要它, Mongo怎么做的三方面來(lái)談?wù)劊劦降脑~匯建議讀者多多google。
背景
簡(jiǎn)單理解就是要多方達(dá)到一致。稍微接觸這塊的都知道raft,這個(gè)Stanford 教授 John Ousterhout 和其博士生 Diego Ongaro 弄出來(lái)的,已經(jīng)在多種分布式數(shù)據(jù)庫(kù)上應(yīng)用了如TiDB, PolarDB。
當(dāng)然業(yè)界還有其它協(xié)議,Lamport的Paxos(被應(yīng)用到Chubby),Zookeeper的ZAB,MongoDB的pv1。
為什么需要它
簡(jiǎn)單來(lái)看,當(dāng)多個(gè)節(jié)點(diǎn)共同來(lái)做決定時(shí),如果你說(shuō)你的,我說(shuō)我的,還怎么決定???就像一群人在房間開(kāi)會(huì),七嘴八舌,就是沒(méi)統(tǒng)一,最后這會(huì)只能白開(kāi)了。同理在分布式系統(tǒng)中,我們需有一套規(guī)則讓各節(jié)點(diǎn)對(duì)事件以及結(jié)果達(dá)成一致,這樣才能正常運(yùn)轉(zhuǎn)。這其實(shí)與現(xiàn)實(shí)世界模型是很吻合的。
Mongo怎么做的
mongo用的是MongoDB pv1 ,是一種類(lèi)raft 協(xié)議,不過(guò)它進(jìn)行了豐富的擴(kuò)展,如rs.conf()中就可配置各節(jié)點(diǎn)的priority,hidden, vote等屬性,有非常大的靈活性;增加了PreVote, DryRun等動(dòng)作等。詳細(xì)細(xì)節(jié)讀者可參考相關(guān)文檔。
背景
這幾個(gè)概念有相似性,就放在一起了。貌似分布式系統(tǒng)里我們一般不談ACID,這是在單機(jī)關(guān)系型數(shù)據(jù)庫(kù)常用的詞匯,且這里面的C 與分布式系統(tǒng)所說(shuō)的Consistency不是一回事!
CAP是Brewer 92年就提出的詞匯了,很多論文現(xiàn)在都不推薦使用這個(gè)詞匯,因?yàn)樗苡衅缌x;
在眾多論文里,還有與一致性很多的詞匯,如
- causal consistency,因果一致性,Mongo中有
- linearizability,線(xiàn)性一致性,針對(duì)于single object的,始終讀到最新的數(shù)據(jù)
- serializability,串行化,強(qiáng)調(diào)多個(gè)事務(wù)操作多個(gè)object的,在關(guān)系型db屬最強(qiáng)的隔離級(jí)別
- strict serializability,linearizability + serializability,在google spanner中有提到
- sequence consistency: 順序一致性,比linearizability弱點(diǎn),比如x86 CPU默認(rèn)一致性是它,我們常在C++ Memory Model里見(jiàn)到`std::memory_order_seq`
在數(shù)據(jù)安全性方面,要有持久化的保證,一般常用技巧是定期做checkpoint,且有write-ahead log,這在WiredTiger引擎層有原生的支持。
為什么需要它
凡是有副本,有讀寫(xiě),就必然存在讀能否讀到最新數(shù)據(jù)的問(wèn)題,這就屬于一致性的問(wèn)題。有的業(yè)務(wù)要求必須讀到最新寫(xiě)入的數(shù)據(jù),此為strongconsistency,但有些業(yè)務(wù)不要求,那數(shù)據(jù)庫(kù)可以放開(kāi)這種強(qiáng)約束,于是有最終一致性eventualconsistency,即意味著給定一定的時(shí)間,最終各副本數(shù)據(jù)都會(huì)一樣的,這樣的實(shí)現(xiàn)比起強(qiáng)一致復(fù)雜度要低很多。
Mongo怎么做的
關(guān)于一致性,我得談?wù)劗?dāng)初自己存在已久的誤解。原來(lái)mongo里的quorom 不是我們常說(shuō)的那種quorum !
以前深入了解過(guò)Cassandra,和其C++產(chǎn)品Scylladb,它們的原型是amazon 的Dynamo,論文里談到quorum模型:當(dāng)有N個(gè)節(jié)點(diǎn),如果寫(xiě)大多數(shù),即 W > N/2,讀也大多數(shù) > N/2,則讀一定可以讀到最新寫(xiě)入的數(shù)據(jù)。然而mongo雖然也有majority的說(shuō)法,但其內(nèi)涵完全是另外一回事。
寫(xiě)mongo時(shí),客戶(hù)端只可能寫(xiě)主,不可能寫(xiě)從,這與leader-less 系統(tǒng)(無(wú)主系統(tǒng),各節(jié)點(diǎn)都是對(duì)等的)就不一樣了,從是從主拉數(shù)據(jù)過(guò)去的;主從節(jié)點(diǎn)都在維護(hù)著一個(gè)majoritycommitted 的時(shí)間點(diǎn),當(dāng)寫(xiě)已經(jīng)到達(dá)大多數(shù)時(shí),這個(gè)點(diǎn)就會(huì)向前推進(jìn);
當(dāng)客戶(hù)端指定 readConcern: majority 時(shí),能不能讀成功,就看發(fā)起操作的時(shí)間點(diǎn)是不是在majoritycommitted 時(shí)間點(diǎn)后面,如果是,則majority 讀就是成功的;
Mongo事務(wù)支持快照隔離,即事務(wù)可讀最近穩(wěn)定的一個(gè)點(diǎn),它可能是老數(shù)據(jù),但是它與其它數(shù)據(jù)是一致的,這樣就避免了讀寫(xiě)沖突。
背景
在分布式系統(tǒng)中,復(fù)制是提高可用性的重要、常規(guī)手段。在復(fù)雜分布式環(huán)境下,總有個(gè)別組件就會(huì)崩,卡住,不響應(yīng),此時(shí)為了不影響用戶(hù)的請(qǐng)求,就需要將請(qǐng)求轉(zhuǎn)到正常的節(jié)點(diǎn)上,那數(shù)據(jù)就得有多份,要不然怎么訪(fǎng)問(wèn)先前訪(fǎng)問(wèn)的數(shù)據(jù)呢?
故障冗余是個(gè)經(jīng)典概念了,分布式里的故障千奇百怪,軟件的,硬件的,人為的;在典型的單主系統(tǒng)里,主節(jié)點(diǎn)要是沒(méi)有,就會(huì)影響用戶(hù)的讀寫(xiě),所以在前一個(gè)的主節(jié)點(diǎn)沒(méi)了的那很短的時(shí)刻就必須有新的主來(lái)替代它,完美的時(shí)候用戶(hù)根本感受不到切主。
為什么需要它
正如前面所說(shuō)保證系統(tǒng)可用性,數(shù)據(jù)安全性。
Mongo怎么做的
Mongo是單主系統(tǒng),寫(xiě)只能寫(xiě)主節(jié)點(diǎn),因此它有選舉機(jī)制,靠的是前面的所說(shuō)的類(lèi)raft協(xié)議。這是保證故障冗余;
復(fù)制方面,從節(jié)點(diǎn)從主節(jié)點(diǎn)拉oplog,oplog就可理解為raft里的log,它反映了主節(jié)點(diǎn)的mutation,從節(jié)點(diǎn)將這在本地apply,就可達(dá)到與主節(jié)點(diǎn)一致的狀態(tài)。
非常詳細(xì)的說(shuō)明見(jiàn)官方源碼[12]。
內(nèi)核
個(gè)人接觸內(nèi)核也沒(méi)多久,在此拋磚引玉。
內(nèi)核其實(shí)分Server層和Storage Engine層,由于Server接觸不完備,暫只講講引擎層的事兒。
這里有一份由doxygen生成的文檔[11],值得一閱。
引擎層技術(shù)可謂是數(shù)據(jù)庫(kù)系統(tǒng)的核心技術(shù),里面涉及了數(shù)據(jù)庫(kù)的核心原理的實(shí)現(xiàn)。首先我們要明白,數(shù)據(jù)的組織可以是多種方式,究竟哪個(gè)方式好,在代碼未實(shí)現(xiàn)出來(lái)之前,恐怕還沒(méi)法說(shuō)。
明顯這里我們需要插拔的特性,數(shù)據(jù)庫(kù)層(也就是干sql,cql,查詢(xún)優(yōu)化,執(zhí)行計(jì)劃等的)可以靈活接入多種存儲(chǔ)引擎,這樣最后誰(shuí)好誰(shuí)差,比一比就知道了。所以引擎層必須很獨(dú)立,提供最原始的接口供上層調(diào)用即可,這也是計(jì)算機(jī)分層思想在數(shù)據(jù)庫(kù)領(lǐng)域的完美體現(xiàn)。
MongoDB引擎從3.x開(kāi)始就是WiredTiger了,官方似乎一直沒(méi)考慮把RocksDB兼容性的代碼放進(jìn)去,所以MongoRocks是一個(gè)第三方的存在;當(dāng)然還有一個(gè)in-memory引擎。
WiredTiger
這里簡(jiǎn)稱(chēng)為WT[8]。WT最初是一家由大佬 Michael Cahill 創(chuàng)立的,某一年被MongoDB收購(gòu),從此一直是mongo默認(rèn)的存儲(chǔ)引擎。我們可以在這兒[2]看到WT的基本介紹,挺豐富的,沒(méi)事可多查閱。
WT首先是一個(gè)kv存儲(chǔ)引擎,類(lèi)別上與Rocksdb一致,不過(guò)名氣確實(shí)小很多,原因可能是比較小眾,貌似只有mongo用,且代碼看著確實(shí)不太易讀;
引擎索引實(shí)現(xiàn)是B tree,而不是B+ tree,這一點(diǎn)網(wǎng)上也有不少的討論,至于為何用B tree,據(jù)我所知:
1.mongo著重于提高point query性能,而非range query,這樣不像B+ tree那樣每次都得去葉子節(jié)點(diǎn)拿數(shù)據(jù),平均來(lái)看,走更短的路徑;
2.優(yōu)化讀多寫(xiě)少的場(chǎng)景;
3.其他。
WT在mongo使用,其實(shí)基本的調(diào)用就那么幾個(gè):
1.創(chuàng)建連接conn
wiredtiger_open(home, NULL,"create,cache_size=**, transaction_sync=**, checkpoint_sync=**,...",&conn)
這在啟動(dòng)時(shí)就需調(diào)用,生成一個(gè)指向db的WT_CONN,它作為WiredTigerKVEngine的私有成員。
2.創(chuàng)建session
mongo里的操作都有session上下文的,文檔里的session,其實(shí)就對(duì)應(yīng)引擎層的WT_SESSION ; 代碼里為了高效利用session,有個(gè)sessionCache供使用,不用每次都去open
conn->open_session(conn,NULL, "isolation=**", &session)
3.創(chuàng)建表/索引
當(dāng)mongo層執(zhí)行createCollection/createIndex時(shí),即有:
sesssion->create(session, "table::access", "key_format=S,value_format=S"))
4.在session上創(chuàng)建cursor
session->open_cursor(session, "table:mytable", NULL,NULL,&cursor)
5.支持事務(wù)時(shí),在session上開(kāi)啟事務(wù)
session->begin_transaction(session, "isolation=**, read_timestamp=**,sync=**,...")
6.用cursor set/get key/value
用戶(hù)看到的json,mongo server層看到的BSON,其實(shí)在底層都轉(zhuǎn)成了(key, value) pair
cursor->set_key(cursor,"key")
cursor->set_value(cursor,"value")
cursor->update(cursor);
7.提交/回滾事務(wù)
session->commit_transaction(session,"commit_timestamp=**, durable_timestamp=**, sync=**,...")
session->rollback_transaction(session,NULL);
對(duì)于以上步驟,幾點(diǎn)澄清:
·WT API調(diào)用就像那種風(fēng)格,特別明顯的是會(huì)有一參數(shù)char* config,里面就用a=b這種格式來(lái)指定各種配置參數(shù)。雖說(shuō)挺原始的做法;
·有關(guān)時(shí)間戳的參數(shù)較為復(fù)雜,需要深入文檔;
·參數(shù)含義還是得參考[2]。
從官方文檔和視頻[14]中來(lái)看,從3.6開(kāi)始引入 logical session,在WT 的update structure里添加timestamp field等這些動(dòng)作都是逐漸在為支持事務(wù)、分布式事務(wù)為鋪路。
我為熟悉MongoRocks對(duì)事務(wù)的支持接觸過(guò)WT的時(shí)間戳一些概念,目前還不能很系統(tǒng)地論述各個(gè)時(shí)間戳之間是如何運(yùn)作的。這方面可多多參考[2] ,我不在此講了。
聽(tīng)名字想必也能猜得到是與rocksdb有關(guān),想到它也很自然,既然底層接kv engine,rocksdb又是kv型,完全可接啊,正如MyRocks那樣。看源碼[3] stars也有300+,最初由開(kāi)發(fā)者 Igor Canadi 及其他實(shí)現(xiàn)了3.2, 3.4的MongoRocks版。項(xiàng)目被擱置一段時(shí)間,幾個(gè)月前 Igor Canadi 接受了wolfkdy 對(duì)MongoRocks 4.0 的MR[16],我在其中參與了相關(guān)PR提交如[4]。
4.0 mongo-rocks 驅(qū)動(dòng)層的實(shí)現(xiàn)主要集中于事務(wù)部分,正如 Igor 所說(shuō),3.6.x之后,mongo的內(nèi)部事務(wù)跳躍性大,若正確實(shí)現(xiàn)4.0版需很大一部分精力[5]。
MongoRocks 4.0 剛出不久,因此還需更多時(shí)間來(lái)穩(wěn)定,比如之前由我發(fā)現(xiàn)的oplog讀取有空洞的問(wèn)題[13],已被作者修復(fù)[15]。個(gè)人還是非常期待Rocksdb能接入到Mongo的,相信會(huì)有比WT更亮的點(diǎn)!在這方面?zhèn)€人應(yīng)該會(huì)投入更多時(shí)間,期待有更多國(guó)內(nèi)開(kāi)發(fā)者加入!
大型代碼,如果用gdb單步著來(lái)學(xué)習(xí)肯定是不行的,單步只適用于調(diào)試bug的時(shí)候。我這里談gdb調(diào)試用來(lái)干嘛呢? get runtimepath !
我一直認(rèn)為,拿到一份大型C++項(xiàng)目,除了肉眼盯著代碼看半天了解code flow之外,用gdb bt 更是一大利器!在server端加一斷點(diǎn),客戶(hù)端發(fā)一個(gè)命令過(guò)來(lái),然后一bt ,立刻知道server 走的核心路徑,很方便!
劃重點(diǎn):請(qǐng)用 >= 8.x 版本的gdb。好處是bt自帶顏色顯示,看著比以前舒服多了。
以下聊聊一般怎么用。
首先啟動(dòng)一副本集或分片集群(取決于你關(guān)注哪個(gè)),對(duì)主進(jìn)行以下設(shè)置:
cfg=rs.conf();cfg.settings.heartbeatTimeoutSecs=3600; cfg.settings.electionTimeoutMillis=3600000;rs.reconfig(cfg)
這里假設(shè)我們要調(diào)試主。為了防止調(diào)試時(shí),默認(rèn)的時(shí)間內(nèi)就failover了,所以增大heartbeat,election的超時(shí),這樣主就一直仍是主(當(dāng)然若想調(diào)試主從代碼的code,就不要這么做了)
當(dāng)我們想看看insert命令的請(qǐng)求路徑時(shí),
隨便看看代碼,去搜索一下insert關(guān)鍵字,相信不難發(fā)現(xiàn)有CmdInsert這樣的字眼。再仔細(xì)一看,發(fā)現(xiàn)它繼承一個(gè)基類(lèi),它還有個(gè)run方法,有感覺(jué)的開(kāi)發(fā)者其實(shí)這時(shí)就能猜一猜了:斷定server收到insert請(qǐng)求時(shí),很可能run要被調(diào)用!
于是乎可在run處加個(gè)斷點(diǎn),或者我們?cè)趃rep中發(fā)現(xiàn)了insertRecords的字眼,更能判定插入文檔時(shí)很可能走了這里,于是有了這樣:
可以繼續(xù)enter,這個(gè)路徑從libc.so start_thread 到run,到insertRecords 很長(zhǎng)的,這一段路徑夠我們分析是怎么走的了。
同樣,對(duì)于find, update,delete都是類(lèi)似手段。
對(duì)于事務(wù)操作,可以去grep transaction字樣,也會(huì)發(fā)現(xiàn)可以被作為斷點(diǎn)的函數(shù),遇到begin_transaction,commit_transaction, rollback_transaction其實(shí)是很熟悉的函數(shù)名稱(chēng),適合加上斷點(diǎn)。
結(jié)語(yǔ)
MongoDB技術(shù)淺談在此,這方面知識(shí)量非常龐大,確不是由一篇文章能道盡的。對(duì)我自己而言,其內(nèi)涵本身是迷人的,因?yàn)樗菙?shù)據(jù)庫(kù),它是分布式系統(tǒng),它還有許多毛病。盡管Mongo官方縮緊了協(xié)議,一些云廠(chǎng)商沒(méi)法玩高版本了。但我想,只要它還是開(kāi)源的,只要它代碼還是真的,對(duì)工程師而言這仍然是一件欣慰的事吧。由淺入深,從此刻開(kāi)始!
看完上述內(nèi)容,你們掌握MongoDB的應(yīng)用是怎樣的的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。