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

語言: CN / TW / HK


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

  • 第一部分:主要介紹 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 文件資料庫研發負責人;

一直專注於分散式快取、高效能伺服器、分散式儲存、資料庫、中介軟體等相關研發。