參考網址:https://www.cnblogs.com/likingzi/p/16556734.html
高可用mongodb叢集(分片+副本):規劃及部署
概述
mongodb是最常用的nosql資料庫,以下記錄如何搭建高可用mongodb叢集(分片+副本)
mongodb叢集有三種模式:主從模式、副本集模式、sharding分片模式
副本集和sharding分片模式是最廣泛使用的方案,這2種方案的選擇透過資料量和併發數來權衡:GB級別採用副本集方案,TB級別或以上採用sharding模式,解決單機容量和單機併發能力
sharding模式分片越多,效能自然下降越多
對應社群版本:
https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-3.6.23.tgz
**下圖是一個典型的3節點分片副本叢集
node1 node2 node3
| | |
--------------- --------------- ---------------
|Mongos Server| |Mongos Server| |Mongos Server|
|App Server | |App Server | |App Server |
|Router | |Router | |Router |
--------------- --------------- ---------------
\_____________________________________________/
/\ /\
|| ||
|| --replica set--
|| |config server| -- node1
|| |-------------|
|| |config server| -- node2
|| |-------------|
|| |config server| -- node3
|| ---------------
|| ||
\/ \/
_____________________________________________
/ \
--replica set-- --replica set-- --replica set--
|shard server1| |shard server2| |shard server3| -- node1
|(Primary) | |(Arbiter) | |(Secondary) |
|-------------| |-------------| |-------------|
|shard server1| |shard server2| |shard server3| -- node2
|(Secondary) | |(Primary) | |(Arbiter) |
|-------------| |-------------| |-------------|
|shard server1| |shard server2| |shard server3| -- node3
|(Arbiter) | |(Secondary) | |(Primary) |
--------------- --------------- ---------------
如上圖可見幾個元件:
■ Mongos Server
資料庫叢集請求的入口,所有請求都透過mongos進行協調,不需在應用程式新增路由選擇器,mongos自己就是一個請求分發中心,它負責把資料請求請求轉發到對應的shard伺服器上。在生產環境通常設定多mongos作為請求入口,防止其中一個掛掉所有的mongodb請求都沒法操作。
1、使用者訪問 mongos 跟訪問單個 mongod 類似
2、所有 mongos 是對等關係,使用者訪問分片叢集可透過任意一個或多個mongos
3、mongos 本身是無狀態的,可任意擴充套件,叢集的服務能力為『Shard服務能力之和』與『mongos服務能力之和』的最小值
4、訪問分片叢集時,應將應用負載均勻的分散到多個 mongos 上
MongoClientURI connectionString = new MongoClientURI("mongodb://:rootpasswd@1.mongodb.rds.aliyuncs.com:3717,2.mongodb.rds.aliyuncs.com:3717/admin");
MongoDatabase database = client.getDatabase("mydb");
MongoCollection collection = database.getCollection("mycoll");
透過上述方式連線分片叢集時,客戶端會自動將請求分散到多個mongos 上,以實現負載均衡;同時,當URI 裡 mongos 數量在2個及以上時,當有mongos故障時,客戶端能自動進行 failover,將請求都分散到狀態正常的 mongos 上。
當 Mongos 數量很多時,還可以按應用來將 mongos 進行分組,比如有2個應用A、B、4個mongos,可以讓應用A 訪問 mongos 1-2(URI裡只指定mongos 1-2 的地址), 應用B 來訪問 mongos 3-4(URI裡只指定mongos 3-4 的地址),根據這種方法來實現應用間的訪問隔離(應用訪問的mongos彼此隔離,但後端 Shard 仍然是共享的)。
總而言之,在訪問分片叢集時,請務必確保 MongoDB URI 裡包含2個及以上的mongos地址,來實現負載均衡及高可用。
5、如何實現讀寫分離?
在options裡新增readPreference=secondaryPreferred即可實現,讀請求優先到Secondary節點,從而實現讀寫分離的功能,更多讀選項參考Read preferences
6、如何限制連線數?
在options裡新增maxPoolSize=xx即可將客戶端連線池限制在xx以內
7、如何保證資料寫入到大多數節點後才返回?
在options裡新增w= majority即可保證寫請求成功寫入大多數節點才向客戶端確認,更多寫選項參考Write Concern
■ config server
配置伺服器,儲存所有資料庫元資訊(路由、分片)的配置。mongos本身沒有物理儲存分片伺服器和資料路由資訊,只是快取在記憶體裡,配置伺服器則實際儲存這些資料。mongos第一次啟動或者關掉重啟就會從 config server 載入配置資訊,以後如果配置伺服器資訊變化會通知到所有的mongos更新自己的狀態,這樣mongos就能繼續準確路由。在生產環境通常設定多個 config server ,因為它儲存了分片路由的後設資料,防止單點資料丟失!
■ shard server
分片(sharding)是指將資料庫拆分,將其分散在不同的機器上的過程。將資料分散到不同的機器上,不需要功能強大的伺服器就可以儲存更多的資料和處理更大的負載。基本思想就是將集合切成小塊,這些塊分散到若干片裡,每個片只負責總資料的一部分,最後透過一個均衡器來對各個分片進行均衡(資料遷移)。
■ replica set
中文翻譯副本集,其實就是shard的備份,防止shard掛掉之後資料丟失。複製提供了資料的冗餘備份,並在多個伺服器上儲存資料副本,提高了資料的可用性, 並可以保證資料的安全性。
■ 仲裁者(Arbiter)
是複製集中的一個MongoDB例項,它並不儲存資料。仲裁節點使用最小的資源並且不要求硬體裝置,不能將Arbiter部署在同一個資料集節點中,可以部署在其他應用伺服器或者監視伺服器中,也可部署在單獨的虛擬機器中。為了確保複製集中有奇數的投票成員(包括primary),需要新增仲裁節點做為投票,否則primary不能執行時不會自動切換primary。
■ 主節點(Primary)
在複製集中,最多隻能擁有一個主節點,主節點是唯一能夠接收寫請求的節點。MongoDB在主節點進行寫操作,並將這些操作記錄到主節點的oplog中。而副節點將會從oplog複製到其本機,並將這些操作應用到自己的資料集上。
■ 副節點(Secondary)
副節點透過應用主節點傳來的資料變動操作來保持其資料集與主節點一致。副節點也可以透過增加額外引數配置來對應特殊需求。例如,副節點可以是non-voting或是priority 0.
■ 仲裁節點(Arbiter)
仲裁節點即投票節點,其本身並不包含資料集,且也無法晉升為主節點。但是,一旦當前的主節點不可用時,投票節點就會參與到新的主節點選舉的投票中。仲裁節點使用最小的資源並且不要求硬體裝置。投票節點的存在使得複製集可以以偶數個節點存在,而無需為複製集再新增節點。不要將投票節點執行在複製集的主節點或副節點機器上。投票節點與其他複製集節點的交流僅有:選舉過程中的投票,心跳檢測和配置資料。這些互動都是不加密的。
■ 心跳檢測
複製整合員每2秒向複製集中其他成員進行心跳檢測。如果某個節點在10秒內沒有返回,那麼它將被標記為不可用。
MongoDB副本集是有故障恢復功能的主從叢集,由一個primary節點和一個或多個secondary節點組成:
節點同步過程:Primary節點寫入資料,Secondary透過讀取Primary的oplog得到複製資訊,開始複製資料並且將複製資訊寫入到自己的oplog。如果某個操作失敗,則備份節點停止從當前資料來源複製資料。如果某個備份節點由於某些原因掛掉了,當重新啟動後,就會自動從oplog的最後一個操作開始同步,同步完成後,將資訊寫入自己的oplog,由於複製操作是先複製資料,複製完成後再寫入oplog,有可能相同的操作會同步兩份,不過MongoDB在設計之初就考慮到這個問題,將oplog的同一個操作執行多次,與執行一次的效果是一樣的。
通俗理解:當Primary節點完成資料操作後,Secondary會做出一系列的動作保證資料的同步:
- 檢查自己local庫的oplog.rs集合,找出最近的時間戳
- 檢查Primary節點local庫oplog.rs集合,找出大於此時間戳的記錄
- 將找到的記錄插入到自己的oplog.rs集合中,並執行這些操作
副本集的同步和主從同步一樣,都是非同步同步的過程,不同的是副本集有個自動故障轉移的功能。其原理是:slave端從primary端獲取日誌,然後在自己身上完全順序的執行日誌所記錄的各種操作(該日誌是不記錄查詢操作的),這個日誌就是local資料庫中的oplog.rs表,預設在64位機器上這個表是比較大的,佔磁碟大小的5%,oplog.rs的大小可以在啟動引數中設 定:–oplogSize 1000,單位是M。
注意:在副本集的環境中,要是所有的Secondary都當機了,只剩下Primary,則Primary會變成Secondary,不能提供服務。
■ 仲裁節點掛掉怎麼辦
要儘量使用奇數個節點而不要使用仲裁節點。
當仲裁節點掛掉後,若Primary節點正常,則不影響正常使用,將仲裁節點恢復即可;若在仲裁節點恢復之前,Primary節點就掛了,這時候因無法進行選擇投票,所以只有secondary節點而沒有Primary節點,因此只能進行檢索操作無法進行更新操作,此時若將仲裁節點恢復,將會重新選舉出Primary節點,叢集重新恢復正常功能。
■■ 分片叢集規劃
■ 注:配置支援IPV6
■ Configure hostname、hosts file、ip address
vim /etc/hosts
172.72.6.84 os8 node1
172.72.6.85 os8_2 node2
172.72.6.86 os8_3 node3
■ 節點角色及埠分配
|node1 |node2 |node3 |port |
|-------------|-------------|-------------|-----|
|mongos server|mongos server|mongos server|20000|
|-------------|-------------|-------------|-----|
|config server|config server|config server|21000|
|-------------|-------------|-------------|-----|
|shard server1|shard server1|shard server1|27001|
|(Primary) |(Secondary) |(Arbiter) | |
|-------------|-------------|-------------|-----|
|shard server2|shard server2|shard server2|27002|
|(Arbiter) |(Primary) |(Secondary) | |
|-------------|-------------|-------------|-----|
|shard server3|shard server3|shard server3|27003|
|(Secondary) |(Arbiter) |(Primary) | |
node |router|config|shard1 |shard2 |shard3
-----|------|------|-------|-------|-------
node1|20000 |21000 |27001/P|27002/A|27003/S
node2|20000 |21000 |27001/S|27002/P|27003/A
node3|20000 |21000 |27001/A|27002/S|27003/P
Note: P-Primary, S-Secondary, A-Arbiter
如需更大能力,則類似如下規劃
node |router|config|shard1 |shard2 |shard3 |shard4 |shard5 |shard6
-----|------|------|-------|-------|-------|-------|-------|-------
node1|20000 |21000 |27001/P|27002/A|27003/S|27004/S| |27006/A
node2|20000 |21000 |27001/S|27002/P|27003/A|27004/A|27005/A|
node3|20000 |21000 |27001/A|27002/S|27003/P| |27005/S|27006/S
node4| | | | | |27004/P|27005/P|27006/P
Pre-task preparation
■ 依賴包
yum install libcurl openssl [3.6.23]
yum install xz-libs [6.0.0另需]
■ 使用者及使用者組
groupadd mongod
groupadd mongodb
useradd -g mongod -G mongodb mongod
echo "Passwd#"|passwd mongod --stdin
**■ 下載、安裝
#https://www.mongodb.com/try/download/community
cd /data
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-3.6.23.tgz
tar zxvf mongodb-linux-x86_64-rhel.tgz
ln -s mongodb-linux-x86_64-rhel80-3.6.23 mongodb
chown -R mongod:mongod /data/mongodb
cp /data/mongodb/bin/* /usr/local/bin/
■ mongosh下載、安裝
cd /data
wget https://downloads.mongodb.com/compass/mongosh-1.5.4-linux-x64.tgz
tar zxvf mongosh-linux-x64.tgz
ln -s mongosh-1.5.4-linux-x64 mongosh
chown -R mongod:mongod /data/mongosh
cp /data/mongosh/bin/* /usr/local/bin/
■ mongosh使用
mongosh
mongosh "mongodb://127.0.0.1:21000"
show dbs
use admin
sh.status()
rs.status()
■ chronyd
略
■ selinux
關閉
■ firewalld
firewall-cmd --add-port=20000/tcp --permanent
firewall-cmd --add-port=21000/tcp --permanent
firewall-cmd --add-port=27001-27003/tcp --permanent
firewall-cmd --reload
■ 以下均用mongod使用者操作
■ 3個節點均建立6個目錄:conf、mongos、config、shard1、shard2、shard3
mkdir -p /data/mongodb/conf
mkdir -p /data/mongodb/mongos/log
mkdir -p /data/mongodb/config/data
mkdir -p /data/mongodb/config/log
mkdir -p /data/mongodb/shard1/data
mkdir -p /data/mongodb/shard1/log
mkdir -p /data/mongodb/shard2/data
mkdir -p /data/mongodb/shard2/log
mkdir -p /data/mongodb/shard3/data
mkdir -p /data/mongodb/shard3/log
■ 最終的目錄結構
tree /data/mongodb -L 2 --dirsfirst
/data/mongodb
├── conf
│ ├── config.conf
│ ├── mongos.conf
│ ├── shard1.conf
│ ├── shard2.conf
│ └── shard3.conf
├── config
│ ├── data
│ └── log
├── mongos
│ └── log
├── shard1
│ ├── data
│ └── log
├── shard2
│ ├── data
│ └── log
└── shard3
├── data
└── log
■■ config server
mongodb3.4以後要求配置伺服器也建立副本集,不然叢集搭建不成功
■ 配置檔案
cat > /data/mongodb/conf/config.conf << EOF
pidfilepath = /data/mongodb/config/log/configsrv.pid
dbpath = /data/mongodb/config/data
logpath = /data/mongodb/config/log/configsrv.log
logappend = true
#叢集IP地址及埠
bind_ip = 0.0.0.0,::
port = 21000
fork = true
#宣告這個配置是叢集
configsvr = true
#副本集名稱
replSet=configs
#設定最大連線數
maxConns=20000
EOF
■ 啟動3個 config server:
mongod -f /data/mongodb/conf/config.conf --ipv6
■ 登入任意一臺配置伺服器,初始化配置副本集
mongosh node1:21000
config變數:
config = {_id: "configs", members: [
{_id: 0, host: "node1:21000"},
{_id: 1, host: "node2:21000"},
{_id: 2, host: "node3:21000"}
]
}
初始化副本集:
rs.initiate(config)
其中,”_id” : “configs”應與配置檔案中配置的 replicaction.replSetName 一致,”members” 中的 “host” 為三個節點的 ip 和 port
檢視此時狀態:
rs.status()
■■ shard server
【3個節點執行】
■ shard server1
cat > /data/mongodb/conf/shard1.conf << EOF
pidfilepath = /data/mongodb/shard1/log/shard1.pid
dbpath = /data/mongodb/shard1/data
logpath = /data/mongodb/shard1/log/shard1.log
logappend = true
#叢集IP地址及埠
bind_ip = 0.0.0.0,::
port = 27001
fork = true
#副本集名稱
replSet=shard1
#宣告這個配置是叢集
shardsvr = true
#設定最大連線數
maxConns=20000
EOF
啟動3個 shard1 server:
mongod -f /data/mongodb/conf/shard1.conf --ipv6
登陸任意節點,初始化副本集:
注:初始化副本集的操作不能在仲裁節點上執行!
mongosh node1:27001
使用admin資料庫,定義副本集配置,"arbiterOnly":true 代表其為仲裁節點:
use admin
config = {_id: "shard1", members: [
{_id: 0, host: "node1:27001"},
{_id: 1, host: "node2:27001"},
{_id: 2, host: "node3:27001", arbiterOnly:true}
]
}
初始化副本集配置
rs.initiate(config);
rs.status()
■ shard server2
cat > /data/mongodb/conf/shard2.conf << EOF
pidfilepath = /data/mongodb/shard2/log/shard2.pid
dbpath = /data/mongodb/shard2/data
logpath = /data/mongodb/shard2/log/shard2.log
logappend = true
#叢集IP地址及埠
bind_ip = 0.0.0.0,::
port = 27002
fork = true
#副本集名稱
replSet=shard2
#宣告這個配置是叢集
shardsvr = true
#設定最大連線數
maxConns=20000
EOF
啟動3個 shard2 server:
mongod -f /data/mongodb/conf/shard2.conf --ipv6
登陸任意節點,初始化副本集:
注:初始化副本集的操作不能在仲裁節點上執行!
mongosh node2:27002
使用admin資料庫,定義副本集配置,"arbiterOnly":true 代表其為仲裁節點
use admin
config = {_id: "shard2", members: [
{_id: 0, host: "node1:27002", arbiterOnly:true},
{_id: 1, host: "node2:27002"},
{_id: 2, host: "node3:27002"}
]
}
初始化副本集配置
rs.initiate(config);
rs.status()
■ shard server3
cat > /data/mongodb/conf/shard3.conf << EOF
pidfilepath = /data/mongodb/shard3/log/shard3.pid
dbpath = /data/mongodb/shard3/data
logpath = /data/mongodb/shard3/log/shard3.log
logappend = true
#叢集IP地址及埠
bind_ip = 0.0.0.0,::
port = 27003
fork = true
#副本集名稱
replSet=shard3
#宣告這個配置是叢集
shardsvr = true
#設定最大連線數
maxConns=20000
EOF
啟動3個 shard3 server:
mongod -f /data/mongodb/conf/shard3.conf --ipv6
登陸任意節點,初始化副本集:
注:初始化副本集的操作不能在仲裁節點上執行!
mongosh node3:27003
使用admin資料庫,定義副本集配置,"arbiterOnly":true 代表其為仲裁節點:
use admin
config = {_id: "shard3", members: [
{_id: 0, host: "node1:27003"},
{_id: 1, host: "node2:27003", arbiterOnly:true},
{_id: 2, host: "node3:27003"}
]
}
初始化副本集配置
rs.initiate(config);
rs.status()
■■ mongos server
先啟動 config server 和 shard server,後啟動 mongos server (3個節點)
cat > /data/mongodb/conf/mongos.conf << EOF
pidfilepath = /data/mongodb/mongos/log/mongos.pid
logpath = /data/mongodb/mongos/log/mongos.log
logappend = true
#叢集IP地址及埠
bind_ip = 0.0.0.0,::
port = 20000
fork = true
#監聽的配置伺服器,只能有1個或者3個,configs為配置伺服器的副本集名字
configdb = configs/node1:21000,node2:21000,node3:21000
#設定最大連線數
maxConns=20000
EOF
啟動3個 mongos server:
mongos -f /data/mongodb/conf/mongos.conf --ipv6
■■ 啟用分片機制
【6.0機制可能不一樣,待驗證】
搭建了mongodb的 config server, shard server, mongos server 後,應用程式連線到 mongos server 並不能使用分片機制,還需設定分片配置才能使分片生效。
登陸任一 mongos server, 使用 admin 資料庫,串聯路由伺服器與分配副本集:
mongosh node1:20000
use admin
sh.addShard("shard1/node1:27001,node2:27001,node3:27001")
sh.addShard("shard2/node1:27002,node2:27002,node3:27002")
sh.addShard("shard3/node1:27003,node2:27003,node3:27003")
檢視叢集狀態:
sh.status()
■■ 使用分片機制
配置服務、路由服務、分片服務、副本集服務都串聯起來以後,為使插入的資料能自動分片,需連線 mongos server, 配置指定的資料庫、指定的集合分片生效。
指定分片生效:
db.runCommand({enablesharding:"testdb"})
指定需分片的集合和片鍵,以及分片策略,如:
設定 table1 表需分片,根據 id 自動分片到 shard1, shard2, shard3
db.runCommand({shardcollection:"testdb.table1",key:{id:"hashed"}})
注意:要這樣設定是因為不是所有 mongodb 的資料庫和表都需分片!
測試分片配置結果
mongosh node1:20000
use testdb
插入測試資料
for (var i = 1; i <= 100000; i++){
db.table2.insertOne({id:i,"test1":"testval1"});
}
檢視分片情況,確認資料是否分片,每個分片的資料數量是否大致相當,應該類似如下:
db.table1.stats()
{
sharded: true,
ns: 'testdb.table1',
count: 100000,
shards: {
shard1: {
ns: 'testdb.table1',
count: 3xxxx,
},
shard2: {
ns: 'testdb.table1',
count: 3xxxx,
},
shard3: {
ns: 'testdb.table1',
count: 33102,
}
},
ok: 1
}
可使用 sh.status() 檢視各個資料庫的分片使用情況
■■ 叢集啟停
■ 啟動順序
先啟動 config server
再啟動 shard server
再啟動 mongos server
mongod -f /data/mongodb/conf/config.conf --ipv6
mongod -f /data/mongodb/conf/shard1.conf --ipv6
mongod -f /data/mongodb/conf/shard2.conf --ipv6
mongod -f /data/mongodb/conf/shard3.conf --ipv6
mongos -f /data/mongodb/conf/mongos.conf --ipv6
■ 關閉時,直接killall程序
killall mongos
killall mongod
■ 使用叢集
#連線mongos server
mongosh mongodb://root:123456@node1:20000,node2:20000,node3:20000/admin
#連線分片
mongosh mongodb://root:123456@node1:27001,node2:27001,node3:27001/admin?replicaSet=shard1
mongosh mongodb://root:123456@node1:27002,node2:27002,node3:27002/admin?replicaSet=shard2
mongosh mongodb://root:123456@node1:27003,node2:27003,node3:27003/admin?replicaSet=shard3