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