MongoDB 3.2 RC 的新玩意兒

MongoDB 3.2 很快就要推出了,稍微整理了一下究竟有哪些新東西。這篇主要是針對官方 Release Note 的理解與整理,細節請猛擊原文

預設使用 wiredTiger 引擎

MongoDB 在 3.0 版時第一次引入的 wiredTiger 引擎,在 MongoDB 3.2 中已經被作為預設引擎來使用了。在新的 3.2 版中,如果用戶沒有指定引擎,則 MongoDB 會自動選擇使用 wiredTiger 作為底層存儲引擎。當然,僅限於 dbpath 為空的時候。

如果 dbpath 中已經存有資料,當然不可能強行使用不合規格的引擎,這時系統會自動偵測底層資料夾內容究竟對應到哪種引擎,並自動選用。

看來升級時應該會蠻輕鬆的。

全文搜尋支援簡繁中文。

註:【需 MongoDB 企業版】

從大約 2.6 開始,MongoDB 就開始(撈過界地)內建了全文檢索索引機制。簡單地說,就是支援 google 的那種檢索方式--只要打幾個關鍵字,就能把所有相關內容取出來。

但是很可惜,以上這種好物不支援中文,因為中文分詞算法比英文難上百倍(英文要分詞原則上只要找空格即可),看得見吃不著。

現在企業版終於提供了一個可支援中文的框架了,但尚需要引入一個其他公司的,名叫 RLP (Rosette Linguistics Platform) 的私有專案,目前看來除 MongoDB 企業版外還需向其他公司另買 License 才行。

細節見:https://docs.mongodb.org/manual/release-notes/3.2-text-search-enterprise/

儲存引擎加密。

註:【需 MongoDB 企業版】

簡單說來,就是硬碟中放置的內容能被加密。就算硬碟被偷或是被有關部門幹走,重要資料也不會外洩這樣,在有關部門特別猖獗的地區格外有用。當然用戶得把 Key 藏好。

Key 分「系統 Key」與「資料庫 Key」。系統 Key 控制其他資料庫 Key。系統 Key 預設是不會存在同一台 MongoDB 機器之上。

預設採用由 openssl 實作的 AES256CBC 對稱金鑰算法。

讀關注特性(readConcern)

增加了一個名叫 majority 的讀關注等級……

註:讀關注(readConcern)和以前就有的 readPreference 完全無關,是不同的概念

關於這個東西,官方的說法實在太過簡潔。我的理解是這樣:

過去,MongoDB 在進行讀取的時候,最安全的讀取策略 (readPreference) 是「設為從 Primary 節點讀取」。因為資料都是由 Primary 寫入的,所以只要從 Primary 讀取,基本上都能讀到最新的資料,而這份資料也會隨時間分散到其他地方,換句話說,這能保持最高一致性。

然而,仔細思考就會發現,這中間還有一個弱點。


舉個例子,以下是正常的操作步驟:

  1. Primary 中存在一份資料 A、Secondary 也存在一份資料 A
  2. Primary 修改 AA’、Secondary 依然存著 A
  3. Client 將 Primary 中的 A’ 讀走。
  4. Primary 存在一份資料 A’、Seconday 複製 Primary 內容,將 A 改為 A’
  5. Client 將 Primary 中的 A’ 讀走。與上次讀取的內容相同。

  

現在假設在 3 與 4 中間,Primary 掛掉了。

  1. Primary 中存在一份資料 A、Secondary 也存在一份資料 A
  2. Primary 修改 AA’、Secondary 依然存著 A
  3. Client 將 Primary 中的 A’ 讀走。
    1. Primary 在這時掛掉了。
    2. 原 Secondary 升級為 Primary。內含資料 A
  4. (沒法執行) Primary 存在一份資料 A’、Seconday 複製 Primary 內容,將 A 改為 A’
  5. Client 將 (從 Secondary 升上來的) Primary 中的 A 讀走。與上次讀取的內容不同。

看到了嗎!第一次明明已經讀出了新版 A’ 資料,但第二次讀取時,資料居然又被回滾回舊版 A!(一致性死去哪裡了?)

這是個很容易被忽略的問題點。

  

readConcern: majority 的作法是同時讀取半數以上節點的資料,當確定半數以上的資料都一致的時候,就會回傳那個結果。一言以蔽之,這可以讓客戶端讀取時只承認「不會被回滾的資料」,這是一種讀取時的隔離機制。

簡單地說,這個功能增強了 MongoDB 的一致性與可靠性。應該可以解決某些用戶碰上的掉資料問題--這和 writeConcern 企圖解決的問題並不一致。

當然,這會使讀取效能稍微降低,因為這樣一來就表示必須要同時從多個節點中進行讀取了。

可自定義 Primary 的切換速度。

簡單說來,發現 Primary 失效以及失效切換的速度,如今可以自行設值決定了。

如果將 Heartbeat 頻率調高,同時存在多個 primary 的時間也有望縮短與減少。

不過代價顯然是網路負擔會增加。此外如果敏感性設太高,我擔心可能會導致 Heartbeat 響應速度稍慢一點就會立刻觸發 Primary 切換。哪怕原本的 Primary 還沒真的死掉也會切換。也許會造成 Primary 在多個伺服器間連續震盪跳躍的問題。之後還要測試一下這個部分。

config servers 底層同步算法更新。

MongoDB 的 Config Servers,在 Sharding Cluster 中扮演著三機一體的 Metadata 伺服器角色。三台 Config Servers 互相備援,確保所有 metadata 不會遺失,且能保持一致。

不過這種備援過去並不是由 MongoDB 內建的 Replica Set 架構所組成的,而是官方強行手刻一套二步提交的機制來完成。大概官方也覺得過去的 Replica Set 在一致性實作上有瑕疵吧?所以不敢用在像 Config Server 這麼關鍵的任務上。不過我猜這次大概是實作了 readConcern: majority 後問題解決,config servers 的底層終於被改為 Replica Set 架構了。

表面上的最大差異,就是終於可以佈署三台以上的 config server。MongoDB Shard Cluster 叢集從此不再有結構上的核心弱點。

另外,官方提及新的 config server 需強制採用 wiredTiger 引擎,升級可能會費點周折。得再看看。

引入 Partial Indexes。

Partial Indexes 這種在傳統資料庫中蠻常見的東西也進入 3.2 了,聽說被部份用戶催討了好久終於生出來。

總之簡單說下原理:用戶可以指定 Collection 中的 Document,只有在滿足某些條件時,才會建立某個特定的索引。舉例來說,可以設定成當 user{age : {$gte: 18}} 時,才對 username 建立索引。

這樣做好處當然有很多,比方說省去寫入所需的效能損耗與硬碟空間消耗等,但最大的好處(在我看來)大概還是節約了記憶體佔用空間。實際佈屬過就知道,MongoDB 蠻吃記憶體的,而且百分之九十都是被索引搶走,簡直強盜。有了 Partial Indexes 後,應該更能把記憶體用在刀口上。

唯一需要注意的是,如果運用一個索引可能得到不完整的結果,則這個索引會被拒絕使用。這種說法是不是會想到 sparse index 呢?其實官方說 sparse index 正好是 partial indexes 的一種特例狀況。細節不囉嗦太多,想想應該就能想通。

Schema 驗證。

嗯哼哼,好東西出現了(蓋章)。

MonogDB 是 Schemaless 的資料庫,應用程式開發者想要新增或調整一個欄位,不需要預先登記劃位,也不需要清洗資料庫表格,開發初期自然愉快又開心。然而過沒多久,狀況就大不相同了。

亂七八糟的資料欄位,根本沒法進行優化,查詢效能當然很差。最糟的時候,就連應用開發者都不知道自己或同事曾經在哪個欄位中加入過什麼鬼東西。欄位 A 究竟是陣列還是單值?NaN 為啥會出現在應該是字串的欄位裡面?面對無限增殖的例外狀況,日後開發與維運的難度都會暴增。

當然啦,這就像維護編碼風格一樣,只要開發時確保紀律,資料結構也不見得一定會變得亂七八糟。但有個強制的規範,讓開發者在搞錯時立刻得到一個錯誤,顯然更加可靠些。這裡提到的 Schema 驗證功能就是在專門搞這個的。

說得更具體一點,用戶可以在創建 Collection 時定義規範,並在每次插入/更新 Document 時進行檢查。如果新的 Document 沒有滿足規範,則依照用戶的設定,可以在 Log 中留下警告紀錄但完成操作,也可以立刻報錯並拒絕更新。以上這些規範也能在日後被修改、刪除、添加,算是蠻有彈性的。順便一提,規範的寫法和查詢語句的寫法幾乎完全一致(有少許例外),因此彈性基本上也是同一等級,不光是定義「欄位存不存在」、「類別為何」這些條件,像是 {age: {$gte: 0}} 等這種條件也都寫得出來。

有了規範之後,AP 撰寫者不可能再失手/亂搞插入非預期的資料。要把資料結構規範到什麼程度是可以自定義的。彈性不會因此流失,但系統複雜性變得更可控。

Aggregate 分析能力提升。

這次在聚合運算的部分加入的東西不少。

$lookup 運算子

註:需企業版。

在聚合運算時支援新的 $lookup 運算子,這可橋接多個 collection 的資料。

簡單地說,過去在 MongoDB 中進行 Aggregate 運算時,資料來源會被限制在同一個 Collection 中,運算時沒可能去找其他 Collection 中的資料。一旦面臨要把多個 Collection 中的資料整合起來分析時,那就只能投降。打個比方:想要分析汽車引擎馬力數與用戶年齡層的關係,但汽車資料與用戶資料分別存放在兩個 Collection 裡,分析就沒法進行。

這是分析時所欠缺的重大缺口。不過現在終於是補上了。可惜是企業版特性,不付錢用不上。

其他

除此之外聚合框架中還加了兩個新的 stage 類型,以及一海票二十個左右的新運算子。包括標準差運算子與各種陣列處理運算子等。部分原有的運算子使用也變得更有彈性了些。這些就不需要企業版也能用了。

叢集架構方面,新的 Aggregate 框架減少了主分片的負擔。整體說來運算更加平行化。

本機備份工具增強。

內建的 mongodump 和 mongorestore 工具終於支援了資料壓縮 + 歸檔功能。

作為開發者感覺是還不錯。之前 dump 出來的資料散成一堆資料夾,雖然也不是不行,但實在讓人感到困惑,直接歸檔成單一檔案處理起來直觀很多。雖然用外部工具也能搞定啦,但內建最方便了。

我還在等增量備份的功能,甚麼時候才會出現呢……

轉用 SpiderMonkey Javascript 引擎。

讓跨平台更容易,並佔用更少記憶體,效能也稍有提升的樣子。

之前似乎是因為使用的 V8 javascript 引擎對硬體架構限制較大,搞得 mongoDB 跨平台比較麻煩。

我自己先前有試著在 Raspberry Pi 上面編譯 2.6 怎樣也過不去,希望更動後能讓 porting 更容易一些。順便一提,我猜這個改動與 MongoDB 公司開始與 IBM 合作有關。也許未來可以期待 Power / Zsystem 版的官方支援。

對一般用戶來說,變更 js 引擎應該是完全感覺不出來的。除非用戶使用了某些 V8 引擎的專屬特性。