高可用mongodb集群(分片+副本)
參考網址:http://www.rzrgm.cn/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

浙公網安備 33010602011771號