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 參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM