1. 程式人生 > 其它 >MongoDB學習筆記2:分片叢集

MongoDB學習筆記2:分片叢集

高可用性 HA(High Availability)指的是縮短因正常運維或者非預期故障而導致的停機時間,提高系統可用性。

無論是資料的高可用,還是元件的高可用全都是一個解決方案:冗餘。通過多個元件和備份對外提供一致性和不中斷的服務。

MongoDB中提供了幾種高可用模式。

1 MongoDB主從模式

1.1 master-slave模式簡介

Mongodb 提供的第一種冗餘策略就是 Master-Slave 策略,這個也是分散式系統最開始的冗餘策略,這種是一種熱備策略。

Master-Slave 一般用於做備份或者讀寫分離,通常是一主一從或者一主多從架構。

主從模式有master角色和slave角色,這兩種角色說明如下:

master

可讀可寫,當有資料修改時,會將Oplog(類似mysql的binlog日誌)同步到所有連線的slave上。

slave

只讀,所有的slave從master同步資料,從節點與從節點之間從不感知,互相不知道對方的存在。

具體架構如下所示:

1.2 master-slave原理

在主從結構中,主節點的操作記錄成為oplog(operation log)。oplog儲存在一個系統資料庫local的集合 oplog.$main 中,這個集合的每個文件都代表主節點上執行的一個操作。
從節點會定期從主節點中獲取oplog記錄,然後在本機上執行!對於儲存oplog的集合,MongoDB採用的是固定集合,也就是說隨著操作過多,新的操作會覆蓋舊的操作!

1.3 master-slave缺點

讀寫分離資料不一致

MongoDB 中 Master 對外提供讀寫服務,有多個 Slave 節點的話,可以用 Slave 節點來提供讀服務的節點。

其中,只有master節點可寫,slave只能同步master資料並對外提供讀服務,這是一個非同步的過程。雖然最終資料會被 Slave 同步到,在資料完全一致之前,資料是不一致的,這個時候去 Slave 節點讀就會讀到舊的資料。所以,總結來說:讀寫分離的結構只適合特定場景,對於必須需要資料強一致的場景是不合適這種讀寫分離的

容災能力差

由於 master-slave的角色是靜態配置的,不能自動切換角色,所以當master發生故障時,需要人為操作,把slave指定為新的master節點。這樣在master發生故障時,需要較多的停服務時間。

目前,MongoDB 3.6 起已不推薦使用主從模式,自 MongoDB 3.2 起,分片群集元件已棄用主從複製。因為 Master-Slave 其中 Master 宕機後不能自動恢復,只能靠人為操作,可靠性也差,操作不當就存在丟資料的風險。

2 MongoDB 副本集模式 Replica Set

2.1 Replica Set 簡介

Replica Set 是一組 Mongodb 的副本集,就是一組mongod程序,這些程序維護同一個資料集合。副本集提供了資料冗餘,提高了資料的可用性。在多臺伺服器儲存資料可以避免因為一臺伺服器故障導致服務不可用或者資料丟失。

2.2 Replica Set 優點

  • 資料多副本,在故障的時候,可以使用完的副本恢復服務。注意:這裡是故障自動恢復
  • 讀寫分離,讀的請求分流到副本上,減輕主(Primary)的讀壓力;
  • 節點直接互有心跳,可以感知叢集的整體狀態;

2.3 Replica Set 角色

在副本集模式中,每個副本都是一個MongoDB例項,包含三類成員:

Primary主節點

  • 只有 Primary 主節點是可讀可寫的,Primary 負責接收客戶端所有的寫請求,然後把操作記錄到 oplog,再把資料同步到所有 Secondary 。
  • 一個 Replica Set 只有一個 Primary 節點,當 Primary 掛掉後,其他 Secondary 或者 Arbiter 節點會重新選舉出來一個 Primary 節點,這樣就又可以提供服務了。
  • 讀請求預設是發到 Primary 節點處理,如果需要故意轉發到 Secondary 需要客戶端修改一下配置(注意:是客戶端配置,決策權在客戶端)。
  • 副本集模式與Master-Slave 模式的最大區別在於,Primary 角色是通過整個叢集共同選舉出來的,人人都可能成為 Primary ,人人最開始只是 Secondary ,而這個選舉過程完全自動,不需要人為參與。

Secondary副本節點

  • 副本節點定期輪詢主節點獲取這些操作,然後對自己的資料副本執行這些操作,從而保證副本節點的資料與主節點一致。
  • 預設情況下,副本節點不支援外部讀取,但可以設定。副本集的機制在於主節點出現故障的時候,餘下的節點會選舉出一個新的主節點,從而保證系統可以正常執行。(自動切換的,無需人工參與)
  • Secondary 和 Master-Slave 模式的 Slave 角色的區別:最根本的一個不同在於:Secondary 相互有心跳,Secondary 可以作為資料來源,Replica 可以是一種鏈式的複製模式。

Arbiter仲裁者

  • 仲裁節點不復制資料,僅參與投票。由於它沒有訪問的壓力,比較空閒,因此不容易出故障。
  • 由於副本集出現故障的時候,存活的節點必須大於副本集節點總數的一半,否則無法選舉主節點,或者主節點會自動降級為從節點,整個副本集變為只讀。
  • 因此,增加一個不容易出故障的仲裁節點,可以增加有效選票,降低整個副本集不可用的風險。仲裁節點可多於一個。也就是說只參與投票,不接收復制的資料,也不能成為活躍節點。

副本集模式的架構圖如下:

注意:在副本集模式中,由於節點相互之間都有心跳,導致節點之間的心跳書以倍數增大,所以單個Replica Set叢集的節點數不宜過大,否則會影響叢集的整體效能。

2.4 Primary選舉

複製集通過 replSetInitiate 命令(或mongo shell的rs.initiate())進行初始化,初始化後各個成員間開始傳送心跳訊息,併發起Priamry選舉操作,獲得『大多數』成員投票支援的節點,會成為Primary,其餘節點成為Secondary。

因此,在副本整合員數上,一般設定為奇數最佳,否則可能會在投票時出現腦裂的問題。因為偶數個數的副本集,可能出現投票數相等的情況,這樣無法選出 Primary節點。

2.5 部署 replica set 叢集

2.5.1 環境準備

配置MongoDB副本集叢集需要多個伺服器環境或者多個例項,由於環境問題,這裡使用多例項方式部署。在一臺伺服器上部署3個例項,

伺服器環境如下:

[root@web01 ~]# cat /etc/redhat-release 
CentOS Linux release 7.9.2009 (Core)
[root@web01 ~]# uname -a
Linux web01 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@web01 ~]# hostname -I
192.168.5.50 
[root@web01 ~]# getenforce 
Disabled
[root@web01 ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:firewalld(1)

軟體版本:mongodb-linux-x86_64-rhel70-4.2.17.tgz

2.5.2 安裝mongodb

具體安裝步驟參考之前的博文:https://www.cnblogs.com/syushin/p/15598624.html

# 下載軟體包
$ cd /usr/local/src && wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.17.tgz

# 解壓
$ tar -xvf mongodb-linux-x86_64-rhel70-4.2.17.tgz

# 移動指定目錄
$ mv mongodb-linux-x86_64-rhel70-4.2.17 /usr/local/mongodb

# 建立使用者和組,並設定使用者密碼
$ useradd mongod
$ echo '123qwe321' | passwd --stdin mongod

# 建立mongodb的配置檔案目錄,日誌目錄和資料目錄
$ mkdir -p /mongodb/

# 設定目錄許可權
$ chown -R mongod:mongod /mongodb/

# 設定使用者環境變數
$ su - mongod
# 編輯.bashrc配置檔案,新增mongo的環境變數
$ echo 'export PATH=/usr/local/mongodb/bin:$PATH' >> .bashrc
$ source .bashrc

2.5.2 建立目錄

這裡建立了3個目錄,分別是28017、28018、28019,這三個數字是MongoDB多例項的3個埠。

$ su - mongod

$ for port in 28017 28018 28019;do mkdir -p /mongodb/${port}/{conf,data,log};done

$ tree /mongodb/
/mongodb/
├── 28017
│ ├── conf
│ ├── data
│ └── log
├── 28018
│ ├── conf
│ ├── data
│ └── log
└── 28019
    ├── conf
    ├── data
    └── log

12 directories, 0 files

2.5.3 準備配置檔案

先準備一個配置檔案:

$ cat > /mongodb/28017/conf/mongod.conf <<EOF
systemLog:
  destination: file
  path: /mongodb/28017/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28017/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
processManagement:
  fork: true
net:
  bindIp: 127.0.0.1
  port: 28017
replication:
  oplogSizeMB: 2048
  replSetName: my_repl
EOF

然後拷貝到其他兩個目錄,再替換掉埠號:

# 拷貝
$ cp /mongodb/28017/conf/mongod.conf /mongodb/28018/conf/
$ cp /mongodb/28017/conf/mongod.conf /mongodb/28019/conf/

# 替換
$ sed -i 's/28017/28018/g' /mongodb/28018/conf/mongod.conf 
$ sed -i 's/28017/28019/g' /mongodb/28018/conf/mongod.conf

2.5.4 啟動多例項MongoDB

$ for num in 28017 28018 28019;do mongod -f /mongodb/$num/conf/mongod.conf ;done

# 檢查是否啟動
$ netstat -lntp | grep mongod
tcp        0      0 127.0.0.1:28017         0.0.0.0:*               LISTEN      4804/mongod         
tcp        0      0 127.0.0.1:28018         0.0.0.0:*               LISTEN      4846/mongod         
tcp        0      0 127.0.0.1:28019         0.0.0.0:*               LISTEN      4888/mongod

2.5.5 配置MongoDB副本集

首先,進入28017的admin庫,然後配置副本集叢集:

$ mongo --port 28017 admin

# 配置副本集叢集
> config = {_id: 'my_repl', members: [
    {_id: 0, host: '127.0.0.1:28017'},
    {_id: 1, host: '127.0.0.1:28018'},
    {_id: 2, host: '127.0.0.1:28019', 'arbiterOnly': true},
]}

# 初始化叢集,至此副本集叢集的配置完成
> rs.initiate(config)
{ "ok" : 1 }

查詢副本集叢集狀態:

# 查詢副本集叢集狀態使用,注意,初始化叢集完成後,左邊提示符變成了my_repl:primary,表明當前節點是主節點
my_repl:PRIMARY> rs.status()

# 同樣,連線到28018例項,顯示是secondary副本節點
$ mongo --port 28018 admin
my_repl:SECONDARY>

# 28019是仲裁者節點
$ mongo --port 28019 admin
my_repl:ARBITER>

rs.status()命令主要關注以下成員的資訊,如果叢集有報錯會在這裡顯示:

"members" : [
		{
			"_id" : 0,
			"name" : "127.0.0.1:28017",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 1028,
			"optime" : {
				"ts" : Timestamp(1638151167, 1),
				"t" : NumberLong(1)
			},
			"optimeDate" : ISODate("2021-11-29T01:59:27Z"),
			"syncingTo" : "",
			"syncSourceHost" : "",
			"syncSourceId" : -1,
			"infoMessage" : "",
			"electionTime" : Timestamp(1638150676, 1),
			"electionDate" : ISODate("2021-11-29T01:51:16Z"),
			"configVersion" : 1,
			"self" : true,
			"lastHeartbeatMessage" : ""
		},
	    省略.........
]

除此之外,也有一些其他命令用於查詢副本集叢集資訊:

  • rs.status():檢視整個複製集狀態
  • rs.isMaster():檢視當前節點是否是主節點
  • rs.conf():檢視複製集配置資訊
  • db.printReplicationInfo():檢視 oplog 的資訊
  • db.printSlaveReplicationInfo():檢視副本節點的同步狀態
my_repl:PRIMARY> db.printReplicationInfo()
configured oplog size:   2048MB									 # 檔案大小
log length start to end: 21776secs (6.05hrs)					 # oplog 日誌的啟用時間段
oplog first event time:  Mon Nov 29 2021 09:51:06 GMT+0800 (CST) # 第一個事務日誌的產生時間
oplog last event time:   Mon Nov 29 2021 15:54:02 GMT+0800 (CST) # 最後一個事務日誌的產生時間
now:                     Mon Nov 29 2021 15:54:06 GMT+0800 (CST) # 現在的時間

2.5.6 增加和刪除節點

  • 新增節點:rs.add("ip:埠)
  • 刪除節點:rs.remove("ip:埠")
  • 新增仲裁節點:rs.addArb("ip:埠")

2.5.7 測試資料同步

在主節點插入資料:

my_repl:PRIMARY> use test
switched to db test
my_repl:PRIMARY> db.student.insert({"name": "ZhangSan", "age": 18})
WriteResult({ "nInserted" : 1 })

在副本節點讀取資料,預設副本節點是不能讀寫資料的,如果直接檢視會報錯:

# 登入到副本節點
$ mongo --port 28018

my_repl:SECONDARY> db
test
my_repl:SECONDARY> show tables;
2021-11-29T15:16:12.158+0800 E  QUERY    [js] uncaught exception: Error: listCollections failed: {
	"operationTime" : Timestamp(1638170172, 1),
	"ok" : 0,
	"errmsg" : "not master and slaveOk=false",
	"code" : 13435,
	"codeName" : "NotPrimaryNoSecondaryOk",
	"$clusterTime" : {
		"clusterTime" : Timestamp(1638170172, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype._getCollectionInfosCommand@src/mongo/shell/db.js:835:15
DB.prototype.getCollectionInfos@src/mongo/shell/db.js:883:16
shellHelper.show@src/mongo/shell/utils.js:893:9
shellHelper@src/mongo/shell/utils.js:790:15
@(shellhelp2):1:1

如果要在副本節點開啟讀,需要將slaveOK開啟:

my_repl:SECONDARY> rs.slaveOk()
# 因為這裡我的版本較新,提示slaveOk即將過時了,建議使用新的命令secondaryOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.

# 所以直接用secondaryOk()
my_repl:SECONDARY> rs.secondaryOk()

# 可以看到副本節點可以正確讀取資料了
my_repl:SECONDARY> show tables;
student
my_repl:SECONDARY> db.student.find().pretty()
{
	"_id" : ObjectId("61a47e0b387b76631e58fcc0"),
	"name" : "ZhangSan",
	"age" : 18
}

可以看到副本節點的資料跟我們在主節點插入的資料是一致的,說明資料已經同步過來了。

注意:禁止在副本節點上做任何修改資料的操作。

2.5.8 replica set 擴充套件

  • 副本集角色人為切換:rs.stepDown()
  • 鎖定副本節點,使其在指定時間內不會轉變為主節點:rs.freeze(300),300的單位是秒
  • 設定副本節點可讀:rs.slaveOK()rs.secondaryOk()

3 MongoDB 分片叢集 Sharding 模式

3.1 Sharding 模式簡介

Replica Set 模式已經非常好的解決了MongoDB可用性的問題,但是為什麼還會有分片叢集呢?是因為資料量的問題。

一旦資料多,搞資料量和吞吐量的應用會對單機的效能造成較大的壓力,大的查詢量會影響到單機的CPU、記憶體等。這個時候 Replica Set 就不太管用了。為了解決這個問題,有兩個基本的優化辦法:

  • 縱向優化(垂直擴充套件):增加更多的CPU和儲存資源來擴充套件容量。
  • 橫向優化(水平擴充套件):將資料集分佈在多個伺服器上,水平擴充套件即分片,通俗來講就是加節點。

縱向優化 通過不斷提高機器的配置來解決問題,但始終追不上資料的增加。

橫向優化 是業務上劃分系統資料集,並在多臺伺服器上處理,做到容量和能力跟機器數量成正比。單臺計算機的整體速度或容量可能不高,但是每臺計算機只能處理全部工作量的一部分,因此與單臺高速大容量伺服器相比,可能提供更高的效率。缺點是軟體的基礎結構要支援,部署維護比較複雜。

在實際生產中,自然是使用橫向優化會更好,如現在的分散式技術。MongoDB 的 Sharding 模式就是 MongoDB 橫向擴容的一個架構實現。

Sharding cluster是一種可以水平擴充套件的模式,在資料量很大時特給力,實際大規模應用一般會採用這種架構去構建。sharding分片很好的解決了單臺伺服器磁碟空間、記憶體、cpu等硬體資源的限制問題,把資料水平拆分出去,降低單節點的訪問壓力。每個分片都是一個獨立的資料庫,所有的分片組合起來構成一個邏輯上的完整的資料庫。因此,分片機制降低了每個分片的資料操作量及需要儲存的資料量,達到多臺伺服器來應對不斷增加的負載和資料的效果。

3.2 Sharding 叢集架構

首先,Sharding 叢集中以下三大模組:

  • Config Server:配置中心,儲存叢集所有節點、分片資料路由資訊。預設需要配置3個Config Server節點。
  • Mongos路由:代理層,提供對外應用訪問,所有操作均通過mongos執行。一般有多個mongos節點。資料遷移和資料自動平衡。
  • sharding分片:資料層, 儲存應用資料記錄。一般有多個Mongod節點,達到資料分片目的。

代理層

代理層的元件也就是 mongos ,這是個無狀態的元件,純粹是路由功能。向上對接 Client ,收到 Client 寫請求的時候,按照特定演算法均衡雜湊到某一個 Shard 叢集,然後資料就寫到 Shard 叢集了。收到讀請求的時候,定位找到這個要讀的物件在哪個 Shard 上,就把請求轉發到這個 Shard 上,就能讀到資料了。

資料層

是儲存資料的地方,每個shard(分片)包含被分片的資料集中的一個子集。每個分片可以被部署為 Replica Set 架構。

配置中心

代理層是無狀態的模組,資料層的每一個 Shard 是各自獨立的,那麼就需要有一個叢集統配管理的地方,這個地方就是配置中心。

配置中心記錄的是叢集所有節點、分片資料路由資訊。這些資訊也非常重要,不能單點儲存,所以配置中心也是一個 Replica Set叢集。

詳細架構圖:

0

3.3 Sharding 讀寫資料原理

單 Shard 叢集是有限的,但 Shard 數量是無限的,Mongo 理論上能夠提供近乎無限的空間,能夠不斷的橫向擴容。那麼現在唯一要解決的就是怎麼去把使用者資料存到這些 Shard 裡?MongDB 是怎麼做的?

首先,要選一個欄位(或者多個欄位組合也可以)用來做 Key,這個 Key 可以是你任意指定的一個欄位。我們現在就是要使用這個 Key 來,通過某種策略算出發往哪個 Shard 上。這個策略叫做:Sharding Strategy ,也就是分片策略。

我們把 Sharding Key 作為輸入,按照特點的分片策略計算出一個值,值的集合形成了一個值域,我們按照固定步長去切分這個值域,每一個片叫做 Chunk每個 Chunk 出生的時候就和某個 Shard 繫結起來,這個繫結關係儲存在配置中心裡。

所以,我們看到 MongoDB 的用 Chunk 再做了一層抽象層,隔離了使用者資料和 Shard 的位置,使用者資料先按照分片策略算出落在哪個 Chunk 上,由於 Chunk 某一時刻只屬於某一個 Shard,所以自然就知道使用者資料存到哪個 Shard 了。

Sharding 模式下資料寫入過程:

Sharding 模式下資料讀取過程:

通過上圖我們也看出來了,mongos 作為路由模組其實就是尋路的元件,寫的時候先算出使用者 key 屬於哪個 Chunk,然後找出這個 Chunk 屬於哪個 Shard,最後把請求發給這個 Shard ,就能把資料寫下去。讀的時候也是類似,先算出使用者 key 屬於哪個 Chunk,然後找出這個 Chunk 屬於哪個 Shard,最後把請求發給這個 Shard ,就能把資料讀上來。

實際情況下,mongos 不需要每次都和 Config Server 互動,大部分情況下只需要把 Chunk 的對映表 cache 一份在 mongos 的記憶體,就能減少一次網路互動,提高效能。

3.4 Sharding 的 Chunk 是什麼

Chunk是指一個集合資料中的子集,可以簡單理解為一個數據塊,每個chunk都是基於片鍵(Shard Key)的範圍取值,區間是左閉右開。chunk的產生,有以下用途:

  • splitting:當一個chunk的大小超過了配置中的chunk size時,MongoDB的後臺程序會把這個chunk切分成更小的chunk,從而避免chunk過大的情況;
  • Balancing:在 MongoDB 中,balancer是一個後臺程序,負責chunk的遷移,從而均衡各個shard server的負載,系統初始1個chunk,chunk size預設值是64M,生產庫上選擇適合業務的chunk size是最好的。MongoDB會自動拆分和遷移chunks。

分片叢集的資料分佈

  1. 使用chunk來儲存資料
  2. 叢集搭建完成之後,預設會開啟一個chunk,大小是64M
  3. 當一個chunk的儲存資料大小超過64M,chunk會進行分裂
  4. 當儲存需求變大,叢集上伺服器的chunk數量變多,每個伺服器的chunk資料嚴重失衡時,就會進行chunk的遷移

Chunk的大小

  • chunk的大小需要結合業務來進行選擇是最好的。

  • chunk的大小不宜過小,如果chunk過小,好處是可以讓資料更加均勻的分佈,但是會導致chunk之間頻繁的遷移,有一定的效能開銷;如果chunk的大小過大,會導致資料分佈不均勻。

3.5 資料區分

MongoDB的分片是以集合為基本單位的,集合中的資料是通過片鍵被分成多個多個部分。這個片鍵其實就是在集合中選一個Key,用該Key的值作為資料拆分的依。上面提到的分片策略就是基於片鍵的。本質上 Sharding Strategy 是形成值域的策略而已,MongoDB 支援兩種 Sharding Strategy:

  1. Hashed Sharding (雜湊分片)
  2. Range Sharding (以範圍為基礎的分片)

雜湊分片

對於基於雜湊的分片,MongoDB計算一個Key的雜湊值,並用這個雜湊值來建立chunk。在使用基於雜湊分片的系統中,擁有”相近”片鍵的文件很可能不會儲存在同一個資料塊中,因此資料的分離性更好一些。

基於範圍的分片

基於範圍的分片本質上是直接用 Key 本身來做值,形成的 Key Space 。

假設有一個數字的片鍵:想象一個從負無窮到正無窮的直線,每一個片鍵的值都在直線上畫了一個點。MongoDB 把這條直線劃分為更短的不重疊的片段,並稱之為資料塊,每個資料塊包含了片鍵在一定範圍內的資料。在使用片鍵做範圍劃分的系統中,擁有”相近”片鍵的文件很可能儲存在同一個資料塊中,因此也會儲存在同一個分片中。

如上圖這個例子,片鍵是name欄位,對於片鍵的值“test_0","test_1","test_2",這樣的key是緊挨著的,那麼這些資料大概率就會被分到同一個chunk裡面。

總結

雜湊分片和範圍分片都有各自的優缺點:

  • 雜湊分片
    • 優點:計算速度快,均衡性好,純隨機
    • 缺點:排序列舉效能差
  • 範圍分片
    • 優點:排序列舉效能高
    • 缺點:容易導致熱點,如果片鍵的值都在一個範圍內,那麼大概率會被分配到同一個shard裡,只盯著這一個shard讀寫,那麼其他的shard就很空閒,浪費資源。

Hash分片與範圍分片互補,能將文件隨機的分散到各個chunk,充分的擴充套件寫能力,彌補了範圍分片的不足,但不能高效的服務範圍查詢,所有的範圍查詢要分發到後端所有的Shard才能找出滿足條件的文件。

3.6 部署分片叢集

3.6.1 環境說明

同樣,基於centos7作業系統,安裝的軟體版本是 mongodb-4.2.17,安裝 MongoDB 的步驟就不在敘述了。

參考:https://www.cnblogs.com/syushin/p/15598624.html

此次安裝分片叢集使用了10個 MongoDB 例項,埠規劃是 28017~28026

  • 28017:mongos代理
  • 28018~28020:Config Server 配置中心(一主、兩從,config server 使用複製集不用有arbiter節點。3.4版本以後config必須為複製集)
  • 28021~28023:shard叢集(一主、一從、一仲裁,副本集名稱sh1)
  • 28024~28026:shard叢集(一主、一從、一仲裁,副本集名稱sh2)

建立多例項目錄:

# 切換到mongod使用者
$ su - mongod

# 刪除原先目錄下的資料
$ rm -rf /mongodb/*

# 建立資料目錄
$ for num in `seq 17 26`;do mkdir -p /mongodb/280$num/{conf,data,log};done

3.6.2 部署Shard叢集

部署第一個shard叢集28021~28023:

# 建立配置檔案
$ cat > /mongodb/28021/conf/mongodb.conf  <<EOF
systemLog:
  destination: file
  path: /mongodb/28021/log/mongodb.log   
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28021/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 127.0.0.1
  port: 28021
replication:
  oplogSizeMB: 2048
  replSetName: sh1
sharding:
  clusterRole: shardsvr
processManagement: 
  fork: true
EOF

# 拷貝配置檔案並替換埠
$ cp /mongodb/28021/conf/mongodb.conf /mongodb/28022/conf/
$ cp /mongodb/28021/conf/mongodb.conf /mongodb/28023/conf/
$ sed -i 's/28021/28022/g' /mongodb/28022/conf/mongodb.conf 
$ sed -i 's/28021/28023/g' /mongodb/28023/conf/mongodb.conf

部署第二個shard叢集28024~28026:

$ cat > /mongodb/28024/conf/mongodb.conf <<EOF
systemLog:
  destination: file
  path: /mongodb/28024/log/mongodb.log   
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28024/data
  directoryPerDB: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 127.0.0.1
  port: 28024
replication:
  oplogSizeMB: 2048
  replSetName: sh2
sharding:
  clusterRole: shardsvr
processManagement: 
  fork: true
EOF

# 拷貝配置檔案並替換埠
$ cp /mongodb/28024/conf/mongodb.conf /mongodb/28025/conf/
$ cp /mongodb/28024/conf/mongodb.conf /mongodb/28026/conf/
$ sed -i 's/28024/28025/g' /mongodb/28025/conf/mongodb.conf 
$ sed -i 's/28024/28026/g' /mongodb/28026/conf/mongodb.conf

啟動所有例項:

$ for num in `seq 21 26`;do mongod -f /mongodb/280$num/conf/mongodb.conf;done

配置副本集:

# 配置第一個副本集
$ mongo --port 28021 admin
> config = {_id: 'sh1', members: [
                          {_id: 0, host: '127.0.0.1:28021'},
                          {_id: 1, host: '127.0.0.1:28022'},
                          {_id: 2, host: '127.0.0.1:28023',"arbiterOnly":true}]
}

> rs.initiate(config)
{ "ok" : 1 }
sh1:SECONDARY> exit
bye

# 配置第二個副本集
$ mongo --port 28024 admin
> config = {_id: 'sh2', members: [
                          {_id: 0, host: '127.0.0.1:28024'},
                          {_id: 1, host: '127.0.0.1:28025'},
                          {_id: 2, host: '127.0.0.1:28026',"arbiterOnly":true}]
}

> rs.initiate(config)
{ "ok" : 1 }
sh2:SECONDARY> exit
bye

3.6.3 部署Config Server

建立配置檔案

$ cat > /mongodb/28018/conf/mongodb.conf <<EOF
systemLog:
  destination: file
  path: /mongodb/28018/log/mongodb.conf
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28018/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 127.0.0.1
  port: 28018
replication:
  oplogSizeMB: 2048
  replSetName: configReplSet
sharding:
  clusterRole: configsvr
processManagement: 
  fork: true
EOF

$ cp /mongodb/28018/conf/mongodb.conf /mongodb/28019/conf/
$ cp /mongodb/28018/conf/mongodb.conf /mongodb/28020/conf/
$ sed -i 's/28018/28019/g' /mongodb/28019/conf/mongodb.conf 
$ sed -i 's/28018/28020/g' /mongodb/28020/conf/mongodb.conf 

啟動例項

$ for num in `seq 18 20`;do mongod -f /mongodb/280$num/conf/mongodb.conf;done

配置副本集:

$ mongo --port 28018 admin
> config = {_id: 'configReplSet', members: [
                          {_id: 0, host: '127.0.0.1:28018'},
                          {_id: 1, host: '127.0.0.1:28019'},
                          {_id: 2, host: '127.0.0.1:28020'}]
}

> rs.initiate(config)  

3.6.4 部署mongos

建立配置檔案

$ cat > /mongodb/28017/conf/mongos.conf <<EOF
systemLog:
  destination: file
  path: /mongodb/28017/log/mongos.log
  logAppend: true
net:
  bindIp: 127.0.0.1
  port: 28017
sharding:
  configDB: configReplSet/127.0.0.1:28018,127.0.0.1:28019,127.0.0.1:28020
processManagement: 
  fork: true
EOF

啟動mongos:

$ mongos -f /mongodb/28017/conf/mongos.conf

將sh1和sh2節點加入叢集中:

# 登入mongos
$ mongo --port 28017 admin

# 新增分片
mongos> db.runCommand( { addshard : "sh1/127.0.0.1:28021,127.0.0.1:28022,127.0.0.1:28023",name:"shard1"} )
{
	"shardAdded" : "shard1",
	"ok" : 1,
	"operationTime" : Timestamp(1638349820, 6),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1638349820, 6),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}
mongos> db.runCommand( { addshard : "sh2/127.0.0.1:28024,127.0.0.1:28025,127.0.0.1:28026",name:"shard2"} )
{
	"shardAdded" : "shard2",
	"ok" : 1,
	"operationTime" : Timestamp(1638349827, 5),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1638349827, 5),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}

檢視分片節點

mongos> db.runCommand( { listshards : 1 } )
{
	"shards" : [
		{
			"_id" : "shard1",
			"host" : "sh1/127.0.0.1:28021,127.0.0.1:28022",
			"state" : 1
		},
		{
			"_id" : "shard2",
			"host" : "sh2/127.0.0.1:28024,127.0.0.1:28025",
			"state" : 1
		}
	],
	"ok" : 1,
	"operationTime" : Timestamp(1638349873, 1),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1638349873, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}

檢視分片叢集狀態:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
  	"_id" : 1,
  	"minCompatibleVersion" : 5,
  	"currentVersion" : 6,
  	"clusterId" : ObjectId("61a739b06c896ba6a5903cac")
  }
  shards:
        {  "_id" : "shard1",  "host" : "sh1/127.0.0.1:28021,127.0.0.1:28022",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "sh2/127.0.0.1:28024,127.0.0.1:28025",  "state" : 1 }
  active mongoses:
        "4.2.17" : 1
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours: 
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
        

至此,MongoDB分片叢集搭建完畢。

3.7 分片叢集的使用

  1. 啟用資料庫的分片功能,需要登入到mongos:
# 語法:
mongos> db.runCommand({enablesharding: '資料庫名稱'})

#比如對test資料庫開啟分片功能:
mongos> db.runCommand({enablesharding: 'test'})
{
	"ok" : 1,
	"operationTime" : Timestamp(1638350573, 31),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1638350573, 31),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}
  1. 給資料庫的某個表指定分片鍵,範圍分片
# 切換到test庫
mongos> use test

# 給表新增一個索引
mongos> db.testRange.ensureIndex({id: 1})

#切換到admin庫
mongos> use admin

# 指定片鍵,開啟分片功能,範圍分片
mongos> db.runCommand({shardcollection: "test.testRange", key: {id: 1}})

測試:

mongos> use test
mongos> for(i=1;i<1000000;i++){ db.testRange.insert({"id":i,"name":"shenzheng","age":70,"date":new Date()}); }

4 參考資料