MongoDB 基礎(chǔ)淺談

1 MongoDB 特點(diǎn)

面向集合存儲(chǔ):MongoDB 是面向集合的,數(shù)據(jù)以 collection 分組存儲(chǔ)。每個(gè) collection 在數(shù)據(jù)庫(kù)中都有唯一的名稱。

模式自由:集合的概念類(lèi)似 MySQL 里的表,但它不需要定義任何模式。

結(jié)構(gòu)松散:對(duì)于存儲(chǔ)在數(shù)據(jù)庫(kù)中的文檔,不需要設(shè)置相同的字段,并且相同的字段不需要相同的數(shù)據(jù)類(lèi)型,不同結(jié)構(gòu)的文檔可以存在同一個(gè) collection 里。

高效的二進(jìn)制存儲(chǔ):存儲(chǔ)在集合中的文檔,是以鍵值對(duì)的形式存在的。鍵用于唯一標(biāo)識(shí)一個(gè)文檔,一般是 ObjectId 類(lèi)型,值是以 BSON 形式存在的。BSON = Binary JSON, 是在 JSON 基礎(chǔ)上加了一些類(lèi)型及元數(shù)據(jù)描述的格式。

支持索引:可以在任意屬性上建立索引,包含內(nèi)部對(duì)象。MongoDB 的索引和 MySQL 的索引基本一樣,可以在指定屬性上創(chuàng)建索引以提高查詢的速度。除此之外,MongoDB 還提供創(chuàng)建基于地理空間的索引的能力。

支持 mapreduce:通過(guò)分治的方式完成復(fù)雜的聚合任務(wù)。

支持 failover:通過(guò)主從復(fù)制機(jī)制,可以實(shí)現(xiàn)數(shù)據(jù)備份、故障恢復(fù)、讀擴(kuò)展等功能?;趶?fù)制集的復(fù)制機(jī)制提供了自動(dòng)故障恢復(fù)的功能,確保了集群數(shù)據(jù)不會(huì)丟失。

支持分片:MongoDB 支持集群自動(dòng)切分?jǐn)?shù)據(jù),可以使集群存儲(chǔ)更多的數(shù)據(jù),實(shí)現(xiàn)更大的負(fù)載,在數(shù)據(jù)插入和更新時(shí),能夠自動(dòng)路由和存儲(chǔ)。

支持存儲(chǔ)大文件:MongoDB 中 BSON 對(duì)象最大不能超過(guò) 16 MB。對(duì)于大文件的存儲(chǔ),BSON 格式無(wú)法滿足。GridFS 機(jī)制提供了一個(gè)存儲(chǔ)大文件的機(jī)制,可以將一個(gè)大文件分割成為多個(gè)較小的文檔進(jìn)行存儲(chǔ)。

2 MongoDB 要素

database: 數(shù)據(jù)庫(kù)。

collection: 數(shù)據(jù)集合,相當(dāng)于 MySQL 的 table。

document: 數(shù)據(jù)記錄行,相當(dāng)于 MySQL 的 row。

field: 數(shù)據(jù)域,相當(dāng)于 MySQL 的 column。

index: 索引。

primary key: 主鍵。

3 MongoDB 數(shù)據(jù)庫(kù)

一個(gè) MongoDB 實(shí)例可以創(chuàng)建多個(gè) database。連接時(shí)如果沒(méi)開(kāi)啟免認(rèn)證模式的話,需要連接到 admin 庫(kù)進(jìn)行認(rèn)證。如果開(kāi)啟免認(rèn)證模式,若不指定 database 進(jìn)行連接,默認(rèn)連接一個(gè)叫 db 的數(shù)據(jù)庫(kù),該數(shù)據(jù)庫(kù)存儲(chǔ)在 data 目錄中。通過(guò) show dbs 命令可以查看所有的數(shù)據(jù)庫(kù)。數(shù)據(jù)庫(kù)名不能包含空字符。數(shù)據(jù)庫(kù)名不能為空并且必須小于 64 個(gè)字符。

MongoDB 預(yù)留了幾個(gè)特殊的 database。

admin: admin 數(shù)據(jù)庫(kù)主要是保存 root 用戶和角色。例如,system.users 表存儲(chǔ)用戶,system.roles 表存儲(chǔ)角色。一般不建議用戶直接操作這個(gè)數(shù)據(jù)庫(kù)。將一個(gè)用戶添加到這個(gè)數(shù)據(jù)庫(kù),且使它擁有 admin 庫(kù)上的名為 dbAdminAnyDatabase 的角色權(quán)限,這個(gè)用戶自動(dòng)繼承所有數(shù)據(jù)庫(kù)的權(quán)限。一些特定的服務(wù)器端命令也只能從這個(gè)數(shù)據(jù)庫(kù)運(yùn)行,比如關(guān)閉服務(wù)器。

local:?local 數(shù)據(jù)庫(kù)是不會(huì)被復(fù)制到其他分片的,因此可以用來(lái)存儲(chǔ)本地單臺(tái)服務(wù)器的任意 collection。一般不建議用戶直接使用 local 庫(kù)存儲(chǔ)任何數(shù)據(jù),也不建議進(jìn)行 CRUD 操作,因?yàn)閿?shù)據(jù)無(wú)法被正常備份與恢復(fù)。

config: 當(dāng) MongoDB 使用分片設(shè)置時(shí),config 數(shù)據(jù)庫(kù)可用來(lái)保存分片的相關(guān)信息。

一個(gè) MongoDB 實(shí)例的數(shù)據(jù)結(jié)構(gòu)如下圖:


4 MongoDB 集合

MongoDB 集合存在于數(shù)據(jù)庫(kù)中,沒(méi)有固定的結(jié)構(gòu),可以往集合插入不同格式和類(lèi)型的數(shù)據(jù)。集合不需要事先創(chuàng)建。當(dāng)?shù)谝粋€(gè)文檔插入,或者第一個(gè)索引創(chuàng)建時(shí),集合就會(huì)被創(chuàng)建。集合名必須以下劃線或者字母符號(hào)開(kāi)始,并且不能包含 $,不能為空字符串(比如 ""),不能包含空字符,且不能以 system. 為前綴。

capped collection 是固定大小的集合,支持高吞吐的插入操作和查詢操作。它的工作方式與循環(huán)緩沖區(qū)類(lèi)似,當(dāng)一個(gè)集合填滿了被分配的空間,則通過(guò)覆蓋最早的文檔來(lái)為新的文檔騰出空間。和標(biāo)準(zhǔn)的 collection 不同,capped collection 需要顯式創(chuàng)建,指定大小,單位是字節(jié)。capped collection 可以按照文檔的插入順序保存到集合中,而且這些文檔在磁盤(pán)上存放位置也是按照插入順序來(lái)保存的,所以更新 capped collection 中的文檔,不可以超過(guò)之前文檔的大小,以便確保所有文檔在磁盤(pán)上的位置一直保持不變。

5 MongoDB 視圖

視圖基于已有的集合進(jìn)行創(chuàng)建,是只讀的,不實(shí)際存儲(chǔ)硬盤(pán),通過(guò)視圖進(jìn)行寫(xiě)操作會(huì)報(bào)錯(cuò)。視圖使用其上游集合的索引。由于索引是基于集合的,所以你不能基于視圖創(chuàng)建、刪除或重建索引,也不能獲取視圖的索引列表。如果視圖依賴的集合是分片的, 那么視圖也視為分片的。視圖是實(shí)時(shí)計(jì)算并讀取的。

6 MongoDB 索引

MongoDB 支持豐富的索引方式。如果沒(méi)有索引,讀操作就必須掃描集合中的每個(gè)文檔并篩選符合查詢條件的記錄。索引能夠在很大程度上提高查詢速度。

單字段索引:有三種方式,(1)在單個(gè)字段上創(chuàng)建索引;(2)在嵌入式字段上創(chuàng)建索引;(3)在內(nèi)嵌文檔上創(chuàng)建索引。

復(fù)合索引:支持在多個(gè)字段上匹配的查詢。對(duì)任何復(fù)合索引施加 32 個(gè)字段的限制。對(duì)于復(fù)合索引,MongoDB 可以使用索引來(lái)支持對(duì)索引前綴的查詢。

多鍵索引:為了索引包含數(shù)組值的字段,MongoDB 為數(shù)組中的每個(gè)元素創(chuàng)建一個(gè)索引鍵。這些多鍵索引支持對(duì)數(shù)組字段的高效查詢。

文本索引:支持對(duì)字符串內(nèi)容的文本搜索查詢。文本索引可以包含任何值為字符串或字符串元素?cái)?shù)組的字段。一個(gè)集合最多可以有一個(gè)文本索引。

通配符索引:支持針對(duì)未知或任意字段的查詢。例如:db.collection.createIndex( {"a.$**" : 1 } ) 可支持諸如 db.collection.find({ "a.b" : 1 })、db.collection.find({ "a.c" : { $lt : 2 } }) 等查詢,提高查詢效率。不能使用通配符索引來(lái)分片集合。不能為通配符創(chuàng)建復(fù)合索引。

通配符文本索引:通配符文本索引不同于通配符索引。通配符索引不支持使用$text操作符的查詢。通配符文本索引為集合中每個(gè)文檔中包含字符串?dāng)?shù)據(jù)的每個(gè)字段建立索引。索引的創(chuàng)建方式示例:db.collection.createIndex( { "$**": "text" } )。

2dsphere 索引:支持球體上的地理空間查詢:包含、相交和鄰近度查詢。

hashed 索引:支持使用哈希的分片鍵進(jìn)行分片?;诠5姆制褂米侄蔚纳⒘兴饕鳛榉制I,以便跨分片集群對(duì)數(shù)據(jù)進(jìn)行分區(qū)。MongoDB 支持任何單個(gè)字段的哈希索引,但不支持創(chuàng)建具有多個(gè)哈希字段的復(fù)合索引,也不能在索引上指定唯一哈希索引。

ttl 索引:一種特殊的單字段索引,支持在一定的時(shí)間或特定的期限后自動(dòng)從集合中刪除文檔。TTL索引不能保證過(guò)期數(shù)據(jù)在過(guò)期時(shí)立即刪除。默認(rèn)每60秒運(yùn)行一次刪除過(guò)期文檔的后臺(tái)進(jìn)程。capped collection 不支持 ttl 索引。

唯一索引:確保索引字段不會(huì)存儲(chǔ)重復(fù)值。如果集合已經(jīng)存在了違反索引的唯一約束的文檔,則后臺(tái)創(chuàng)建唯一索引會(huì)失敗。

部分索引:只索引集合中滿足指定篩選器表達(dá)式的文檔。例如:db.collection.createIndex({ a:1 },{ partialFilterExpression: { b: { $lt: 100 } } }) 表示只對(duì)集合中 b 字段小于 100 的文進(jìn)行索引,大于等于 100 的文檔不會(huì)被索引。這可以有效提高存儲(chǔ)效率。

稀疏索引:只包含有索引字段的文檔的條目,即使索引字段包含空值。索引會(huì)跳過(guò)任何缺少索引字段的文檔。非稀疏索引包含集合中的所有文檔,為那些不包含索引字段的文檔存儲(chǔ)空值。

7 MongoDB ObjectId

ObjectId 可以快速生成并排序,長(zhǎng)度為 12 個(gè)字節(jié),包括:

一個(gè) 4 字節(jié)的時(shí)間戳,表示 unix 時(shí)間戳

5 字節(jié)隨機(jī)值

3 字節(jié)遞增計(jì)數(shù)器,初始化為隨機(jī)值

在 MongoDB 中,存儲(chǔ)在集合中的每個(gè)文檔都需要一個(gè)唯一的 _id 字段作為主鍵。如果插入的文檔省略了 _id 字段,則自動(dòng)為文檔生成一個(gè) _id。

8 MongoDB 復(fù)制集

MongoDB 的復(fù)制集又稱為副本集(Replica Set),是一組維護(hù)相同數(shù)據(jù)集合的 mongod 進(jìn)程。復(fù)制集包含多個(gè)數(shù)據(jù)節(jié)點(diǎn)和一個(gè)可選的仲裁節(jié)點(diǎn)(arbiter)。在數(shù)據(jù)節(jié)點(diǎn)中,有且僅有一個(gè)成員為主節(jié)點(diǎn)(primary),其他節(jié)點(diǎn)為從節(jié)點(diǎn)(secondary)。

一個(gè)典型的復(fù)制集架構(gòu)圖如下:


8.1 復(fù)制集節(jié)點(diǎn)類(lèi)型

主節(jié)點(diǎn):接收所有的寫(xiě)操作,并將集合所有的變化記錄到操作日志中,即 oplog。

從節(jié)點(diǎn):通過(guò)復(fù)制主節(jié)點(diǎn)的操作來(lái)維護(hù)一個(gè)相同的數(shù)據(jù)集。從節(jié)點(diǎn)有幾個(gè)選配項(xiàng):v 參數(shù)決定是否具有投票權(quán);priority 參數(shù)決定節(jié)點(diǎn)選主過(guò)程時(shí)的優(yōu)先級(jí);hidden 參數(shù) 決定是否對(duì)客戶端可見(jiàn);slaveDelay 參數(shù)表示復(fù)制 n 秒之前的數(shù)據(jù),保持與主節(jié)點(diǎn)的時(shí)間差。從節(jié)點(diǎn)可以配置成 0 優(yōu)先級(jí),阻止它在選舉中成為主節(jié)點(diǎn),適用于將該節(jié)點(diǎn)部署在備用數(shù)據(jù)中心,或者將它作為一個(gè)冷節(jié)點(diǎn);可以配置為隱藏復(fù)制集,防止應(yīng)用程序從它讀取數(shù)據(jù),適用于在該節(jié)點(diǎn)上運(yùn)行需要與正常流量分離的程序;可以配置為延遲復(fù)制集,保持一個(gè)歷史快照,以便做按特定時(shí)間的故障恢復(fù)。

仲裁節(jié)點(diǎn):如果將一個(gè) mongod 實(shí)例作為仲裁節(jié)點(diǎn)添加到一個(gè)復(fù)制集中,該節(jié)點(diǎn)可以參與主節(jié)點(diǎn)選舉,但不保存數(shù)據(jù)。仲裁節(jié)點(diǎn)永遠(yuǎn)只能是仲裁節(jié)點(diǎn)。

8.2 復(fù)制集選主

MongoDB 的副本集協(xié)議(又稱為 pv1),是一種 raft-like 協(xié)議,即基于 raft 協(xié)議的理論思想實(shí)現(xiàn),并且對(duì)之進(jìn)行了一些擴(kuò)展。當(dāng)往復(fù)制集添加一個(gè)節(jié)點(diǎn),或當(dāng)主節(jié)點(diǎn)無(wú)法和集群中其他節(jié)點(diǎn)通信的時(shí)間超過(guò)參數(shù) electionTimeoutMillis 配置的期限時(shí),從節(jié)點(diǎn)會(huì)嘗試通過(guò) pv1 協(xié)議發(fā)起選舉來(lái)推薦自己成為新主節(jié)點(diǎn)。

在選舉前具有投票權(quán)的節(jié)點(diǎn)之間兩兩互相發(fā)送心跳,以偵測(cè)節(jié)點(diǎn)是否存活。復(fù)制集節(jié)點(diǎn)每?jī)擅胂虮舜税l(fā)送心跳。如果心跳未在 10 秒內(nèi)返回,則發(fā)送心跳的一方將被發(fā)送方標(biāo)記為不可訪問(wèn),也就是說(shuō),默認(rèn)當(dāng)5次心跳未收到時(shí)判斷為節(jié)點(diǎn)失聯(lián)。如果失聯(lián)的是主節(jié)點(diǎn),從節(jié)點(diǎn)會(huì)發(fā)起選舉,選出新的主節(jié)點(diǎn); 如果失聯(lián)的是從節(jié)點(diǎn)則不會(huì)產(chǎn)生新的選舉。選舉基于 raft 一致性算法實(shí)現(xiàn),在大多數(shù)投票節(jié)點(diǎn)存活下選舉出主節(jié)點(diǎn)。只有能夠與多數(shù)節(jié)點(diǎn)建立連接且具有較新的 oplog 的節(jié)點(diǎn)才可能被選舉為主節(jié)點(diǎn),如果集群里的節(jié)點(diǎn)配置了優(yōu)先級(jí),那么具有較高的優(yōu)先級(jí)的節(jié)點(diǎn)更可能被選舉為主節(jié)點(diǎn)。

復(fù)制集中最多可以有50個(gè)節(jié)點(diǎn),但具有投票權(quán)的節(jié)點(diǎn)最多7個(gè)。

8.3 復(fù)制集作用

主節(jié)點(diǎn)發(fā)生故障時(shí)自動(dòng)選舉出一個(gè)新的主節(jié)點(diǎn),以實(shí)現(xiàn) failover。

將數(shù)據(jù)從一個(gè)數(shù)據(jù)中心復(fù)制到另一個(gè)數(shù)據(jù)中心,減少另一個(gè)數(shù)據(jù)中心的讀延遲。

實(shí)現(xiàn)讀寫(xiě)分離。

實(shí)現(xiàn)容災(zāi),可以在數(shù)據(jù)中心故障時(shí)快速切換到同城或異地的數(shù)據(jù)中心。

9 MongoDB 分片集

MongoDB 支持通過(guò)分片技術(shù)來(lái)支持海量數(shù)據(jù)存儲(chǔ)。解決數(shù)據(jù)增長(zhǎng)的擴(kuò)展方式有兩種:垂直擴(kuò)展和水平擴(kuò)展。垂直擴(kuò)展通過(guò)增加單個(gè)服務(wù)器的能力來(lái)實(shí)現(xiàn),比如磁盤(pán)空間、內(nèi)存容量、CPU 數(shù)量等;水平擴(kuò)展則通過(guò)將數(shù)據(jù)存儲(chǔ)到多個(gè)服務(wù)器上來(lái)實(shí)現(xiàn)。MongoDB 通過(guò)分片實(shí)現(xiàn)水平擴(kuò)展。

一個(gè)典型的分片集群架構(gòu)如下:


9.1 分片集組件

shard:每個(gè)分片上可以保存一個(gè)集合的子集,所有分片上的子集的數(shù)據(jù)互不相交,構(gòu)成完整的集合。每個(gè)分片可以被部署為復(fù)制集架構(gòu)。最大為 1024 個(gè)分片。

mongos:充當(dāng)查詢路由器,在客戶端和分片集之間提供讀寫(xiě)接口。mongos 提供集群?jiǎn)我蝗肟?,轉(zhuǎn)發(fā)應(yīng)用端請(qǐng)求,選擇合適的數(shù)據(jù)節(jié)點(diǎn)進(jìn)行讀寫(xiě),合并多個(gè)數(shù)據(jù)節(jié)點(diǎn)的返回。 mongos 是無(wú)狀態(tài)的,分片集群一般需要配置至少 2 個(gè) mongos。

config server:存儲(chǔ)分片集的相關(guān)配置信息。

9.2 分片鍵

MongoDB 集合若要采用分片,必須要指定分片鍵(shard key)。分片鍵由文檔中的一個(gè)或多個(gè)字段組成。分片集合必須具有支持分片鍵的索引,索引可以是分片鍵的索引,也可以是以分片鍵是索引前綴的復(fù)合索引。要對(duì)已填充的集合進(jìn)行分片,該集合必須具有以分片鍵開(kāi)頭的索引;分片一個(gè)空集合時(shí),如果該集合還沒(méi)有包含指定分片鍵的索引,則 MongoDB 會(huì)默認(rèn)給分片鍵創(chuàng)建索引。

對(duì)于一個(gè)即將要分片的集合,如果該集合具有其他唯一索引,則無(wú)法分片該集合。

對(duì)于已分片的集合,不能在其他字段上創(chuàng)建唯一索引。

4.2 版本開(kāi)始可以更改文檔的分片鍵值,除非分片鍵字段為不可變的 _id 字段。更新分片鍵時(shí)必須在事務(wù)中或以可重試寫(xiě)入的方式在 mongos 上運(yùn)行,不能直接在分片上執(zhí)行操作。在此之前文檔的分片鍵字段值是不可變的。

4.4 版本開(kāi)始,可以向現(xiàn)有片鍵中添加一個(gè)或多個(gè)后綴字段以優(yōu)化集合的片鍵。

5.0 版本開(kāi)始,實(shí)現(xiàn)了實(shí)時(shí)重新分片(live resharding),可以實(shí)現(xiàn)分片鍵的完全重新選擇。live resharding 機(jī)制下,數(shù)據(jù)將根據(jù)新的分片規(guī)則進(jìn)行遷移,不過(guò)有一些限制,比如一個(gè)實(shí)例中有且只能有一個(gè)集合在相同的時(shí)間下 resharding 等。

數(shù)據(jù)庫(kù)可以混合使用分片和未分片集合。分片集合被分區(qū)并分布在集群中的各個(gè)分片中。而未分片集合僅存儲(chǔ)在主分片中。

設(shè)置 shard key 時(shí)應(yīng)該充分考慮取值基數(shù)和取值分布。分片鍵應(yīng)被盡可能多的業(yè)務(wù)場(chǎng)景用到。盡可能避免使用單調(diào)遞增或遞減的字段作為分片鍵。

9.3 分片策略

MongoDB 將分片數(shù)據(jù)拆分成塊。每個(gè)分塊都有一個(gè)基于分片鍵的上下限范圍 。分片策略包括哈希分片、范圍分片和自定義 zone 分片。

哈希分片會(huì)計(jì)算分片鍵字段的哈希值,這個(gè)值被用作片鍵,然后根據(jù)哈希值的散列為每個(gè)塊分配一個(gè)范圍。

范圍分片根據(jù)分片鍵的值將數(shù)據(jù)劃分為多個(gè)連續(xù)范圍。,然后基于分片鍵的值分配每個(gè)塊的范圍。當(dāng)片鍵的基數(shù)大、頻率低且值非單調(diào)變更時(shí),范圍分片更高效。

自定義 zone 分片基于 shard key 創(chuàng)建。每個(gè) zone 與集群中的一個(gè)或者更多分片關(guān)聯(lián)。一個(gè)分片可以和任意數(shù)目的非沖突 zone 相關(guān)聯(lián)。

10 MongoDB 聚合

MongoDB 聚合框架(Aggregation Framework)是一個(gè)計(jì)算框架,功能是:

作用在一個(gè)或幾個(gè)集合上。

對(duì)集合中的數(shù)據(jù)進(jìn)行的一系列運(yùn)算。

將這些數(shù)據(jù)轉(zhuǎn)化為期望的形式。

MongoDB 提供了三種執(zhí)行聚合的方法:聚合管道,map-reduce 和單一目的聚合方法(如 count、distinct 等方法)。

10.1 聚合管道

在聚合管道中,整個(gè)聚合運(yùn)算過(guò)程稱為管道(pipeline),它是由多個(gè)步驟(stage)組成的, 每個(gè)管道的工作流程是:

接受一系列原始數(shù)據(jù)文檔

對(duì)這些文檔進(jìn)行一系列運(yùn)算

結(jié)果文檔輸出給下一個(gè) stage

聚合計(jì)算基本格式如下:

pipeline=[$stage1,$stage2,...$stageN];db.collection.aggregate(pipeline,{ options })

10.2 map-reduce

map-reduce 操作包括兩個(gè)階段:map 階段處理每個(gè)文檔并將 key 與 value 傳遞給 reduce 函數(shù)進(jìn)行處理,reduce 階段將map 操作的輸出組合在一起。map-reduce 可使用自定義 JavaScript 函數(shù)來(lái)執(zhí)行 map 和 reduce 操作,以及可選的 finalize 操作。通常情況下效率比聚合管道低。

10.3 單一目的聚合方法

主要包括以下三個(gè):

db.collection.estimatedDocumentCount()

db.collection.count()

db.collection.distinct()

11 MongoDB 一致性

分布式系統(tǒng)有個(gè) PACELC 理論。根據(jù) CAP,在一個(gè)存在網(wǎng)絡(luò)分區(qū)(P)的分布式系統(tǒng)中,要面臨在可用性(A)和一致性(C)之間的權(quán)衡,除此之外(E),即使沒(méi)有網(wǎng)絡(luò)分區(qū)的存在,在實(shí)際系統(tǒng)中,我們也要面臨在訪問(wèn)延遲(L)和一致性(C)之間的權(quán)衡。MongoDB 的一致性模型對(duì)讀寫(xiě)操作 L 和 C 的選擇提供了豐富的選項(xiàng)。

11.1 因果一致性

單節(jié)點(diǎn)的數(shù)據(jù)庫(kù)由于為讀寫(xiě)操作提供了順序保證,因此實(shí)現(xiàn)了因果一致性。分布式系統(tǒng)同樣可以提供這些保證,但必須對(duì)所有節(jié)點(diǎn)上的相關(guān)事件進(jìn)行協(xié)調(diào)和排序。

以下是一個(gè)不遵循因果一致性的例子:

為了保持因果一致性,必須有以下保證:

Read your writes讀操作必須能夠反映出在其之前的寫(xiě)操作。

Monotonic reads如果某個(gè)讀操作已經(jīng)讀取到某個(gè)對(duì)象的某個(gè)值,那么任何后續(xù)訪問(wèn)都不應(yīng)該返回在此之前的值。

Monotonic writes如果某個(gè)寫(xiě)操作先于其它寫(xiě)操作執(zhí)行,寫(xiě)操作順序不應(yīng)受到任何其他條件打亂。

Writes follow reads如果某個(gè)寫(xiě)操作發(fā)生在讀操作之后,則該寫(xiě)操作將等待讀操作完成后進(jìn)行。

實(shí)現(xiàn)因果一致性的單號(hào)讀寫(xiě)應(yīng)遵循以下流程:

為了建立復(fù)制集和分片集事件的全局偏序關(guān)系,MongoDB 實(shí)現(xiàn)了一個(gè)邏輯時(shí)鐘,稱為 lamport logical clock。每個(gè)寫(xiě)操作在應(yīng)用于主節(jié)點(diǎn)時(shí)都會(huì)被分配一個(gè)時(shí)間值。這個(gè)值可以在副本和分片之間進(jìn)行比較。從驅(qū)動(dòng)到查詢路由器再到數(shù)據(jù)承載節(jié)點(diǎn),分片集群中的每個(gè)成員都必須在每條消息中跟蹤和發(fā)送其最新時(shí)間值,從而允許分片之間的每個(gè)節(jié)點(diǎn)在最新時(shí)間保持一致。主節(jié)點(diǎn)將最新的時(shí)間值賦值給后續(xù)的寫(xiě)入,這為任何一系列相關(guān)操作創(chuàng)建了一個(gè)因果順序。節(jié)點(diǎn)可以使用這個(gè)因果順序在執(zhí)行所需的讀或?qū)懼暗却源_保它在另一個(gè)操作之后發(fā)生。

從 MongoDB 3.6 開(kāi)始,在客戶端會(huì)話中開(kāi)啟因果一致性,保證 read concern 為 majority 的讀操作和 write concern 為 majority 的寫(xiě)操作的關(guān)聯(lián)序列具有因果關(guān)系。應(yīng)用程序必須確保一次只有一個(gè)線程在客戶端會(huì)話中執(zhí)行這些操作。

對(duì)于因果相關(guān)的操作:

客戶端開(kāi)啟客戶端會(huì)話,需滿足以下條件:read concern 為 majority,數(shù)據(jù)已被大多數(shù)復(fù)制集成員確認(rèn)并且是持久化的;write concern 為 majority,確認(rèn)該操作已應(yīng)用于復(fù)制集中大多數(shù)可投票成員。

當(dāng)客戶端發(fā)出 read concern 為 majority 的讀操作和 write concern 為 majority 的寫(xiě)操作的序列時(shí),客戶端將會(huì)話信息包含在每個(gè)操作中。

對(duì)于與會(huì)話相關(guān)聯(lián)的每個(gè) read concern 為 majority 的讀操作和 write concern 為 majority 的寫(xiě)操作,即使操作出錯(cuò),MongoDB也會(huì)返回操作時(shí)間和集群時(shí)間。

相關(guān)的客戶端會(huì)話會(huì)跟蹤這兩個(gè)時(shí)間字段。

11.2 線性一致性

線性一致性又被稱為強(qiáng)一致性。CAP 中的 C 指的就是線性一致性。順序一致性中進(jìn)程只關(guān)心各自的順序一樣就行,不需要與全局時(shí)鐘一致。線性一致性是順序一致性的進(jìn)化版,要求順序一致性的這種偏序(partial order)要達(dá)到全序(total order)。

在實(shí)現(xiàn)了線性一致性的系統(tǒng)中,任何操作在該系統(tǒng)生效的時(shí)刻都對(duì)應(yīng)時(shí)間軸上的一個(gè)點(diǎn)。把這些時(shí)刻連接成一條線,則這條線會(huì)一直沿時(shí)間軸向前,不會(huì)反向。任何操作都需要互相比較決定發(fā)生的順序。

以下是一個(gè)線性一致性的系統(tǒng)示例:

在以上系統(tǒng)中,寫(xiě)操作生效之前的任何時(shí)刻,讀取值均為 1,生效后均為 2。也就是說(shuō),任何讀操作都能讀到某個(gè)數(shù)據(jù)的最近一次寫(xiě)的數(shù)據(jù)。系統(tǒng)中的所有進(jìn)程看到的操作順序,都遵循全局時(shí)鐘的順序。

11.3 read concern

read concern 是針對(duì)讀操作的配置。它控制讀取數(shù)據(jù)的新近度和持久性。read concern 選項(xiàng)控制數(shù)據(jù)讀取的一致性,分為 local、available、majority、linearizable 四種,它們對(duì)一致性的承諾依次由弱到強(qiáng)。其中 linearizable 表示線性一致性,另外 3 種級(jí)別代表了 MongoDB 在實(shí)現(xiàn)最終一致性時(shí),對(duì)訪問(wèn)延遲和一致性的取舍。

local/available: 語(yǔ)義基本一致,都是讀操作直接讀取本地最新的數(shù)據(jù),但不保證該數(shù)據(jù)已被寫(xiě)入大多數(shù)復(fù)制集成員。數(shù)據(jù)可能會(huì)被回滾。默認(rèn)是針對(duì)主節(jié)點(diǎn)讀。 如果讀取操作與因果一致的會(huì)話相關(guān)聯(lián),則針對(duì)副節(jié)點(diǎn)讀。唯一的區(qū)別在于,avaliable 在分片集群場(chǎng)景下,為了保證性能,可能返回孤兒文檔。

majority:讀取 majority committed 的數(shù)據(jù),可以保證讀取的數(shù)據(jù)不會(huì)被回滾,但是并不能保證讀到本地最新的數(shù)據(jù)。受限于不同節(jié)點(diǎn)的復(fù)制進(jìn)度,可能會(huì)讀取到更舊的值。當(dāng)寫(xiě)操作對(duì)應(yīng)的 write concern 配置中 w 的值越大,則寫(xiě)操作在擴(kuò)散到更多的復(fù)制集節(jié)點(diǎn)上之后才返回寫(xiě)成功,這時(shí)通過(guò) read concern 被配置為 majority 的讀操作進(jìn)行讀取數(shù)據(jù),就有更大的概率讀取到最新的數(shù)據(jù)。

linearizable:讀取 majority committed 的數(shù)據(jù),但會(huì)等待在讀之前所有的 majority committed 確認(rèn)。它承諾線性一致性,要求讀寫(xiě)順序和操作真實(shí)發(fā)生的時(shí)間完全一致,既保證能讀取到最新的數(shù)據(jù),也保證讀到數(shù)據(jù)不會(huì)被回滾。只對(duì)讀取單個(gè)文檔時(shí)有效,且可能導(dǎo)致非常慢的讀,因此總是建議配合使用 maxTimeMS 使用。linearizable 只能用在主節(jié)點(diǎn)的讀操作上,考慮到寫(xiě)操作也只能發(fā)生在主節(jié)點(diǎn)上,相當(dāng)于說(shuō) MongoDB 的線性一致性被限定在單機(jī)環(huán)境下實(shí)現(xiàn)。實(shí)現(xiàn) linearizable,讀取的數(shù)據(jù)應(yīng)該是被 write concern 為 majority 的寫(xiě)操作寫(xiě)入到 MongoDB 集群中的、且持久化到日志中的數(shù)據(jù)。如果數(shù)據(jù)寫(xiě)入到多數(shù)節(jié)點(diǎn)后,沒(méi)有在日志中持久化,當(dāng)這些節(jié)點(diǎn)發(fā)生重啟恢復(fù),那么之前通過(guò)配置 read concern 為 linearizable 的讀操作讀取到的數(shù)據(jù)就可能丟失??梢酝ㄟ^(guò) writeConcernMajorityJournalDefault?選項(xiàng)保證指定 write concern 為 majority 的寫(xiě)操作在日志中是否持久化。如果寫(xiě)操作持久化到了日志中,但是沒(méi)有復(fù)制到多數(shù)節(jié)點(diǎn),在重新選主后,同樣可能會(huì)發(fā)生數(shù)據(jù)丟失,違背一致性承諾。

snapshot: 與關(guān)系型數(shù)據(jù)庫(kù)中的快照隔離級(jí)別語(yǔ)義一致。最高隔離級(jí)別,接近于 serializable。是伴隨著 MongoDB 4.0 版本中新出現(xiàn)的多文檔事務(wù)而設(shè)計(jì)的,只能用在顯式開(kāi)啟的多文檔事務(wù)中。如果事務(wù)是因果一致會(huì)話的一部分,且 write concern 為 majority,則在事務(wù)提交后,讀操作可以保證已從多數(shù)提交數(shù)據(jù)的快照中讀取,該快照提供與該事務(wù)開(kāi)始之前的操作的因果一致性。它讀取 majority committed 的數(shù)據(jù),但可能讀不到最新的已提交數(shù)據(jù)。snapshot 保證在事務(wù)中的讀不出現(xiàn)臟讀、不可重復(fù)讀和幻讀。 因?yàn)樗械淖x都將使用同一個(gè)快照,直到事務(wù)提交為止該快照才被釋放。

下面借用一張圖展示 majority 和 linearizable 的區(qū)別:

11.4 write concern

write concern?是針對(duì)寫(xiě)操作的配置,表示寫(xiě)請(qǐng)求對(duì)獨(dú)立 mongod 實(shí)例或復(fù)制集或分片集進(jìn)行寫(xiě)操作的確認(rèn)級(jí)別。它主要是控制數(shù)據(jù)寫(xiě)入的持久性。包含三個(gè)選項(xiàng):

w:指定了寫(xiě)操作需要復(fù)制并應(yīng)用到多少個(gè)復(fù)制集成員才能返回成功,可以為數(shù)字或 majority。

w:0?表示客戶端不需要收到任何有關(guān)寫(xiě)操作是否執(zhí)行成功的確認(rèn),就直接返回成功,具有最高性能。

w:1 表示寫(xiě)主成功則返回。

w: majority?需要收到多數(shù)節(jié)點(diǎn)(含主節(jié)點(diǎn))關(guān)于操作執(zhí)行成功的確認(rèn),具體個(gè)數(shù)由 MongoDB 根據(jù)復(fù)制集配置自動(dòng)得出。w?值越大,對(duì)客戶端來(lái)說(shuō),數(shù)據(jù)的持久性保證越強(qiáng),寫(xiě)操作的延遲越大。w:1?要求事務(wù)只要在本地成功提交即可,而?w: majority?要求事務(wù)在復(fù)制集的多數(shù)派節(jié)點(diǎn)提交成功。

w:all 表示全部節(jié)點(diǎn)確認(rèn)才返回成功。

j:表示寫(xiě)操作對(duì)應(yīng)的修改是否要被持久化到存儲(chǔ)引擎日志中,只能選填 true 或 false。

j:false 表示寫(xiě)操作到達(dá)內(nèi)存即算作成功。

j:true 表示寫(xiě)操作落到 journal 文件中才算成功。w:0 如果指定 j:true,則優(yōu)先使用 j:true 來(lái)請(qǐng)求獨(dú)立或復(fù)制集主副本的確認(rèn)。j:true 本身并不能保證不會(huì)因復(fù)制集主故障轉(zhuǎn)移而回滾寫(xiě)操作。

wtimeout:主節(jié)點(diǎn)在等待足夠數(shù)量的確認(rèn)時(shí)的超時(shí)時(shí)間,單位為毫秒。超時(shí)返回錯(cuò)誤,但并不代表寫(xiě)操作已經(jīng)執(zhí)行失敗。跟 w 有關(guān),比如:w 是 1,則是帶主節(jié)點(diǎn)確認(rèn)的超時(shí)時(shí)間;w 為 0,則永不返回錯(cuò)誤;w 為 majority,表示多數(shù)節(jié)點(diǎn)確認(rèn)的超時(shí)時(shí)間。

12 MongoDB WiredTiger 引擎

從 3.2 版本開(kāi)始,默認(rèn)使用 WiredTiger 存儲(chǔ)引擎,每個(gè)被創(chuàng)建的表和索引,都對(duì)應(yīng)各自獨(dú)立的 WiredTiger 表。為了保證MongoDB中數(shù)據(jù)的持久性,使用 WiredTiger 的寫(xiě)操作會(huì)先寫(xiě)入 cache,并持久化到 WAL(write ahead log),每 60s 或日志文件達(dá)到 2 GB,就會(huì)做一次 checkpoint,定期將緩存數(shù)據(jù)刷到磁盤(pán),將當(dāng)前的數(shù)據(jù)持久化產(chǎn)生一個(gè)新的快照。

12.1 WiredTiger 數(shù)據(jù)結(jié)構(gòu)

MongoDB 采用插件式存儲(chǔ)引擎架構(gòu),實(shí)現(xiàn)了服務(wù)層和存儲(chǔ)引擎層的解耦,可支持使用多種存儲(chǔ)引擎。除此之外,底層的 WiredTiger 引擎還支持使用 B+ 樹(shù)和 LSM 兩種數(shù)據(jù)結(jié)構(gòu)進(jìn)行數(shù)據(jù)管理和存儲(chǔ),默認(rèn)使用 B+ 樹(shù)結(jié)構(gòu)做存儲(chǔ)。使用 B+ 樹(shù)時(shí),WiredTiger 以 page 為單位往磁盤(pán)讀寫(xiě)數(shù)據(jù),B+ 樹(shù)的每個(gè)節(jié)點(diǎn)為一個(gè) page,包含三種類(lèi)型的 page,即 root page、internal page 和 leaf page。

以下是 B+ 樹(shù)的結(jié)構(gòu)示意圖:

root page 是 B+ 樹(shù)的根節(jié)點(diǎn)。

internal page 是不實(shí)際存儲(chǔ)數(shù)據(jù)的中間索引節(jié)點(diǎn)。

leaf page 是真正存儲(chǔ)數(shù)據(jù)的葉子節(jié)點(diǎn),包含頁(yè)頭(page header)、塊頭(block header)和真正的數(shù)據(jù)(key-value 對(duì))。page header 定義了頁(yè)的類(lèi)型、頁(yè)存儲(chǔ)的記錄條數(shù)等信息;塊頭定義了頁(yè)的校驗(yàn)和 checksum、塊在磁盤(pán)上的尋址位置等信息。真正的數(shù)據(jù)由一個(gè) WT_ROW 結(jié)構(gòu)的數(shù)組變量進(jìn)行存儲(chǔ),每一條記錄還有一個(gè) cell_offset 變量,表示這條記錄在 page 上的偏移量。WiredTiger 有一個(gè)用來(lái)為 page 分配 block 的塊設(shè)備管理模塊。定位文檔位置時(shí),先計(jì)算 block 的位置,通過(guò) block 的位置找到它對(duì)應(yīng)的 page,再通過(guò) page 找到文檔行數(shù)據(jù)的相對(duì)位置。leaf page 為了實(shí)現(xiàn) MVCC,還會(huì)維護(hù)一個(gè) WT_UPDATE 結(jié)構(gòu)的數(shù)組變量,每條記錄對(duì)應(yīng)一個(gè)數(shù)組元素,每個(gè)元素是一個(gè)鏈表,將所有修改值以鏈表形式保存。

12.2 WiredTiger 壓縮

WiredTiger 支持在內(nèi)存和磁盤(pán)上對(duì)索引進(jìn)行壓縮,通過(guò)前綴壓縮的方式減少 RAM 的使用。

12.3 WiredTiger 一致性原理

WiredTiger 使用了二級(jí)緩存 WiredTiger Cache 和 File System Cache 來(lái)保證 Disk 上 Database File 數(shù)據(jù)的最終一致性。

WiredTiger Cache:通過(guò) B+ 樹(shù)緩存未壓縮的數(shù)據(jù),并通過(guò)淘汰算法確保內(nèi)存占用在合理范圍內(nèi)。

File System Cache:由操作系統(tǒng)管理,緩存壓縮后的數(shù)據(jù)。

Database File:存儲(chǔ)壓縮后的數(shù)據(jù)。每個(gè) WiredTiger 表對(duì)應(yīng)一個(gè)獨(dú)立的磁盤(pán)文件。磁盤(pán)文件劃分成多個(gè)按 4 KB 對(duì)齊的 extent,并通過(guò) 3 個(gè)鏈表來(lái)管理:available list(可分配的 extent 列表)?,discard list(廢棄的 extent 列表)和?allocate list(當(dāng)前已分配的 extent 列表)

12.4 WiredTiger MVCC

WiredTiger 使用 MVCC 進(jìn)行寫(xiě)操作,多個(gè)客戶端可以并發(fā)同時(shí)修改集合的不同文檔。事務(wù)開(kāi)始時(shí),WiredTiger 為操作提供反映內(nèi)存數(shù)據(jù)的一致視圖的時(shí)間點(diǎn)快照。 MVCC 通過(guò)非鎖機(jī)制進(jìn)行讀寫(xiě)操作,是一種樂(lè)觀并發(fā)控制模式。 WiredTiger 僅在全局、數(shù)據(jù)庫(kù)和集合級(jí)別使用意向鎖。當(dāng)存儲(chǔ)引擎檢測(cè)到兩個(gè)操作之間存在沖突時(shí),將引發(fā)寫(xiě)沖突,從而導(dǎo)致 MongoDB 自動(dòng)重試該操作。

使用 WiredTiger,如果沒(méi)有 journal 記錄,MongoDB能且僅能從最后一個(gè)檢查點(diǎn)恢復(fù)。如果需要恢復(fù)最后一次 checkpoint 之后所做的更改,那么開(kāi)啟日志是必要的。

13 MongoDB 數(shù)據(jù)讀寫(xiě)

13.1 讀偏好 ReadPerference

默認(rèn)情況下,客戶端讀取復(fù)制集主節(jié)點(diǎn)上的數(shù)據(jù)。但客戶端可以指定一個(gè) read perference 改變讀取行為,以便對(duì)復(fù)制集上的其他節(jié)點(diǎn)進(jìn)行直接讀操作。可選值包括:

primary默認(rèn)模式。從當(dāng)前復(fù)制集主節(jié)點(diǎn)讀取?。

包含讀取操作的多文檔事務(wù)必須使用該選項(xiàng),給定事務(wù)中的所有操作都必須路由到同一個(gè)成員。

primaryPreferred在大多數(shù)情況下從主節(jié)點(diǎn)讀取,但如果不可用,則讀取從節(jié)點(diǎn)上的數(shù)據(jù)。

secondary所有操作都從復(fù)制集的從節(jié)點(diǎn)讀取。

secondaryPreferred在大多數(shù)情況下讀取從節(jié)點(diǎn)數(shù)據(jù),但如果不可用,則讀取主節(jié)點(diǎn)上的數(shù)據(jù)。

nearest讀操作會(huì)在復(fù)制集中網(wǎng)絡(luò)延時(shí)最小的節(jié)點(diǎn)上進(jìn)行,隨機(jī)從合格的復(fù)制集成員中讀取,不管是否主節(jié)點(diǎn)。

13.2 在復(fù)制集上進(jìn)行讀寫(xiě)操作

讀操作由客戶端指定的 read prefenence 選項(xiàng)決定。

所有的寫(xiě)操作都在集合的主節(jié)點(diǎn)上執(zhí)行。主節(jié)點(diǎn)執(zhí)行寫(xiě)操作并將操作記錄在操作日志或 oplog 上。oplog 是 local 數(shù)據(jù)庫(kù)的一個(gè)集合,叫l(wèi)ocal.oplog.rs。這是一個(gè)?capped collection,是固定大小,循環(huán)使用的。oplog 是對(duì)數(shù)據(jù)集的可重復(fù)操作序列,其記錄的每個(gè)操作都是冪等的,也就是說(shuō),對(duì)目標(biāo)數(shù)據(jù)集應(yīng)用一次或多次 oplog 操作都會(huì)產(chǎn)生相同的結(jié)果。從節(jié)點(diǎn)從上一次結(jié)束時(shí)間點(diǎn)建立 tailable cursor,不斷的從同步源拉取 oplog 并重放應(yīng)用到自身,且嚴(yán)格按照原始的寫(xiě)順序?qū)o定的文檔執(zhí)行寫(xiě)操作。mongodb 使用多線程批量執(zhí)行寫(xiě)操作來(lái)提高并發(fā),根據(jù)文檔 id 進(jìn)行分批執(zhí)行。MongoDB 為了提升同步效率,將拉取 oplog 以及重放 oplog 分到了不同的線程來(lái)執(zhí)行。

大致的寫(xiě)流程如下:

producer thread 不斷的從主節(jié)點(diǎn)上拉取 oplog,并把它加入到一個(gè) blockQueue 里,blockQueue 不是無(wú)限容量的,當(dāng)超過(guò)最大存儲(chǔ)容量,producer thread 就必須等到 oplog 被 replBatcher thread 從隊(duì)列里取出后才能繼續(xù)拉取 oplog。

replBatcher thread 不斷從 producer thread 對(duì)應(yīng)的 blockQueue 里取出 oplog,放到自己的內(nèi)存隊(duì)列里,內(nèi)存隊(duì)列也不是無(wú)限容量,一旦滿了,就需要等待被 oplogApplication thread 消費(fèi)。

oplogApplication thread 不斷取出 replBatch thread 內(nèi)存隊(duì)列里的所有元素,分散到不同的 replWriter thread,由 replWriter thread 根據(jù) oplog 進(jìn)行寫(xiě)操作。等待所有 oplog 都應(yīng)用完畢,oplogApplication hread 將所有的 oplog 順序?qū)懭氲絣ocal.oplog.rs 集合。

13.3 在分片集群上進(jìn)行讀寫(xiě)操作

對(duì)于分片集群,需要一個(gè) mongos 實(shí)例提供客戶端應(yīng)用程序和分片集群之間的接口。在客戶端看來(lái),該 mongos 實(shí)例的行為與其他MongoDB實(shí)例是相同的。客戶端向路由節(jié)點(diǎn) mongos 發(fā)送請(qǐng)求,由該節(jié)點(diǎn)決定往哪個(gè)分片進(jìn)行讀寫(xiě)。對(duì)于讀取操作,若能定向到特定分片時(shí),效率最高。一般而言,分片集合的查詢應(yīng)包含集合的分片鍵,以避免低效的全分片查詢。在這種情況下,mongos 可以使用配置數(shù)據(jù)庫(kù) config 中的集群元數(shù)據(jù)信息,將查詢路由到分片。如果查詢不包含分片鍵,則 mongos 節(jié)點(diǎn)必須將查詢定向到集群中的所有分片,然后在 mongos 上聚合所有分片的查詢結(jié)果,返回給客戶端。

對(duì)于寫(xiě)操作, mongos 定向到負(fù)責(zé)數(shù)據(jù)集特定部分的分片,config 數(shù)據(jù)庫(kù)上有集合相關(guān)的分片鍵信息,mongos 從中讀取配置,并路由寫(xiě)操作到適當(dāng)?shù)姆制?/p>

14 MongoDB 事務(wù)

14.1 ACID 特性

MongoDB 在一定程度上支持了事務(wù)的 ACID 特性。MongoDB 4.0 版本開(kāi)始支持復(fù)制集上的多文檔事務(wù),4.2 版本引入了分布式事務(wù),它增加了對(duì)分片群集上多文檔事務(wù)的支持。

原子性:成功提交事務(wù)時(shí),事務(wù)中所有數(shù)據(jù)更新將完全進(jìn)行成功,并在事務(wù)外部可見(jiàn)。在提交事務(wù)之前,事務(wù)外部看不到在事務(wù)中進(jìn)行的任何數(shù)據(jù)更新。當(dāng)事務(wù)被打斷或終止時(shí),事務(wù)中進(jìn)行的所有數(shù)據(jù)更新都將被丟棄,對(duì)事務(wù)外部完全不可見(jiàn)。但是當(dāng)事務(wù)寫(xiě)入多個(gè)分片時(shí),并非所有事務(wù)外的讀操作都需要等待事務(wù)提交后所有分片上數(shù)據(jù)完全可見(jiàn)。

隔離性:MongoDB 提供 snapshot 隔離級(jí)別,在事務(wù)開(kāi)始創(chuàng)建一個(gè) WiredTiger snapshot,然后在整個(gè)事務(wù)過(guò)程中,便可以使用這個(gè)快照提供事務(wù)讀。

持久性:事務(wù)使用 write concern 指定 {j: true}?時(shí),MongoDB 會(huì)保證事務(wù)日志提交才返回,即使發(fā)生 crash,也能根據(jù)事務(wù)日志來(lái)恢復(fù);而如果沒(méi)有指定?{j: true}?級(jí)別,即使事務(wù)提交成功了,在故障恢復(fù)之后,事務(wù)的也可能被回滾掉。

一致性:參考前文提到的 MongoDB 一致性。

14.2 事務(wù)的使用限制

僅 WiredTiger 引擎支持事務(wù)。

對(duì)集合的創(chuàng)建和刪除操作,不能出現(xiàn)在事務(wù)中。

對(duì)索引的創(chuàng)建和刪除操作,不能出現(xiàn)在事務(wù)中。

不能對(duì)系統(tǒng)級(jí)別的數(shù)據(jù)庫(kù)和集合進(jìn)行操作。

默認(rèn)情況下,事務(wù)大小的限制在 16 MB。

默認(rèn)情況下,事務(wù)操作整體不允許超過(guò) 60 秒。

事務(wù)不能在 session 外運(yùn)行。

一個(gè) session 只能運(yùn)行一個(gè)事務(wù),多個(gè) session 可以并行運(yùn)行事務(wù)。

不能對(duì) capped collection 進(jìn)行操作。

不能使用 explain 操作做查詢分析。

14.3 事務(wù)與 read concern

事務(wù)中的操作使用事務(wù)級(jí)別的 read concern。事務(wù)內(nèi)部忽略在集合和數(shù)據(jù)庫(kù)級(jí)別設(shè)置的任何 read concern。事務(wù)支持設(shè)置 read concern 為 local、majority 和 snapshot 其中之一。

當(dāng) read concern 為 local 時(shí),可讀取節(jié)點(diǎn)可用的最新數(shù)據(jù),但數(shù)據(jù)可能回滾。對(duì)于分片群集上的事務(wù),local 不能保證數(shù)據(jù)是從整個(gè)分片的同一快照視圖獲取。

當(dāng) read concern 為 majority 時(shí),如果在提交事務(wù)時(shí)指定了 write concern 為 majority 級(jí)別,則返回大多數(shù)副本成員已確認(rèn)的數(shù)據(jù)(即無(wú)法回滾數(shù)據(jù))。如果事務(wù)未指定 write concern 為 majority 級(jí)別,則不保證讀操作可以讀取多數(shù)提交的數(shù)據(jù)。對(duì)于分片群集上的事務(wù),不能保證數(shù)據(jù)是從整個(gè)分片的同一快照視圖中獲取。

當(dāng) read concern 為 snapshot 時(shí),如果在提交事務(wù)時(shí)指定了 write concern 為 majority 級(jí)別,則從大多數(shù)已提交數(shù)據(jù)的快照中返回?cái)?shù)據(jù)。如果事務(wù)未指定 write concern 為 majority 級(jí)別,則不保證讀操作使用了 majority commited 的數(shù)據(jù)的快照。對(duì)于分片群集上的事務(wù),snapshot 跨分片同步。

14.4 事務(wù)與 write concern

事務(wù)使用事務(wù)級(jí)別的 write concern 來(lái)進(jìn)行寫(xiě)操作提交,可以通過(guò)配置 w 選項(xiàng)設(shè)置節(jié)點(diǎn)個(gè)數(shù),來(lái)決定事務(wù)寫(xiě)入是否成功,默認(rèn)情況下為 1。

w:0 表示事務(wù)寫(xiě)入不關(guān)注是否成功,默認(rèn)為成功。

w:1 表示事務(wù)寫(xiě)入到主節(jié)點(diǎn)就開(kāi)始往客戶端發(fā)送確認(rèn)寫(xiě)入成功。

w:majority 表示大多數(shù)節(jié)點(diǎn)成功原則,例如一個(gè)復(fù)制集 3 個(gè)節(jié)點(diǎn),2 個(gè)節(jié)點(diǎn)成功就認(rèn)為本次事務(wù)寫(xiě)入成功。

w:all 表示所有節(jié)點(diǎn)都寫(xiě)入成功,才認(rèn)為事務(wù)提交成功。

j:false 表示寫(xiě)操作到達(dá)內(nèi)存就算事務(wù)成功。

j:true 表示寫(xiě)操作只有記錄到日志文件才算事務(wù)成功。

wtimeout: 寫(xiě)入超時(shí)時(shí)間,過(guò)期表示事務(wù)失敗。

15 MongoDB Change Stream

15.1 變更流使用場(chǎng)景

MongoDB 3.6 引入了 change stream(變更流)。它的使用場(chǎng)景包括:

數(shù)據(jù)同步:多個(gè) MongoDB 集群之間的增量數(shù)據(jù)同步。

審計(jì):對(duì) MongoDB 操作進(jìn)行審計(jì)、監(jiān)控。

數(shù)據(jù)訂閱:外部程序訂閱 MongoDB 的數(shù)據(jù)變更,可離線數(shù)據(jù)同步、計(jì)算或分析等。

15.2 變更流特點(diǎn)

change stream 允許外部程序訪問(wèn)實(shí)時(shí)數(shù)據(jù)更改,而不會(huì)增加 MongoDB 基礎(chǔ)操作的復(fù)雜性,也不會(huì)導(dǎo)致 oplog 延遲的風(fēng)險(xiǎn)。應(yīng)用程序可以使用 change stream 來(lái)訂閱單個(gè)集合、數(shù)據(jù)庫(kù)或整個(gè)集群中的所有數(shù)據(jù)變更。若要開(kāi)啟 change stream,必須使用 WiredTiger 存儲(chǔ)引擎。

change stream 可應(yīng)用于復(fù)制集和分片集。應(yīng)用于復(fù)制集時(shí),可以在復(fù)制集中任意一個(gè)節(jié)點(diǎn)上開(kāi)啟監(jiān)聽(tīng);應(yīng)用于分片集時(shí),則只能在 mongos 上開(kāi)啟監(jiān)聽(tīng)。在 mongos 上發(fā)起監(jiān)聽(tīng),是利用全局邏輯時(shí)鐘提供了整個(gè)分片上變更的總體排序,確保監(jiān)聽(tīng)事件可以按接收到的順序安全地解釋。mongos 會(huì)一直檢查每個(gè)分片,查看每個(gè)分片是否存在最新的變更。如果多個(gè)分片上一直很少出現(xiàn)變更,則可能會(huì)對(duì) change stream 的響應(yīng)時(shí)間產(chǎn)生負(fù)面影響,因?yàn)?mongos 仍必須檢查這些冷分片保持總體有序。

15.3 變更流監(jiān)聽(tīng)事件類(lèi)型

從 change stream 中能監(jiān)聽(tīng)到的變更事件包括:insert、update、replace、delete、drop、rename、dropDatabase 和 invalidate。

15.4 變更流故障恢復(fù)

MongoDB 4.0 之后,可以通過(guò)指定 startAtOperationTime 來(lái)控制從某個(gè)特定的時(shí)間點(diǎn)開(kāi)啟監(jiān)聽(tīng),但該時(shí)間點(diǎn)必須在所選擇節(jié)點(diǎn)的有效 oplog 時(shí)間范圍內(nèi)。change stream 監(jiān)聽(tīng)返回的字段中有個(gè) _id 字段,表示的是 resume token,這是唯一標(biāo)志 change stream 流中的位置的字段。

如果 change stream 監(jiān)聽(tīng)比中止后需要繼續(xù)監(jiān)聽(tīng),那么可指定 resumeAfter 恢復(fù)訂閱。指定 resumeAfter 為 change stream 中斷處的 _id 字段即可。

當(dāng)監(jiān)聽(tīng)的集合發(fā)生 rename、drop 或 dropDatabase 事件,就會(huì)導(dǎo)致 invalidate 事件;當(dāng)監(jiān)聽(tīng)的數(shù)據(jù)庫(kù)出現(xiàn) dropDatabase 事件,也會(huì)導(dǎo)致無(wú)效事件。invalidate 事件后 change stream 的游標(biāo)會(huì)被關(guān)閉,這時(shí)就需要使用 resumeAfter 選項(xiàng)來(lái)恢復(fù) change stream 的監(jiān)聽(tīng),在 4.2 版本后也可以通過(guò) startAfter 選項(xiàng)創(chuàng)建新的更改流來(lái)恢復(fù)監(jiān)聽(tīng)。

15.5 變更流使用限制

change stream 無(wú)法配置到系統(tǒng)庫(kù)或者 system.xxx 表上。

change stream 依賴于 oplog,因此中斷時(shí)間不可超過(guò) oplog 回收的最大時(shí)間窗。

16 MongoDB 性能問(wèn)題定位方式

可以為 mongod 實(shí)例啟用數(shù)據(jù)庫(kù)分析。數(shù)據(jù)庫(kù)分析器既可以在實(shí)例上啟用,也可以在單個(gè)數(shù)據(jù)庫(kù)層面上啟用。它收集在實(shí)例上執(zhí)行的 CRUD 操作、游標(biāo)、命令、配置等詳細(xì)信息,并將它收集的所有數(shù)據(jù)寫(xiě)到 system.profile 集合。這是一個(gè)capped collection,默認(rèn)情況下,system.profile 容量大小為 4M。開(kāi)啟實(shí)時(shí)數(shù)據(jù)庫(kù)分析往往伴隨著副作用,請(qǐng)謹(jǐn)慎使用。

使用 db.currentOp() 操作。它返回一個(gè)文檔,其中包含有關(guān)數(shù)據(jù)庫(kù)實(shí)例正在進(jìn)行的操作的信息。

使用 db.serverStatus() 命令。它返回一個(gè)文檔,提供數(shù)據(jù)庫(kù)狀態(tài)的概述,通過(guò)它可以收集有關(guān)該實(shí)例的統(tǒng)計(jì)信息。

使用 explain 來(lái)評(píng)估查詢性能,例如 cursor.explain() 或 db.collection.explain() 方法可以用來(lái)返回關(guān)于查詢執(zhí)行的信息。

借用一些商業(yè)工具,比如 MongoDB Ops Manager、Percona 等。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、簡(jiǎn)介 MongoDB 是一款流行的開(kāi)源文檔型數(shù)據(jù)庫(kù),從它的命名來(lái)看,確實(shí)是有一定野心的。MongoDB 的原名...
    raysonfang閱讀 661評(píng)論 0 7
  • WiredTiger 是一個(gè)開(kāi)源的、高性能、可伸縮的 MongoDB 數(shù)據(jù)存儲(chǔ)引擎。 SSPL協(xié)議是只對(duì)使用云廠商...
    Bogon閱讀 1,862評(píng)論 0 1
  • 今天簡(jiǎn)單寫(xiě)一下mongo4.0后支持的事務(wù)https://docs.mongodb.com/manual/core...
    supremecsp閱讀 1,466評(píng)論 0 1
  • 1、MongoDB特點(diǎn) 面向集合存儲(chǔ):MongoDB 是面向集合的,數(shù)據(jù)以 collection 分組存儲(chǔ)。每個(gè) ...
    彳亍口巴閱讀 546評(píng)論 0 1
  • 因果一致性要解決的問(wèn)題(Causal Consistency and Read and Write Concern...
    北海北_6dc3閱讀 594評(píng)論 0 0

友情鏈接更多精彩內(nèi)容