1

MongoDB集群搭建

 2 years ago
source link: https://shidongxu0312.github.io/2020/02/17/MongoDB%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MongoDB集群搭建

MongoDB主从搭建

这个是最简答的集群搭建,不过准确说也不能算是集群,只能说是主备。并且官方已经不推荐这种方式,所以在这里只是简单的介绍下吧,搭建方式也相对简单。纯主从不能高可用,主挂了,则集群挂了,不推荐。

[root@localhost mongodb]# mkdir mongo-ms/master/data -p
[root@localhost mongodb]# mkdir mongo-ms/master/logs -p
[root@localhost mongodb]# mkdir mongo-ms/slave/logs -p       
[root@localhost mongodb]# mkdir mongo-ms/slave/data -p

主机配置 /usr/local/mongodb/mongo-ms/master/mongodb.cfg

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-ms/master/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-ms/master/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27001
# 主从模式下,指定我自身的角色是主机
master=true
# 主从模式下,从机的地址信息
source=192.168.222.128:27002

从机配置 /usr/local/mongodb/mongo-ms/slave/mongodb.cfg

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-ms/slave/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-ms/slave/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27001
# 主从模式下,指定我自身的角色是主机
master=true
# 主从模式下,从机的地址信息
source=192.168.222.128:27002
mongod -f /usr/local/mongodb/mongo-ms/master/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-ms/slave/mongodb.cfg
mongo --port=27001
mongo --port=27002
db.isMaster()

MongoDB副本集对读写分离的支持是通过Read Preferences特性进行支持的,这个特性非常复杂和灵 活。设置读写分离需要先在从节点SECONDARY 设置

rs.slaveOk()

基本上只要在主节点和备节点上分别执行这两条命令,Master-Slaver 就算搭建完成了。我没有试过主节点挂掉后备节点是否能变成主节点,不过既然已经不推荐了,大家就没必要去使用了。

MongoDB副本集集群

中文翻译叫做副本集,不过我并不喜欢把英文翻译成中文,总是感觉怪怪的。其实简单来说就是集群当中包含了多份数据,保证主节点挂掉了,备节点能继续提供数据服务,提供的前提就是数据需要和主节点一致。如下图: Mongodb(M)表示主节点,Mongodb(S)表示备节点,Mongodb(A)表示仲裁节点。主备节点存储数据,仲裁节点不存储数据。客户端同时连接主节点与备节点,不连接仲裁节点。 默认设置下,主节点提供所有增删查改服务,备节点不提供任何服务。但是可以通过设置使备节点 提供查询服务,这样就可以减少主节点的压力,当客户端进行数据查询时,请求自动转到备节点上。这 个设置叫做 Read Preference Modes,同时 Java 客户端提供了简单的配置方式,可以不必直接对数 据库进行操作。 仲裁节点是一种特殊的节点,它本身并不存储数据,主要的作用是决定哪一个备节点在主节点挂掉之后 提升为主节点,所以客户端不需要连接此节点。这里虽然只有一个备节点,但是仍然需要一个仲裁节点 来提升备节点级别。我开始也不相信必须要有仲裁节点,但是自己也试过没仲裁节点的话,主节点挂了 备节点还是备节点,所以咱们还是需要它的。

副本集中有三种角色:主节点、从节点、仲裁节点

仲裁节点不存储数据,主从节点都存储数据。

优点: 主如果宕机,仲裁节点会选举从作为新的主 如果副本集中没有仲裁节点,那么集群的主从切换依然可以进行。 缺点: 如果副本集中拥有仲裁节点,那么一旦仲裁节点挂了,集群中就不能进行主从切换了。

有仲裁节点的副本集

[root@localhost mongodb]# mkdir mongo-rs/rs01/node1/data -p
[root@localhost mongodb]# mkdir mongo-rs/rs01/node1/logs -p
[root@localhost mongodb]# mkdir mongo-rs/rs01/node2/data -p       
[root@localhost mongodb]# mkdir mongo-rs/rs01/node2/logs -p
[root@localhost mongodb]# mkdir mongo-rs/rs01/node3/data -p       
[root@localhost mongodb]# mkdir mongo-rs/rs01/node3/logs -p

节点1配置

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-rs/rs01/node1/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-rs/rs01/node1/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27003
#注意:不需要显式的去指定主从,主从是动态选举的
#副本集集群,需要指定一个名称,在一个副本集下,名称是相同的
replSet=rs001

节点2配置

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-rs/rs01/node2/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-rs/rs01/node2/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27004
#注意:不需要显式的去指定主从,主从是动态选举的
#副本集集群,需要指定一个名称,在一个副本集下,名称是相同的
replSet=rs001

节点3配置

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-rs/rs01/node3/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-rs/rs01/node3/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27005
#注意:不需要显式的去指定主从,主从是动态选举的
#副本集集群,需要指定一个名称,在一个副本集下,名称是相同的
replSet=rs001

启动副本集节点

mongod -f /usr/local/mongodb/mongo-rs/rs01/node1/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs01/node2/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs01/node3/mongodb.cfg

配置主备和仲裁

需要登录到mongodb的客户端进行配置主备和仲裁角色。 注意创建dbpath和logpath

mongo --port=27003
use admin
cfg={_id:"rs001",members: [
{_id:0,host:"192.168.222.128:27003",priority:2}, #主的可能性大
{_id:1,host:"192.168.222.128:27004",priority:1},
{_id:2,host:"192.168.222.128:27005",arbiterOnly:true}
]}
rs.initiate(cfg);

说明: cfg中的_id的值是【副本集名称】 priority:数字越大,优先级越高。优先级最高的会被选举为主库 arbiterOnly:true,如果是仲裁节点,必须设置该参数

rs.status()

###无仲裁副本集

和有仲裁的副本集基本上完全一样,只是在admin数据库下去执行配置的时候,不需要指定优先级和仲 裁节点。这种情况,如果节点挂掉,那么他们都会进行选举。

[root@localhost mongodb]# mkdir mongo-rs/rs02/node1/data -p
[root@localhost mongodb]# mkdir mongo-rs/rs02/node1/logs -p
[root@localhost mongodb]# mkdir mongo-rs/rs02/node2/data -p       
[root@localhost mongodb]# mkdir mongo-rs/rs02/node2/logs -p
[root@localhost mongodb]# mkdir mongo-rs/rs02/node3/data -p       
[root@localhost mongodb]# mkdir mongo-rs/rs02/node3/logs -p

节点1配置

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-rs/rs02/node1/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-rs/rs02/node1/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27006
#注意:不需要显式的去指定主从,主从是动态选举的
#副本集集群,需要指定一个名称,在一个副本集下,名称是相同的
replSet=rs002

节点2配置

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-rs/rs02/node2/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-rs/rs02/node2/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27007
#注意:不需要显式的去指定主从,主从是动态选举的
#副本集集群,需要指定一个名称,在一个副本集下,名称是相同的
replSet=rs002

节点3配置

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-rs/rs02/node3/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-rs/rs02/node3/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork=true
#绑定客户端访问的ip
bind_ip=0.0.0.0
# 默认27017
port=27008
#注意:不需要显式的去指定主从,主从是动态选举的
#副本集集群,需要指定一个名称,在一个副本集下,名称是相同的
replSet=rs002

启动副本集节点

mongod -f /usr/local/mongodb/mongo-rs/rs02/node1/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs02/node2/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs02/node3/mongodb.cfg

需要登录到mongodb的客户端进行配置主备。

mongo --port=27006
use admin
cfg={_id:"rs001",members: [
{_id:0,host:"192.168.222.128:27006"}, #主的可能性大
{_id:1,host:"192.168.222.128:27007"},
{_id:2,host:"192.168.222.128:27008"}
]}
rs.initiate(cfg);

MongoDB混合方式集群

数据服务器配置(副本集)

在副本集中每个数据节点的mongodb.cfg配置文件【追加】以下内容(仲裁节点除外):

shardsvr=true

配置服务器配置(先启动配置集再启动数据副本集)

配置三个配置服务器,配置信息如下,端口和path单独指定:

#数据库文件位置
dbpath=/usr/local/mongodb/mongo-conf/node1/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-conf/node1/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
# 默认27017
port=28001
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
#数据库文件位置
dbpath=/usr/local/mongodb/mongo-conf/node2/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-conf/node2/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
# 默认27017
port=28002
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
#数据库文件位置
dbpath=/usr/local/mongodb/mongo-conf/node3/data
#日志文件位置
logpath=/usr/local/mongodb/mongo-conf/node3/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
# 默认27017
port=28003
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr

注意创建dbpath和logpath

[root@localhost mongodb]# mkdir mongo-conf/node1/data -p
[root@localhost mongodb]# mkdir mongo-conf/node1/logs -p
[root@localhost mongodb]# mkdir mongo-conf/node2/data -p
[root@localhost mongodb]# mkdir mongo-conf/node2/logs -p
[root@localhost mongodb]# mkdir mongo-conf/node3/data -p
[root@localhost mongodb]# mkdir mongo-conf/node3/logs -p
#先启动配置集再启动数据副本集
#启动配置集
mongod -f /usr/local/mongodb/mongo-conf/node1/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-conf/node2/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-conf/node3/mongodb.cfg
#启动数据副本集--shard1
mongod -f /usr/local/mongodb/mongo-rs/rs01/node1/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs01/node2/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs01/node3/mongodb.cfg
#启动数据副本集--shard2
mongod -f /usr/local/mongodb/mongo-rs/rs02/node1/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs02/node2/mongodb.cfg
mongod -f /usr/local/mongodb/mongo-rs/rs02/node3/mongodb.cfg

配置副本集

mongo --port=28001
use admin
cfg={_id:"configsvr",members: [
{_id:0,host:"192.168.222.128:28001"},
{_id:1,host:"192.168.222.128:28002"},
{_id:2,host:"192.168.222.128:28003"}
]}
rs.initiate(cfg);

路由服务器配置

configdb=configsvr/192.168.222.128:28001,192.168.222.128:28002,192.168.222.128:28003
#日志文件位置
logpath=/usr/local/mongodb/mongo-router/node01/logs/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
# 默认28001
port=30000

路由服务器启动(注意这里是mongos命令而不是mongod命令

mongos -f /usr/local/mongodb/mongo-router/node01/mongodb.cfg

关联切片和路由

登录到路由服务器中,执行关联切片和路由的相关操作。

mongo  --port=30000 
#查看shard相关的命令
sh.help()
sh.addShard("切片名称/地址")
#数据副本集 
sh.addShard("rs001/192.168.222.128:27003");
sh.addShard("rs002/192.168.222.128:27006");
use kkb
sh.enableSharding("kkb");
#新的集合
sh.shardCollection("kkb.citem",{name:"hashed"});
for(var i=1;i<=1000;i++) db.citem.insert({name:"iphone"+i,num:i});
#分片效果
mongos> db.citem.count()
1000
mongo --port=27003
use kkb
rs001:PRIMARY> db.citem.count()
516
#从库
mongo --port=27004
use kkb
db.getMongo().setSlaveOk() # 设置主从读写分离 
rs001:SECONDARY> db.citem.count()
516 
mongo --port=27006
use kkb
db.citem.count()
484

MongoDB集群之复制集(基于docker)

一组Mongodb复制集,就是一组mongod进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,这是生产部署的基础。

  • 保证数据在生产部署时的冗余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢

失。能够随时应对数据丢失、机器损坏带来的风险。

  • 还能提高读取能力,用户的读取服务器和写入服务器在不同的地方,而且,由不同的服务器为不同的用户提供

服务,提高整个系统的负载。

  • 一组复制集就是一组mongod实例掌管同一个数据集,实例可以在不同的机器上面。实例中包含一个主导

(Primary),接受客户端所有的写入操作,其他都是副本实例(Secondary),从主服务器上获得数据并保持 同步。

  • 主服务器很重要,包含了所有的改变操作(写)的日志。但是副本服务器集群包含有所有的主服务器数据,因

此当主服务器挂掉了,就会在副本服务器上重新选取一个成为主服务器。

  • 每个复制集还有一个仲裁者(Arbiter),仲裁者不存储数据,只是负责通过心跳包来确认集群中集合的数量,并在主服务器选举的时候作为仲裁决定结果。

###架构

基本的架构由3台服务器组成,一个三成员的复制集,由三个有数据,或者两个有数据,一个作为仲裁者。

####三个存储数据的复制集

一个主,两个从库组成,主库宕机时,这两个从库都可以被选为主库。

当主库宕机后,两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。

####存在arbiter节点的复制集

一个主库,一个从库,可以在选举中成为主库,一个arbiter节点,在选举中,只进行投票,不能成为主库。

说明:由于arbiter节点没有复制数据,因此这个架构中仅提供一个完整的数据副本。arbiter节点只需要更少的资源,代价是更有限的冗余和容错。

当主库宕机时,将会选择从库成为主,主库修复后,将其加入到现有的复制集群中即可。

###Primary选举

复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。

『大多数』的定义

假设复制集内投票成员数量为N,则大多数为 N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。

###成员说明

成员 说明 Primary Priamry的作用是接收用户的写入操作,将自己的数据同步给其他的Secondary。 Secondary 正常情况下,复制集的Seconary会参与Primary选举(自身也可能会被选为Primary),并从Primary同步最新写入的数据,以保证与Primary存储相同的数据。Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力,同时提升复制集的可用性。另外,Mongodb支持对复制集的Secondary节点进行灵活的配置,以适应多种场景的需求。 Arbiter Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据。比如你部署了一个2个节点的复制集,1个Primary,1个Secondary,任一节点宕机,复制集将不能提供服务了(无法选出Primary),这时可以给复制集添加一个Arbiter节点,即使有节点宕机,仍能选出Primary。Arbiter本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性。 Priority0 Priority0节点的选举优先级为0,不会被选举为Primary。比如你跨机房A、B部署了一个复制集,并且想指定Primary必须在A机房,这时可以将B机房的复制集成员Priority设置为0,这样Primary就一定会是A机房的成员。(注意:如果这样部署,最好将『大多数』节点部署在A机房,否则网络分区时可能无法选出Primary) Vote0 Mongodb 3.0里,复制集成员最多50个,参与Primary选举投票的成员最多7个,其他成员(Vote0)的vote属性必须设置为0,即不参与投票。 Hidden Hidden节点不能被选为主(Priority为0),并且对Driver不可见。因Hidden节点不会接受Driver的请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响复制集的服务。 Delayed Delayed节点必须是Hidden节点,并且其数据落后与Primary一段时间(可配置,比如1个小时)。因Delayed节点的数据比Primary落后一段时间,当错误或者无效的数据写入Primary时,可通过Delayed节点的数据来恢复到之前的时间点。

搭建复制集

docker create --name mongo01 -p 27017:27017 -v mongo-data-01:/data/db mongo:4.0.3 --replSet "rs0" --bind_ip_all
docker create --name mongo02 -p 27018:27017 -v mongo-data-02:/data/db mongo:4.0.3 --replSet "rs0" --bind_ip_all
docker create --name mongo03 -p 27019:27017 -v mongo-data-03:/data/db mongo:4.0.3 --replSet "rs0" --bind_ip_all
#启动容器
docker start mongo01 mongo02 mongo03
#进入容器操作
docker exec -it mongo01 /bin/bash
#登录到mongo服务
mongo 172.17.0.1:27017
#初始化复制集集群
rs.initiate( {
   _id : "rs0",
   members: [
      { _id: 0, host: "172.17.0.1:27017" },
      { _id: 1, host: "172.17.0.1:27018" },
      { _id: 2, host: "172.17.0.1:27019" }
   ]
})
#响应
{
        "ok" : 1,  #成功
        "operationTime" : Timestamp(1551619334, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1551619334, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

测试复制集群:

#在主库插入数据
rs0:PRIMARY> use test
rs0:PRIMARY> db.user.insert({"id":1001,"name":"zhangsan"})
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> db.user.find()
{ "_id" : ObjectId("5c7bd5965504bcd309686907"), "id" : 1001, "name" : "zhangsan" }
#在复制库查询数据
mongo 172.17.0.1:27018
rs0:SECONDARY> use test
rs0:SECONDARY> db.user.find()
Error: error: { #出错,默认情况下从库是不允许读写操作的
        "operationTime" : Timestamp(1551619556, 1),
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotMasterNoSlaveOk",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1551619556, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
rs0:SECONDARY> rs.slaveOk()  #设置允许从库读取数据
rs0:SECONDARY> db.user.find()
{ "_id" : ObjectId("5c7bd5965504bcd309686907"), "id" : 1001, "name" : "zhangsan" }

###故障转移

  • 测试一:从节点宕机
    • 集群依然可以正常使用,可以读写操作。
  • 测试二:主节点宕机
    • 选举出新的主节点继续提供服务
  • 测试三:停止集群中的2个节点
    • 当前集群无法选举出Priamry,无法提供写操作,只能进行读操作

###增加arbiter节点

当集群中的节点数为偶数时,如一主一从情况下,任意一节点宕机都无法选举出Priamry,无法提供写操作,加入 arbiter节点后即可解决该问题。

docker create --name mongo04 -p 27020:27017 -v mongo-data-04:/data/db mongo:4.0.3 --replSet "rs0" --bind_ip_all
docker start mongo04
#在主节点执行
rs0:PRIMARY> rs.addArb("172.17.0.1:27020")
{
        "ok" : 1,
        "operationTime" : Timestamp(1551627454, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1551627454, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
#查询集群状态
rs.status()

通过测试,添加arbiter节点后,如果集群节点数不满足N/2+1时,arbiter节点可作为“凑数”节点,可以选出主节点,继续提供服务。

MongoDB集群之分片集群(基于docker)

分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法。尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事。 和MySQL分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同服务器之间的均衡。

###简介

高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。 为了解决这些问题,有两个基本的方法: 垂直扩展和水平扩展。

  • 垂直扩展:增加更多的CPU和存储资源来扩展容量。
  • 水平扩展:将数据集分布在多个服务器上。水平扩展即分片。

分片为应对高吞吐量与大数据量提供了方法。使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,集群可以提高自己的存储容量和吞吐量。举例来说,当插入一条数据时,应用只需要访问存储这条数据的分片。 使用分片减少了每个分片存储的数据。例如,如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256GB的数据。如果有40个分片,那么每个切分可能只有25GB的数据。

###优势

  • 对集群进行抽象,让集群“不可见”

    • MongoDB自带了一个叫做mongos的专有路由进程。mongos就是掌握统一路口的路由器,其会将客户端

    发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来发回到客 户端。

  • 保证集群总是可读写

    • MongoDB通过多种途径来确保集群的可用性和可靠性。

    • 将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时,也确保了每分数据都有

      相应的备份,这样就可以确保有服务器换掉时,其他的从库可以立即接替坏掉的部分继续工作。

  • 使集群易于扩展

    当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量。

组件 说明 Config Server 存储集群所有节点、分片数据路由信息。默认需要配置3个Config Server节点。 Mongos 提供对外应用访问,所有操作均通过mongos执行。一般有多个mongos节点。数据迁移和数据自动平衡。 Mongod 存储应用数据记录。一般有多个Mongod节点,达到数据分片目的。

Mongos本身并不持久化数据,Sharded cluster所有的元数据都会存储到Config Server,而用户的数据会分散存储到各个shard。Mongos启动后,会从配置服务器加载元数据,开始提供服务,将用户的请求正确路由到对应的分片。

当数据写入时,MongoDB Cluster根据分片键设计写入数据。当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据。

###集群中数据分布

在一个shard server内部,MongoDB会把数据分为chunks,每个chunk代表这个shard server内部一部分数据。 chunk的产生,会有以下两个用途:

  • Splitting:当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小

的chunk,从而避免chunk过大的情况

  • Balancing:在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个shard server的负

载,系统初始1个chunk,chunk size默认值64M,生产库上选择适合业务的chunk size是最好的。mongoDB会 自动拆分和迁移chunks。

####chunk分裂及迁移

随着数据的增长,其中的数据大小超过了配置的chunk size,默认是64M,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。

这时候,各个shard 上的chunk数量就会不平衡。mongos中的一个组件balancer 就会执行自动平衡。把chunk从 chunk数量最多的shard节点挪动到数量最少的节点。

####chunksize

chunk的分裂和迁移非常消耗IO资源;chunk分裂的时机:在插入和更新,读数据不会分裂。

  • 小的chunksize:数据均衡是迁移速度快,数据分布更均匀。数据分裂频繁,路由节点消耗更多资源。
  • 大的chunksize:数据分裂少。数据块移动集中消耗IO资源。

适合业务的chunksize是最好的。

chunkSize 对分裂及迁移的影响

  • MongoDB 默认的 chunkSize 为64MB,如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。

  • chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。

  • chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。
  • chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。
#创建3个config节点
docker create --name configsvr01  -p 17010:27019 -v mongoconfigsvr-data-01:/data/configdb mongo:4.0.3 --configsvr --replSet "rs_configsvr" --bind_ip_all
docker create --name configsvr02  -p 17011:27019 -v mongoconfigsvr-data-02:/data/configdb mongo:4.0.3 --configsvr --replSet "rs_configsvr" --bind_ip_all
docker create --name configsvr03  -p 17012:27019 -v mongoconfigsvr-data-03:/data/configdb mongo:4.0.3 --configsvr --replSet "rs_configsvr" --bind_ip_all
#启动服务
docker start configsvr01 configsvr02 configsvr03
#进去容器进行操作
docker exec -it configsvr01 /bin/bash
mongo 172.17.0.1:17010
#集群初始化
rs.initiate(
  {
    _id: "rs_configsvr",
    configsvr: true,
    members: [
      { _id : 0, host : "172.17.0.1:17010" },
      { _id : 1, host : "172.17.0.1:17011" },
      { _id : 2, host : "172.17.0.1:17012" }
    ]
  }
)
#创建2个shard集群,每个集群都有3个数据节点
#集群一
docker create --name shardsvr01 -p 37000:27018 -v mongoshardsvr-data-01:/data/db mongo:4.0.3 --replSet "rs_shardsvr1" --bind_ip_all --shardsvr
docker create --name shardsvr02 -p 37001:27018 -v mongoshardsvr-data-02:/data/db mongo:4.0.3 --replSet "rs_shardsvr1" --bind_ip_all --shardsvr
docker create --name shardsvr03 -p 37002:27018 -v mongoshardsvr-data-03:/data/db mongo:4.0.3 --replSet "rs_shardsvr1" --bind_ip_all --shardsvr

#集群二
docker create --name shardsvr04 -p 37003:27018 -v mongoshardsvr-data-04:/data/db mongo:4.0.3 --replSet "rs_shardsvr2" --bind_ip_all --shardsvr
docker create --name shardsvr05 -p 37004:27018 -v mongoshardsvr-data-05:/data/db mongo:4.0.3 --replSet "rs_shardsvr2" --bind_ip_all --shardsvr
docker create --name shardsvr06 -p 37005:27018 -v mongoshardsvr-data-06:/data/db mongo:4.0.3 --replSet "rs_shardsvr2" --bind_ip_all --shardsvr
#启动容器
docker start shardsvr01 shardsvr02 shardsvr03
docker start shardsvr04 shardsvr05 shardsvr06
#进去容器执行
docker exec -it shardsvr01 /bin/bash
mongo 172.17.0.1:37000
#集群初始化
rs.initiate(
  {
    _id: "rs_shardsvr1",
    members: [
      { _id : 0, host : "172.17.0.1:37000" },
      { _id : 1, host : "172.17.0.1:37001" },
      { _id : 2, host : "172.17.0.1:37002" }
    ]
  }
)
#集群初始化二
mongo 172.17.0.1:37003
rs.initiate(
  {
    _id: "rs_shardsvr2",
    members: [
      { _id : 0, host : "172.17.0.1:37003" },
      { _id : 1, host : "172.17.0.1:37004" },
      { _id : 2, host : "172.17.0.1:37005" }
    ]
  }
)
#创建mongos节点容器,需要指定config服务
docker create --name mongos -p 6666:27017 --entrypoint "mongos" mongo:4.0.3 --configdb rs_configsvr/172.17.0.1:17010,172.17.0.1:17011,172.17.0.1:17012 --bind_ip_all
docker start mongos
#进入容器执行
docker exec -it mongos /bin/bash
mongo 172.17.0.1:6666
#添加shard节点
sh.addShard("rs_shardsvr1/172.17.0.1:37000,172.17.0.1:37001,172.17.0.1:37002")
sh.addShard("rs_shardsvr2/172.17.0.1:37003,172.17.0.1:37004,172.17.0.1:37005")
#启用分片
sh.enableSharding("test")
#设置分片规则,按照_id的hash进行区分
sh.shardCollection("test.order", {"_id": "hashed" })
#插入测试数据
use test
for (i = 1; i <= 1000; i=i+1){
    db.order.insert({'id':i , 'price': 100+i})
}
#分别在2个shard集群中查询数据进行测试
db.order.count()
#集群操作(在mongos中执行)
use config
db.databases.find()  #列出所有数据库分片情况
db.collections.find() #查看分片的片键
sh.status() #查询分片集群的状态信息

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK