mongoDB高級篇-mongo分片初體驗

如果說復(fù)制集是mongo為了備份數(shù)據(jù),將一份數(shù)據(jù)存儲在多臺實例上的一種集群架構(gòu)的話,那么當(dāng)我們的數(shù)據(jù)存儲過多,最好能將數(shù)據(jù)分開存儲,這個時候就可以使用mongo的另一個多實例部署架構(gòu)--數(shù)據(jù)分片

分片的概念就是將數(shù)據(jù)拆分,將其分散存儲在不同機器上的過程。在很多中間件都有類似的概念,有些中間件中叫做'分區(qū)'概念,其實也是與分片類似的說法。基本上現(xiàn)代主流數(shù)據(jù)庫都支持,用戶手動管理,將數(shù)據(jù)存儲在不同的集合中,連接彼此獨立,但是手動維護帶來的問題就是維護難度的增大,整體分片的調(diào)整,如增加節(jié)點,刪除節(jié)點難度都變得很困難。而在MongoDB中,內(nèi)部集成了自動分片功能,可以讓分片操作對用戶不可見,從而簡化手動管理的成本。

分片初體驗

想要開始搭建一個分片集群,我們先理解一下這個集群中的幾個組件以及其作用,由于分片集群的目標(biāo)一般是想要滿足五臺、十臺甚至更多的實例,對外僅僅是和連接單機mongo一樣,因此必須有其他組件幫忙隱藏其中的一些細(xì)節(jié),因此整個mongo的分片集群架構(gòu)就可以分成如下這樣:

1621232061692.png
分片集群的三大組件

我們可以看到整個MongoDB Sharding集群中大體可以分為三個組件:

數(shù)據(jù)分片 -- shards

數(shù)據(jù)分片用于保存數(shù)據(jù),保證了數(shù)據(jù)的高可用和一致性,這里每一個shard可以是獨立的mongod實例,也可以是一個復(fù)制集,防止出現(xiàn)單點的故障問題。當(dāng)然也可以選擇把所有的shard的復(fù)制集放在一個服務(wù)器中啟動多個mongod實例,在sharding中,每個node的database數(shù)據(jù)庫可以選擇分片也可以選擇不分片,每一個db中都有一個獨立的primary shard,在未分片的集合中就是存在于各自的primary shard中的

查詢路由 -- mongos

我們可以看到,當(dāng)我們想要連接分片集群,將數(shù)據(jù)分開存儲,或者想要從分開存儲的分片集群中,將我們想要的數(shù)據(jù)聚集查詢出來,為了隱藏其中的細(xì)節(jié),這個時候就需要使用查詢路由--mongos了,客戶端連接不是直接連接分片集群中,而是連接到mongos,通過mongos進行一次路由過程的操作分發(fā),而mongos通過查詢維護的一個“內(nèi)容列表”,里面記錄了每個分片中按照什么規(guī)則(分片鍵)存儲數(shù)據(jù),每個分片中包含了哪些內(nèi)容等,而客戶端的請求到達mongos以后,mongos會根據(jù)記錄的內(nèi)容,選擇性將請求分發(fā)到對應(yīng)的一個或者多個分片服務(wù)實例上,然后再將所有分片的響應(yīng)結(jié)果進行合并再統(tǒng)一輸出給客戶端程序,從而實現(xiàn)了屏蔽分片的細(xì)節(jié),流程大體如下:

1614761108966.png

配置服務(wù)器 -- Config Server

配置服務(wù)器就是用于存儲分片集群的配置信息的元數(shù)據(jù),其中包含了mongos需要的shard的路由規(guī)則,路由鍵等信息,在mongo 3.2版本開始config server可以配置成為replica set了,在3.4以后官方已經(jīng)規(guī)定config server必須為replica set,并且為了保證生產(chǎn)環(huán)境的穩(wěn)定,rs中至少要有三個副本集成員存在

分片集群上手初體驗

現(xiàn)在我們開始搭建一個簡單的分片架構(gòu),在mongo3.4以后的版本中,一個分片集群,shard節(jié)點的數(shù)量至少要是兩個,而config節(jié)點和路由mongos節(jié)點則至少需要一個,因此我們這里來通過在一臺服務(wù)器上啟動多個節(jié)點的方式 模擬分片架構(gòu),搭建兩個shards復(fù)制集作為兩個shard節(jié)點,搭建一個config server復(fù)制集作為config節(jié)點,而mongos我們則使用單節(jié)點啟動方式,快速搭建一個分片集群架構(gòu)快速上手體驗一下

配置shards
cd /etc;
#首先創(chuàng)建一下shard的log存放的目錄
mkdir -p /var/log/mongodb_shard;
mkdir -p /var/log/mongo_shard1;
mkdir -p /var/log/mongo_shard2;
mkdir -p /var/log/mongodb_shard3;
#接著創(chuàng)建shard的數(shù)據(jù)庫文件存放目錄
mkdir -p /var/lib/mongo_shard;
mkdir -p /var/lib/mongo_shard1;
mkdir -p /var/lib/mongo_shard2;
mkdir -p /var/lib/mongo_shard3;
#創(chuàng)建shard啟動配置文件存放目錄
mkdir mongo_shard/;
mkdir mongo_shard1/;
mkdir mongo_shard2/;
mkdir mongo_shard3/;
#創(chuàng)建pidFilePath存放目錄
mkdir -p /var/run/mongo_shard;
mkdir -p /var/run/mongo_shard1;
mkdir -p /var/run/mongo_shard2;
mkdir -p /var/run/mongo_shard3;
#將mongo的配置文件分別cp到這些目錄中,這里我們cp的是上篇文章中我們配置的復(fù)制集的配置文件,否則配置文件中還要配置復(fù)制集
cp -r mongod1.conf mongo_shard/mongod.conf;
cp -r mongod1.conf mongo_shard1/mongod.conf;
cp -r mongod1.conf mongo_shard2/mongod.conf;
cp -r mongod1.conf mongo_shard3/mongod.conf;
#vim修改每一個shard的config啟動參數(shù),主要修改systemLog下的path,storage下的dbPath,processManagement下的pidFilePath,這里我們分別修改為上面創(chuàng)建的log目錄,db存儲目錄,以及pidFilePath存放目錄,net下的啟動端口這里分別修改為30017、30018和30019、30020,除此之外,由于shard我們要創(chuàng)建成為兩個rs,因此,replication下的replSetName參數(shù),mongo_shard和mongo_shard1我們將其指定為shard,mongo_shard和mongo_shard1我們將其指定為shard2,代表幾個shard組成的復(fù)制集名稱分別叫shard和shard2
vim mongo_shard/mongod.conf;
vim mongo_shard1/mongod.conf;
vim mongo_shard2/mongod.conf;
vim mongo_shard3/mongod.conf;

這里我們需要注意,由于當(dāng)前的角色是shard,因此我們在給常規(guī)的配置文件參數(shù)修改后,還要添加sharding相關(guān)的配置,代表當(dāng)前的角色為shard,如下:


1614847222095.png

添加如下配置即可:

sharding:
  # 分片角色
  clusterRole: shardsvr

下面是一個配置好的shard的mongod.conf的完整配置示例:

# mongod.conf
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb_shard/mongod.log

# Where and how to store data.
storage:
  dbPath: /var/lib/mongo_shard
  journal:
    enabled: true
#  engine:

#  wiredTiger:

# how the process runs
processManagement:
  fork: true  #后臺啟動進程
  pidFilePath: /var/run/mongo_shard/mongod.pid  
  timeZoneInfo: /usr/share/zoneinfo

# network interfaces
net:
  port: 30017
  bindIp: 0.0.0.0  # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.

#security:

#operationProfiling:

#replication:
replication:
 replSetName: shard
#sharding:
sharding:
  # 分片角色
  clusterRole: shardsvr

## Enterprise-Only Options

#auditLog:

#snmp:

將所有的shard的目錄和文件配置準(zhǔn)備完成,我們開始將其分別啟動,組成rs復(fù)制集:

/usr/bin/mongod -f /etc/mongo_shard/mongod.conf;
/usr/bin/mongod -f /etc/mongo_shard1/mongod.conf;
/usr/bin/mongod -f /etc/mongo_shard2/mongod.conf;
/usr/bin/mongod -f /etc/mongo_shard3/mongod.conf;
#查看當(dāng)前進程是否都存在
ps -ef|grep mongo
#可以看到 已經(jīng)啟動完成了
root     113935      1 13 02:35 ?        00:00:02 /usr/bin/mongod -f /etc/mongo_shard/mongod.conf
root     114005      1 26 02:35 ?        00:00:03 /usr/bin/mongod -f /etc/mongo_shard1/mongod.conf
root     114151      1 41 02:35 ?        00:00:03 /usr/bin/mongod -f /etc/mongo_shard2/mongod.conf
root     114252      1 41 02:35 ?        00:00:03 /usr/bin/mongod -f /etc/mongo_shard3/mongod.conf
root     114341   2433  0 02:35 pts/0    00:00:00 grep --color=auto mongo
#登錄shard復(fù)制集的任意一個節(jié)點,初始化復(fù)制集
/usr/bin/mongo --port 30017;
#初始化復(fù)制集 
rs.initiate({ _id:"shard", members:[{ _id:0, host:"127.0.0.1:30017" },{ _id:1, host:"127.0.0.1:30018" }] });
#登錄shard2復(fù)制集的任意一個節(jié)點,初始化復(fù)制集
/usr/bin/mongo --port 30019;
#初始化復(fù)制集 
rs.initiate({ _id:"shard2", members:[{ _id:0, host:"127.0.0.1:30019" },{ _id:1, host:"127.0.0.1:30020" }] });
配置config server

用于存儲分片配置的Config Server我們這里僅僅配置成一個master-slave的rs即可:

cd /etc;
#首先創(chuàng)建一下config_server的log存放的目錄
mkdir -p /var/log/mongo/config_master;
mkdir -p /var/log/mongo/config_slave;
#接著創(chuàng)建config_server的數(shù)據(jù)庫文件存放目錄
mkdir -p /var/lib/mongo/config_master;
mkdir -p /var/lib/mongo/config_slave;
#創(chuàng)建config_server啟動配置文件存放目錄
mkdir mongo_config_master/;
mkdir mongo_config_slave/;
#創(chuàng)建pidFilePath存放目錄
mkdir -p /var/run/mongo_config_master;
mkdir -p /var/run/mongo_config_slave;
#將mongo的配置文件分別cp到這些目錄中,這里我們cp的是上篇文章中我們配置的復(fù)制集的配置文件,否則配置文件中還要配置復(fù)制集
cp -r mongod1.conf mongo_config_master/mongod.conf;
cp -r mongod1.conf mongo_config_slave/mongod.conf;
#vim修改每一個config_server的config啟動參數(shù),主要修改systemLog下的path,storage下的dbPath,processManagement下的pidFilePath,這里我們分別修改為上面創(chuàng)建的log目錄,db存儲目錄,以及pidFilePath存放目錄,net下的啟動端口這里分別修改為37017和37018,除此之外,由于這里的節(jié)點我們要創(chuàng)建成為一個主-從的rs,因此,replication下的replSetName參數(shù)我們將其定義成config,代表幾個節(jié)點組成的復(fù)制集名稱叫config
vim mongo_config_master/mongod.conf;
vim mongo_config_slave/mongod.conf;

與上面shard的處理方式幾乎一樣,當(dāng)然這里唯一的區(qū)別就是,分片角色的配置,上面是shard,這里需要配置為config,如下:

#sharding:
sharding:
  # 分片角色
  clusterRole: configsvr

一切配置準(zhǔn)備好以后,我們開始啟動config server復(fù)制集,完成初始化操作:

/usr/bin/mongod -f /etc/mongo_config_master/mongod.conf;
/usr/bin/mongod -f /etc/mongo_config_slave/mongod.conf;
#登錄第一個復(fù)制集
/usr/bin/mongo --port 37017;
#初始化復(fù)制集 
rs.initiate({ _id:"config", members:[{ _id:0, host:"127.0.0.1:37017" },{ _id:1, host:"127.0.0.1:37018" }] });
啟動查詢路由--mongos

這里我們只配置一個mongos實例,不去做集群啟動了,因此我們需要和前面一樣,創(chuàng)建四個目錄,分別存放log,db文件和pid目錄以及config配置文件的目錄

cd /etc;
#首先創(chuàng)建一下shard的log存放的目錄
mkdir -p /var/log/mongos_db;
#接著創(chuàng)建mongos的數(shù)據(jù)庫文件存放目錄
mkdir -p /var/lib/mongos;
#創(chuàng)建mongos啟動配置文件存放目錄
mkdir mongos/;
#創(chuàng)建pidFilePath存放目錄
mkdir -p /var/run/mongos;
#復(fù)制 配置文件到目錄下
cp -r mongod1.conf mongos/mongod.conf;

#修改配置文件
vim mongos/mongod.conf;
#修改 path: /var/log/mongos_db/mongod1.log
#修改 dbPath: /var/lib/mongos/mongo1
#修改 pidFilePath: /var/run/mongos/mongod1.pid
#修改  port: 27050
#修改 replSetName: mongos
#配置文件中添加如下配置,指定config集群的信息
sharding:
  configDB: config/127.0.0.1:37017,127.0.0.1:37018
  
#需要注意的是,使用mongos的話,是無法識別storage.dbPath配置的,因此要注釋當(dāng)前的配置
#storage:
  #dbPath: /var/lib/mongos/mongo1  
  #journal:
  #  enabled: true
#除此之外,還要刪除replication相關(guān)的配置
#replication:
# replSetName: mongos
#啟動mongos,注意這里用的是mongos啟動
/usr/bin/mongos -f /etc/mongos/mongod.conf;
#連接mongos
mongo --port=27050;

這個時候我們連接進去,可以看到,當(dāng)前的角色已經(jīng)變了,成為了mongos,需要注意的是我們要做分片操作,首先需要給需要分片的數(shù)據(jù)庫啟動分片,開啟命令如下:

sh.enableSharding("db_name");

現(xiàn)在整個db的集合都可以進行分片了,不過需要注意的是,我們要對某個集合做分片,必須要選擇一個片鍵,片鍵是集合文檔內(nèi)的一個鍵,Mongo會根據(jù)策略和這個片鍵進行拆分?jǐn)?shù)據(jù),我們要給集合啟用分片之前,需要先在片鍵上創(chuàng)建索引:

db.collection.ensureIndex({"key":1});

然后我們就可以設(shè)置根據(jù)這個key來作為片鍵進行分片操作了:

sh.shardCollection("db.collection",{id:"key"});

配置完成后,我們來將shard信息添加進來,進行分片數(shù)據(jù)插入測試:

use admin;
#添加shard信息
mongos> sh.addShard("shard/127.0.0.1:30017,127.0.0.1:30018");
mongos> sh.addShard("shard2/127.0.0.1:30019,127.0.0.1:30020");
#設(shè)置test庫為允許sharding
sh.enableSharding("test");
#設(shè)置test中的tes集合的id按照hash的方式進行sharding
sh.shardCollection("test.tes",{id:"hashed"});
#插入幾條數(shù)據(jù)測試一下
for(i=1;i<=1000;i++){db.tes.insert({id:i,name:"Leo"})}

這里我們插入數(shù)據(jù)完成以后,分別進入兩個shard查看一下當(dāng)前的數(shù)據(jù),看是否生效

#登錄第一個shard
/usr/bin/mongo --port 30017;
use test;
db.tes.find();
#結(jié)果如下:
shard:PRIMARY> db.tes.find()
{ "_id" : ObjectId("60a3a08cd9ea857697bcf0cd"), "id" : 3, "name" : "Leo3" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d0"), "id" : 6, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d2"), "id" : 8, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d5"), "id" : 11, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d6"), "id" : 12, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0de"), "id" : 20, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0df"), "id" : 21, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e4"), "id" : 26, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e5"), "id" : 27, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e8"), "id" : 30, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0ea"), "id" : 32, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0eb"), "id" : 33, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0f0"), "id" : 38, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0f2"), "id" : 40, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0f3"), "id" : 41, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0f4"), "id" : 42, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0f6"), "id" : 44, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0fa"), "id" : 48, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0fc"), "id" : 50, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0fe"), "id" : 52, "name" : "Leo" }

#登錄第二個shard
/usr/bin/mongo --port 30019;
use test;
db.tes.find();
#結(jié)果如下:
shard2:PRIMARY> db.tes.find()
{ "_id" : ObjectId("60a3a07bd9ea857697bcf0ca"), "id" : 0, "name" : "Leo0" }
{ "_id" : ObjectId("60a3a081d9ea857697bcf0cb"), "id" : 1, "name" : "Leo1" }
{ "_id" : ObjectId("60a3a086d9ea857697bcf0cc"), "id" : 2, "name" : "Leo2" }
{ "_id" : ObjectId("60a3a091d9ea857697bcf0ce"), "id" : 4, "name" : "Leo4" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0cf"), "id" : 5, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d1"), "id" : 7, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d3"), "id" : 9, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d4"), "id" : 10, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d7"), "id" : 13, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d8"), "id" : 14, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0d9"), "id" : 15, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0da"), "id" : 16, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0db"), "id" : 17, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0dc"), "id" : 18, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0dd"), "id" : 19, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e0"), "id" : 22, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e1"), "id" : 23, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e2"), "id" : 24, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e3"), "id" : 25, "name" : "Leo" }
{ "_id" : ObjectId("60a3a0d3d9ea857697bcf0e6"), "id" : 28, "name" : "Leo" }

可見當(dāng)前我們的分片整個是已經(jīng)搭建完成了,這個時候我們可以查看當(dāng)前的分片集群的一個狀態(tài),通過如下命令:

mongos> sh.status();
--- Sharding Status --- 
  sharding version: {
        "_id" : 1,
        "minCompatibleVersion" : 5,
        "currentVersion" : 6,
        "clusterId" : ObjectId("60a39c69f5d7a342f9cdfb1d")
  }
  shards:
        {  "_id" : "shard",  "host" : "shard/127.0.0.1:30017,127.0.0.1:30018",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/127.0.0.1:30019,127.0.0.1:30020",  "state" : 1 }
  active mongoses:
        "4.2.8" : 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: 
                512 : Success
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                shard   512
                                shard2  512
                        too many chunks to print, use verbose if you want to force print
        {  "_id" : "test",  "primary" : "shard2",  "partitioned" : true,  "version" : {  "uuid" : UUID("72bb131e-1e88-4267-adc2-9f97e1e8d663"),  "lastMod" : 1 } }
                test.tes
                        shard key: { "id" : "hashed" }
                        unique: false
                        balancing: true
                        chunks:
                                shard   2
                                shard2  2
                        { "id" : { "$minKey" : 1 } } -->> { "id" : NumberLong("-4611686018427387902") } on : shard Timestamp(1, 0) 
                        { "id" : NumberLong("-4611686018427387902") } -->> { "id" : NumberLong(0) } on : shard Timestamp(1, 1) 
                        { "id" : NumberLong(0) } -->> { "id" : NumberLong("4611686018427387902") } on : shard2 Timestamp(1, 2) 
                        { "id" : NumberLong("4611686018427387902") } -->> { "id" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 3) 

sh命令和前面的rs命令很像,不過這里是用來代替分片集群管理的,而rs則是一個全局變量,除了可以管理以外,還內(nèi)置了很多輔助函數(shù),我們也可以選擇使用sh.help();查看可以使用的輔助函數(shù)有哪些

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容