MongoDB 3.4 Sharding 觀念、安裝、與配置

Sharding 為 MongoDB 所擁有的一種資料分散處理架構,簡單的說就是將資料分片 (Shard) 儲存到不同的機器中,最常應用在大數據的案例上。在海量資料的儲存情境上,垂直擴充架構是無法滿足的,必須透過水平擴充來實現。

MongoDB Sharded Cluster 基本的架構示意如下:

在這樣的機制下,儲存進 Collection 的資料會被盡可能平均地分散到每一個 Shard 上,每一個 Shard 都是由獨立的 mongod 實體或者 Replica Set 所構成 (想了解 Replica Set 可以看看之前的文章)。上圖我們也可以看到,Sharding 機制本身是不負責備份的,在產品上線的環境中,強烈建議使用 Replica Set 來搭建 MongoDB Sharded Cluster。在這個架構中,假設我們有 1TB 的資料,可以透過 Sharding 機制將資料切分為四個 Shard,每一個 Shard 負責儲存 256G,應該很好理解。

MongoDB Sharded Cluster 運作機制

那麼 MongoDB Sharded Cluster 是如何運作的呢?我們先看看下面的架構圖:

當使用者或應用程式 (Driver) 要操作 MongoDB Sharded Cluster 時,是透過 mongos 來進行連接,mongos 扮演 Router 的角色,Router 通常由多個實體組成,可以將 Loading 分散處理,保持高可用性 (HA)。對於應用程式來說,並不需要瞭解 MongoDB Sharding 怎麼運作的,他們看到的只是一個資料庫,所以使用上基本不會有太大的差別。如下所示:

之前提到,雖然每一個 Shard 都是由一個 mongod 或 Replica Set 構成,但如果沒有透過 mongos (Router) 進行連線操作,而直接對 Shard 進行連線,那麼你看到 Collection 的資料就不會是完整的集合,而只是單一個 Shard 的資料。那麼 Cluster 是如何分配資料呢,這裡有一個很重要的角色,就是 Config Server。

Config Server

Config Server 是 MongoDB Sharding 架構中相當重要的一個角色,它存放了資料的 Metadata,包括透過 Shard Key 計算出來的索引,用來記錄每一個資料存放的 Shard 位置,好讓 Router 可以正確的 Query 資料,如果沒有這些索引,那麼每一個用來存放資料的 mongod 實體,就「純粹」只是獨立存放分散的資料,無法協同工作。Config Server 聽起來很重要對吧!?因此 Config Server 部署時必須要多台機器,在新版 3.2 之後也可以用 Replica Set 架設 Config Server。

為了將資料分散儲存,就必須找一個方法來管理 Index,我們必須將 Document 某一個欄位定義為 Shard Key,然後透過 range based partitioning 或 hash based partitioning 其中一中方式將資料分配到 chunks 中 (每一個 Chunks 預設的大小為 64MB),最後才將 Chunks 分散到不同的 Shard 上。接下來我們先看看兩種不同的 Partitioning 機制:

Range Based Sharding

這種方式就是將 Document Shard Key 欄位的值,以線性的方式進行分群,透過 Range 範圍進行切割,相近的值理所當然會被分到同一個 Chunk 中,Chunk 分配的情況會直接受到 Range 的影響(比如某一段 Range 出現頻率高,Chunk 資料就比較大)。用這種方法我們必須考慮 Shard Key 的範圍,如下所示:

Hash Based Sharding

這種方法顧名思義就是透過「雜湊函式」將我們指定的 Shard Key 欄位進行雜湊,這樣的方式資料比較容易分散到每個 Shard 中,如果資料量足夠豐富,佔用空間的分配也會比較平均。如下:

比較一下兩種方法,Range Based 實際在 Query 時,Router 可以很輕易地判斷資料的位置,然後正確地派送運算到所屬的 Shard 上。但如果要查詢的資料片段大小差異過高,且又分散在不同的 Shard 上,查詢必定會「等待」其他 Shard 處理的情況產生。來看看 Hash Based,Shard Key 透過 Hash 計算後,很容易分散在不同的 Chunk,資料分散性佳,擴充也比較容易。但是在 Query 時就必須要經過比較多的 Shard 計算,才能由 Router 返回最後的結果。兩種方法各有利弊,可以依照實際的應用情況選用。那如果想要自行實作資料分散的邏輯呢?MongoDB 當然也是支援的,就是透過 Tag 這個功能,但我沒有研究,所以就無從介紹了。有興趣可以看看官方 Tag Aware Sharding 相關介紹。

開始建置:

由於 Shard 本身沒有備援機制,因此 MongoDB 的 Shard 必須建置在 Replica Set 上,Replica Set 建置方式請參考「MongoDB Replica Set 高可用性架構搭建」這一篇文章。假設您已經建立好 Replica Set,那麼就可以繼續接著開始建置 Shard。

我們規劃原來做的mongsh1~mongsh3三台伺服器,都拿來做shard server與config server,另外mongsh1/mongsh2拿來當router。

建置 Config Server

一開始我們先建立 Config Server,在 MongoDB Sharding Cluster 架構中,Config Server 必須有三個實體以上,強烈建議部署在不同的機器上,因為 Metadata 實在太重要了。下面的範例其實是與 Replica Set 一起部署,實際上分開會比較好。慣例上我們會先透過 /etc/hosts 管理我們的主機資訊 (用 DNS 也是可以),假設要安裝 Config Server 的 Hostname 為 mong-cfg1 ~ mong-cfg3

vi /etc/hosts
10.21.1.45 mongsh1
10.21.1.46 mongsh2
10.21.1.44 mongsh3

10.21.1.45 mong-cfg1
10.21.1.46 mong-cfg2
10.21.1.44 mong-cfg3

10.21.1.45 mong-router1
10.21.1.46 mong-router2

接著建立 Config Server 要存放的 DB File Directory,命令如下:

mkdir -p /home/mongodb-cfg
sudo chown -R mongodb:mongodb /home/mongodb-cfg

三台伺服器都要做。

然後建立 MongoDB Config Server 需要的設定檔,之後再透過 mongod 來載入啟用,因為我們測試環境的 Config Server 與 Replica Set 都在其實同一台機器(正式環境建議分開或分散比較好),Port 與 PID 都要不同,Port 隔開可以方便我們部署在不同機器,然而透過不同的設定檔也會比較好進行管理。大致上的 Port 分配如下:

  • Replica Set:27019 Port
  • Config Server:27018 Port
  • Mongo Router (mongos):27017 Port

編輯 Config Server 設定檔內容如下:

vi /etc/mongod-cfgserv.conf
storage:
  dbPath: /home/mongodb-cfg
  journal:
    enabled: true
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod-cfgserv.log
sharding:
   clusterRole: configsvr
net:
  port: 27018
  bindIp: 0.0.0.0
processManagement:
  fork: true
  pidFilePath: /var/run/mongod-cfgserv.pid

上面的設定檔先不開啟認證模式,官方規定在 Production 環境最少要配置三台以上的 Config Server。透過以下 mongod 命令分別在「mong-cfg1, mong-cfg2, mong-cfg3」三台機器各別透過 mongod 指令來啟用 Config Server。

sudo mongod -f /etc/mongod-cfgserv.conf

啟動後我們也可以在 /var/log/mongodb/mongod-cfgserv.log 看到 log 資訊。

建置 MongoDB Router

再來就是建置 MongoDB Sharding Router。首先建立 mongos 要載入的設定檔,基本上跟上面 Config Server 使用的 mongod 差不多。我們這裡測試用的 Router 是跟 Config Server 放在一起,實際上可分開存放會更好。

這裡,我們就都不設定認證模式,若有意設置,可參考這篇文章

設定檔內容如下:

sudo vi /etc/mongod-router.conf
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod-router.log
sharding:
  configDB: mong-cfg1:27018,mong-cfg2:27018,mongodb-cfg-3:27018
net:
  port: 27017
  bindIp: 0.0.0.0 
processManagement:
  fork: true
  pidFilePath: /var/run/mongodb-router.pid

我們可以看到上述的設定檔有指定 Config Server 的位置,好讓 Router 啟動時可以讀取 Config Server 中的 Metadata。也需要配置到另外一台伺服器mong-router2

如同上述的設定檔,先不用啟用認證來執行 mongos,主要是為了要先在 Config Server 中建立 User 好讓後續整個認證流程可以順利串起來,這裡通常大家都會遇到最多問題,很多人最後乾脆關閉認證來配置 Sharding,這樣其實很危險,不建議關閉認證。此外由於應用程式大多是透過 27017 Port 進行連線,所以要提供連線的 Router Bind 直接啟動在標準 MongoDB Port 這樣應用程式就不用修改囉。

先啟用其中一台的 MongoDB Sharding Router,等透過 Mongo Router 設定好 Config Server 之後再啟動所有的 Rouer 即可。注意這裡是用 mongos 命令來啟動喔,如下:

sudo mongos -f /etc/mongod-router.conf

配置自動啟動config與router服務:
於10.21.1.45與10.21.1.46兩台伺服器

vi /etc/rc.local
mongod -f /etc/mongod-cfgserv.conf
mongos -f /etc/mongod-router.conf

於10.21.1.44則配置如下

vi /etc/rc.local
mongod -f /etc/mongod-cfgserv.conf

建立 Sharding Cluster

接著我們透過認證模式進入 Mongo Router 來設定 Sharding,因為後端的 Monogd 是採用Replica Set 認證模式,所以配置 Sharding 的動作一定要透過同一把 Key 啟動,才能把服務整個串起來。以下透過我們建立好的管理者帳密登入 Mongo Router 來進行配置:

mongo mongodb-router1
mongos>sh.addShard("bes/mongodb-a1:27019,mongodb-a2:27019,mongodb-a3:27019")

加入後,再檢查一下狀態:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
	"_id" : 1,
	"minCompatibleVersion" : 5,
	"currentVersion" : 6,
	"clusterId" : ObjectId("59b4e7b564bf31ba36e4615c")
}
  shards:
	{  "_id" : "bes",  "host" : "bes/mongsh1:27019,mongsh2:27019,mongsh3:27019",  "state" : 1 }
  active mongoses:
	"3.4.8" : 2
 autosplit:
	Currently enabled: yes
  balancer:
	Currently enabled:  yes
	Currently running:  no
		Balancer lock taken at Sun Sep 10 2017 20:27:09 GMT+0800 (CST) by ConfigServer:Balancer
	Failed balancer rounds in last 5 attempts:  5
	Last reported error:  Cannot accept sharding commands if not started with --shardsvr
	Time of Reported error:  Wed Sep 13 2017 11:18:59 GMT+0800 (CST)
	Migration Results for the last 24 hours: 
		No recent migrations
  databases:
	{  "_id" : "teammerge12",  "primary" : "bes",  "partitioned" : true }

mongos> 

實務的配置上,我們會部署多台 Router 來平衡應用程式端的查詢,在應用程式中也可以指定這一群「Router」作為 Server。到這裡 MongoDB Sharding Cluster 就算是被置完成囉,實際的使用上還需要指定 Collection Shard Key 來分配 Chunk 到不同的 Shard 上,這裡請自行參考 MongoDB 官方的 Shard Key 使用介紹。