直播分享| 騰訊雲 MongoDB 智能診斷及性能優化實踐

本次直播分享主要分爲五個部分展開:

第一部分:主要介紹 MongoDB 的核心優勢;

第二部分:主要總結雲上 MongoDB 用戶常見的一些問題;

第三部分:介紹騰訊雲 MongoDB 智能索引推薦實現流程及其實現原理;

第四部分:介紹騰訊雲 MongoDB 內核 SQL 限流功能及其實現;

第五部分:主要分享騰訊雲 DBbrain for MongoDB 的兩個典型診斷案例

MongoDB 有哪些核心優勢?

MongoDB 是一個基於分佈式文件存儲的數據庫,由 C++ 語言編寫。首先,我們來看下它有哪些核心優勢,下面列舉幾個:

分佈式

MongoDB 是開源的分佈式數據庫,可以解決傳統數據庫存儲容量上的瓶頸問題,用戶不必再提前考慮分庫分表等操作。同時,MongoDB 也是一個天然高可用的數據庫,比如在一主兩從的工作模式下,當主節點意外宕機,從節點就會接替主節點的工作,整個過程無須依賴任何第三方組件。

lschema-free

MongoDB 的表結構相對自由,添加字段方便快捷,相比於傳統數據庫在一張大表裏添加字段,運維成本被大大降低。

高性能

MongoDB 早期使用 MMAPv1 存儲引擎,後來替換爲 WiredTiger 存儲引擎,它支持行級粒度鎖、熱點數據緩存等特性,這給 MongoDB 帶來了高性能、低延遲、高吞吐等能力。

高壓縮比

在默認配置下,MongoDB 使用 snappy 壓縮算法,可達到平均2到4倍的文本數據壓縮能力,如果使用 zlib 壓縮算法則可以提升到3至7倍,但是 zlib 對性能有一定影響,因此線上通常使用默認配置即可。經測試,在默認配置的情況下,同樣一份數據寫入 MongoDB、MySQL、ES 的真實磁盤消耗比約爲1 : 3 : 6。

完善的客戶端訪問策略

MongoDB 支持五種均衡訪問策略:

primary:讀主節點,當主節點異常時,可能造成短期內的業務異常。

primaryPreferred:主節點優先,當主節點異常時可以讀從節點。

secondary:讀從節點,把流量平均衡分配給多個從節點,實現負載均衡。

secondaryPreferred:從節點優先,如果從節點異常,則讀取主節點。

nearest:就近訪問,在多機房場景下,就近訪問可以避免出現跨機房訪問的情況。

騰訊雲 MongoDB 核心優勢

騰訊雲 MongoDB 當前已服務於遊戲、電商、社交、教育、新聞資訊、金融、物聯網、軟件服務、汽車出行、音視頻等多個行業。

相比用戶自建 MongoDB 數據庫,騰訊雲 MongoDB 在智能運維、可用性、安全性、性能等方面更具優勢。同時通過 DBbrain 提供一站式監控的診斷分析,並且能給出相應的優化建議,同時也集成了官方的常用工具,讓用戶使用更方便。

此外,騰訊雲 MongoDB 還在內核裏做了一些定製化的開發,比如解決表數量達到百萬級別時的性能問題、提供 SQL 限流功能減少流量過大導致的集羣不可用問題。安全方面,騰訊雲 MongoDB 可以將數據恢復到7天內的任意時間點,並且提供24小時的專業支持服務。除此之外,也天然集成了雲上高可用、高性能等通用能力。

雲上 MongoDB 集羣常見問題

3.1. 分片集羣使用問題

雲上分片集羣經常遇到的問題如上,主要包括大表不啓用分片、分片方式不是最優、片建選擇不當、高峯期沒有設置 balance 窗口集羣抖動。

大數據量大流量表不啓用分片

有些用戶有錯誤認識,就是從副本集切到多分片集羣,認爲啥也不做,集羣天然性能就是副本集好幾倍,存儲容量默認是副本集幾倍。

分片集羣如果不啓用分片功能,數據和流量默認都會到主分片,也就是分片集羣中的一個分片,因此大數據量、大流量集羣切記啓用分片功能。

分片方式

通常情況,如果用戶主要是點查,例如按照訂單id查詢,則可以選擇 hash 分片方式,這樣除了保證讀取性能同時,同時確保數據離散寫入不同分片,保證了寫入性能並避免了數據不均衡引起的大量 moveChunk 操作。

如果用戶查詢主要是範圍查詢,一般建議採用範圍分片方式。

如果既有大量點查,又有大量範圍類查詢,爲了避免範圍查詢引起的所有分片廣播查詢,因此建議範圍分片,這樣點查和普通範圍查詢都可以從一個分片獲取數據。

片建選擇

分片集羣片建通常選擇高頻類查詢字段作爲片建字段,同時注意 insert、update 等寫入操作一定要帶上片建字段,否則 mongos 會返回異常信息,因爲不知道該去寫操作那個分片的數據。

balance 窗口設置

分片集羣抖動很多都和 moveChunk 相關,moveChunk 操作會增加鎖、資源消耗,同時涉及路由刷新等流程,因此建議分片集羣設置 balance 窗口期,儘量業務低峯期進行balance 操作。

分片方式和片建選擇比較特殊,和業務使用方式關係密切,因此需要提取評估,確保讀寫性能最優。

3.2. 索引問題

索引問題注意包括索引操作過程問題和索引內容問題,下面進行詳細說明。

索引操作過程不合理

以副本集添加索引爲例,createIndex 創建索引成功實際上在主節點成功後就返回了,從節點還沒有添加索引成功。如果用戶做了讀寫分離,並且從節點壓力比較大,這時候可能從節點執行索引時間會更長,如果用戶主節點執行成功後,createindex 返回立馬又添加其他索引,這時候可能存在多個索引在從節點執行的情況,這樣從節點壓力會很大。

此外,用戶如果添加一個索引“成功”,這時候從節點實際上還在執行該索引,用戶立馬刪除該表某個索引,這時候從節點會不可訪問,因爲刪除索引會添加 MODE_X 排它鎖。

如果業務非常核心,不允許任何抖動,還可以使用通過滾動添加索引方式來添加索引,具體操作步驟詳見:

https://www.mongodb.com/docs/manual/tutorial/build-indexes-on-replica-sets/

https://www.mongodb.com/docs/manual/tutorial/build-indexes-on-sharded-clusters/

MongoDB 智能索引推薦實現

智能索引推薦主要是基於索引規則和代價估算來實現的,整體架構如下:

智能索引推薦分爲四個模塊:

agent模塊:實時收集 mongod 節點日誌,然後輸出日誌到kafka模塊。

kafka模塊:存儲 mongo 節點收集的日誌信息。

日誌分類模塊:kafka 有用慢日誌收集,並進行分類處理。

代價估算模塊:模擬 MongoDB 內核執行計劃過程,進行候選索引代價估算。系統整體的工作流程如下:每個 MongoDB 節點上佈署有 agent 節點,它會實時採集所有有用的日誌並存儲到 Kafka 裏,日誌分類處理模塊會從 Kafka 裏把需要的日誌提取出來進行分類,然後把分類好的日誌交給索引代價計算模塊進行計算,最終得到最優索引。

其中,agent 模塊和 kafka 模塊的邏輯相對簡單,這裏重點介紹日誌分類模塊和代價估算模塊。

4.1. 日誌分類模塊的實現步驟

第一步:提取有效的慢日誌。

並不是所有的慢查詢日誌都需要處理,只需提取存在索引問題的慢查詢,諸如索引不是最優、全表掃描,這類日誌才需要提取。如果判斷索引不是最優?

答案是對比走索引時候數據掃描的行數與實際返回數據的行數,如果差值較大,就判斷這個索引不是最優的,需要進一步優化。

第二步:根據 filter 對 SQL 分類。

同一個庫表中會有很多查詢,查詢條件不盡相同,屬於同一類的 SQL 需要滿足幾個條件,即庫、表、命令、查詢條件完全相同。前三個條件容易區分,比如同庫同表情況下,查詢條件(包括 find 、update 、delete 等)相同算一類,而查詢條件相同的前提是查詢關鍵字要相同且操作符屬於同一類,同時要忽略查詢字段順序。

日誌聚合處理

定期從 DB 中獲取分類好的 SQL 信息交給代價估算模塊進行處理。

4.2. 索引代價計算模塊處理流程

抽象語法樹生成及分解:從日誌分類處理模塊中獲取對應 SQL,抽象語法樹,同時進行分解。

根據索引規則生成候選索引:根據最優候選索引規則生成侯選索引的流程,可以參考騰訊雲數據庫公衆號發佈的文章:《雲上MongoDB常見索引問題及最優索引規則大全》一文。

對候選索引進行代價估算:從源集羣隨機抽樣數據,對候選索引進行代價估算,最終得出最優索引。下面重點介紹候選索引代價計算過程。

4.3. 候選索引代價計算

代價計算主要步驟:

隨機採樣少量數據:從線上實例隨機採樣少量數據,爲了儘量減少對線上集羣影響,優先採集隱藏節點,如果沒有隱藏節點則採集從節點。同時控制採樣數據量和採樣頻率,盡力減少對線上集羣影響。

獲取每個字段區分度:根據採樣的數據獲取查詢條件對應字段的區分度。

根據裁剪後的子樹按照索引規則生成候選索引:這裏可以參考騰訊雲數據庫公衆號輸出的 MongoDB 索引規則大全。

每個候選索引代價計算:對每個候選索引模擬內核執行計劃流程確定計算代價成本。

假設有 [{work:1, city:1, province:1}, {city:1, province:1, age:1}]) 這個候選索引,其代價計算過程如下圖所示:

上面的候選索引對應的執行計劃流程是:如果查詢選擇該候選索引執行,其執行計劃首先進入 index scan stage,然後進入 OR stage,OR stage 執行完成後就會開始做fetch操作,最後就會得出整個流程掃描了多少行數據、得到了多少行數據,以及整個流程的執行時間。

騰訊雲的代價估算是由一個旁路模塊實現的,實現難度較大,需要對整個內核執行計劃有較透徹的理解。所以對於自研用戶,如果研發人力有限,可以採樣數據到新的 MongoDB 集羣,根據候選索引規則,同時藉助內核已有的能力進行字段區分度、候選索引代價計算,最終得出執行這個索引掃描了多少行、返回了多少行、執行了多長時間,最終也可以得到最優索引。

智能索引推薦當前已經服務化,逐步會開放給用戶使用,如果大家有興趣可以去體驗。索引推薦基本上能在半小時內發現實例上存在的索引問題,除了推薦最優索引外,還可以把實例上的無用索引和重複索引也找出來,這樣能用最少的索引滿足用戶需求,性能等方面也會更好。

4.4. 騰訊雲MongoDB索引推薦總結

快:慢查產生半小時左右推出最優索引

準:推薦索引爲候選索引中代價計算最小的索引

穩:採樣計算過程對雲上集羣影響較小,索引添加過程增加保護措施,同一實例同一時刻最多同時添加一個索引。

MongoDB 內核 SQL 限流實現

5.1. 爲什麼要做SQL限流?

首先,我們先思考這樣一個問題:爲什麼要做SQL限流?

一方面,當流量過大、負載過高,數據庫抖動可能造成雪崩時,就可以限制一下流量,保證一些請求能正常返回。另一方面,有些用戶爲了節約成本,將多個用戶的數據寫到了同一個實例不同表中,某一時刻可能出現用戶新上的接口不對或其它異常情況,導致流量非常高,就會影響這個實例上的其他核心業務,這時就可以通過限流對異常或者不太重要的表做限流處理,保證核心的業務流量能正常訪問。此外,還有一些突發掃表,高危操作等都可以通過限流進行限制。

5.2. 內核哪個位置增加限流功能?

那麼,我們在內核哪個地方做了SQL限流功能呢?

先捋一下 MongoDB 的整體架構,它是分層的,第一層是網絡收發模塊,網絡收發過後,交由命令處理模塊把這些 SQL 解析出來,然後這些 SQL 會進入查詢引擎模塊、讀寫模塊以及併發控制模塊等流程。

5.3. SQL 限流核心實現

我們整個 SQL 限流模塊是加在命令處理模塊之後的,加在這裏有什麼好處呢?因爲在這裏已經拿到了詳細的 SQL ,並且在併發控制之前做到 SQL 限流,避免 SQL 限流裏面的操作會影響併發控制和數據庫讀寫訪問,防止與下層的併發控制模塊產生衝突。

內核 SQL 限流的整體流程如下:

首先,在 DBbrain 界面上可以配置策略規則,比如 SQL 類型、併發數,可以配置定時關閉還是手動關閉,定時關閉是指最多運行多少時間,手動關閉就是打開後一直執行,除非人爲手動關閉停止。

然後,是根據讀寫 SQL 關鍵字,配置完規則,就可以對指定庫、表或者指定的 SQL 語句做限流。整個流程是首先在 DBbrain 的控制檯下發規則,以分片集羣爲例,就下發給分片集羣的 config server,config server 接收到後把這個規則寫到 config server 的一個表裏,shard server 的每個 mongod 定期從 config server 獲取這些規則並加載到自己內存裏,所有的 mongod 節點內存裏就會有完整的規則數據存在,當發起一個請求,通過客戶端到代理,再到 mongod 節點的時候,進行限流規則匹配,觸發限流操作。

至於爲什麼選擇在 mongod 上做限流,而不是在 mongos 上。主要是因爲,mongos 上面的流量控制是由客戶端基於 IP 做哈希的,可能導致流量不均勻。此外,線上有副本集的集羣,也有分片集羣,在mongod上做可以實現代碼統一。在 mongos 上做限流,因爲 mongos 之間是無狀態的,無法保證相互的控制達到一定的級別。最後,瓶頸一般都在 mongod 節點上,所以就選擇在 mongod 上面做限流。

5.4. SQL 限流規則及規則匹配限流流程

下面繼續分享騰訊雲 MongoDB SQL 限流的限流規則和規則匹配限流流程。

限流規則:

至於 SQL 限流規則主要包含哪些信息,主要包括 SQL 類型(比如增刪改查)、限流時間以及併發數,併發數可以限制某類請求同時訪問我們 DB 的併發量,另外就是關鍵字,可以匹配庫,也可以匹配表,甚至可以匹配詳細的 SQL,這樣就可以實現指定的庫、表和某一類型的 SQL 限 流。

請求匹配規則流程:

當一個請求到達 MongoDB 後,具體的處理流程是,先看這個實例是否啓用了 SQL 限流功能,如果已啓用,則提取用戶請求中的庫、表和 SQL 關鍵字信息,下一步和配置的限流規則做匹配,判斷這類 SQL 是否有可用的 ticket。

ticket 代表併發控制中的併發數,如果沒有可用的 ticket ,比如 ticket 值爲0,就直接限制這個請求,返回客戶端異常。如果有可用的 ticket ,就把 ticket 值減1,同時訪問 DB ,訪問到 DB 後就將數據返回給客戶端,同時釋放當前 ticket ,後面的請求就可以繼續複用,這就是整個限流工作流程。

SQL 限流體驗如下:

智能診斷案例分享

(路由問題、排它鎖問題)

以下內容是在 MongoDB 社區分享的兩例典型案例,如果踩坑後果非常比較嚴重,因此這裏單獨一節分享。

6.1. 路由異常診斷優化

mongos 1觸發 chunk [1-50}從分片2遷移到分片1,整個遷移過程 mongos 1、分片2及分片1都能感知到這個事件,因此他們都有最新的路由信息。然而, mongos 2、 mongos 3和分片0感知不到這個事件,因此還是老的路由信息,認爲 chunk [1-50}還是在分片2,實際上數據已經遷移到了分片1。

由於客戶端讀走從節點,mongos 1收到例如 xx =20的請求後,查詢內存中路由信息,數據在分片1,因此從分片1從節點獲取數據,由於 chunk [1-50}對應的數據全在分片1,因此可以訪問到數據。

由於從節點默認不會做路由版本檢測,因此當 mongos 2或者 mongos 3訪問 xx =20的數據,該數據路由記錄 chunk [1-50}在分片2,由於數據已經從分片2遷移到分片1,分片2實際上已經沒有該數據了,因此訪問不到數據。

優化方法:

暴力優化方法:db.adminCommand({“flushRouterConfig”:1}) ,強制路由刷新,存在性能抖動。該操作會情況內存緩存的所有路由信息,新請求進來後需要從 config server 獲取所有 chunks 信息加載到本地內存,如果 chunks 較多,整個過程 mongos 訪問不可用。

騰訊雲優雅優化方法:實時檢測每個 mongos shardVersion 主版本號,如果存在差異,則版本較低的 mongos 主動訪問一次每個分片主節點,確保 mongos 從 config server 獲取最新增量路由信息。這個過程只會從 config server 獲取差異的 chunks 信息,因此影響較小。

6.2. MODE_X 排它鎖檢測

很多高危操作會添加排它鎖,會引起庫表維度,甚至是整節點維度阻塞不可用,例如下面這些操作都會添加 MODE_X 獨佔鎖:

①.表加索引過程中刪除索引

②.前臺加索引

③.表重命名

④.索引重構

⑤. ……

MODE_X 排他鎖檢測方法:

定期實時獲取 lockInfo ,獲取其中的排它鎖對應 DDL 操作,實時同步用戶。

關於作者:楊亞洲

MongoDB 中文社區成員 ;

現任騰訊雲數據庫專家工程師 ;

前滴滴出行技術專家;

前 OPPO 文檔數據庫研發負責人;

一直專注於分佈式緩存、高性能服務器、分佈式存儲、數據庫、中間件等相關研發。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章