MongoDB - 分布式集群搭建部署详解2(分片Sharding 模式)
前文我介绍了 MongoDB 集群的副本模式(点击查看),本文接着介绍分片(Sharding)模式。MongoDB 的分片机制允许创建一个包含许多台机器的集群,将数据子集分散在集群中,每个分片维护着一个数据集合的子集。与副本集相比,使用集群架构可以使应用程序具有更强大的数据处理能力。


(3)执行如下命令将安装包解压:
(4)将解压出来的文件夹拷贝到指定目录:
(5)进入 mongodb 文件夹:
(6)分别在每台机器建立 conf、route、config、shard1、shard2、shard3 六个目录,因为 route 不存储数据,只需要建立日志文件目录即可:
(7)最后各服务器还要执行下面命令配置 firewall 防火墙策略:
(2)在文件中填写如下内容:
(3)保存退出后,执行如下命令启动三台服务器的 config server:
(4)接着我们要初始化配置副本集,首先连接任意一个节点的 config server:
(5)执行如下命令设置副本集配置:
(6)最后执行如下命令使配置生效:
(7)稍等一会执行如下命令查看副本集状态:
(8)可以看到副本集的启动配置已完成,至此 config server 已部署完毕:
(2)在文件中填写如下内容:
(3)保存退出后,执行如下命令启动三台服务器的 shard1 server:
(4)接着我们要初始化副本集,首先连接任意一个节点的 shard1 server:
(5)执行如下命令使用 admin 数据库:
(6)然后执行如下命令设置副本集配置:
(7)最后执行如下命令使配置生效:
(8)稍等一会执行如下命令查看副本集状态:
(9)可以看到副本集的启动配置已完成(其中 26.47.136.14 作为主节点),至此 shard1 server 已部署完毕:
(2)在文件中填写如下内容:
(3)保存退出后,执行如下命令启动三台服务器的 shard2 server:
(4)接着我们要初始化副本集,首先连接任意一个节点的 shard2 server:
(5)执行如下命令使用 admin 数据库:
(6)然后执行如下命令设置副本集配置:
(7)最后执行如下命令使配置生效:
(8)稍等一会执行如下命令查看副本集状态:
(9)可以看到副本集的启动配置已完成(这次 26.47.136.15 作为主节点),至此 shard2 server 已部署完毕:
(2)在文件中填写如下内容:
(3)保存退出后,执行如下命令启动三台服务器的 shard3 server:
(4)接着我们要初始化副本集,首先连接任意一个节点的 shard3 server:
(5)执行如下命令使用 admin 数据库:
(6)然后执行如下命令设置副本集配置:
(7)最后执行如下命令使配置生效:
(8)稍等一会执行如下命令查看副本集状态:
(9)可以看到副本集的启动配置已完成(这次 26.47.136.16 作为主节点),至此 shard3 server 已部署完毕:
(2)在文件中填写如下内容:
(3)保存退出后,执行如下命令启动三台服务器的 route server:
(4)接着将路由服务器和分片副本集串联起来。首先连接任意一个节点的 route server:
(5)执行如下命令使用 admin 数据库:
(6)执行如下命令将副本集添加到分片集中:
(7)然后执行如下命令查看集群状态:
(8)显示如下内容说明整个集群已经搭建完毕:
(2)执行如下命令使用 admin 数据库:
(3)假设我们需要让 hangge 这个数据库启用分片,只需要执行如下命令,便可以对该数据库内的集合进行分片了:
(4)切换到 hangge 数据库:
(5)接着执行如下命令往 user 集合中插入 10 万条数据:
(6)查看集群信息,发现目前所有数据都插入到了第 1 个分片(shard1)中,这个是由于我们没有对集合指定分片键(shard key),因此该集合还不是个分片集合:
(11)再次查看 users 集合的分片情况,虽然目前已经是分片集合了,但是所有的数据仍然在 shard1 分片上。这是因为启用分片之后,数据会以 chunk 为单位(默认 64MB)根据片键均匀的分散到后端 1 或多个 shard 上。但目前记录数还没有超过阀值,因此只有 1 个 chunk,不会分裂。
构建一个 MongoDB 的分片集群,需要三个重要的组件,分别是分片服务器(Shard Server)、配置服务器(Config Server)和路由服务器(Route Server):
在实际生产环境中,为了满足高可用性和高可扩展性的需求,并不会单纯只采用分片模式。而是将副本集和分片是结合起来使用的,即每一个分片服务器又会由多台机器组成的一个副本集来承担,从而防止某一分片单点故障导致整个系统奔溃。下面通过样例进行演示如何搭建副本集+分片集群模式。
- Shard Server:每个 Shard Server 都是一个 mongod 数据库实例,用于存储实际的数据块。整个数据库集合分成多个块存储在不同的 Shard Server 中。在实际生产中,一个 Shard Server 可由几台机器组成一个副本集来承担,防止因主节点单点故障导致整个系统崩溃。
- Config Server:这是独立的一个 mongod 进程,保存集群和分片的元数据,在集群启动最开始时建立,保存各个分片包含数据的信息。
- Route Server:这是独立的一个 mongos 进程,Route Server 在集群中可作为路由使用,客户端由此接入,让整个集群看起来像是一个单一的数据库,提供客户端应用程序和分片集群之间的接口。Route Server 本身不保存数据,启动时从 Config Server 加载集群信息到缓存中,并将客户端的请求路由给每个 Shard Server,在各 Shard Server 返回结果后进行聚合并返回客户端。

二、搭建分片(Sharding)+副本集(Replica Set)集群模式
1,环境准备
这里我们准备三台 CentOS 服务器作用于搭建包含三个分片的集群(并且每个分片又由三个节点的副本集来承担),因此每台服务器上都会包含 5 个服务,具体如下:
注意:这里每个分片副本集的主节点分别使用不同的服务器,起到分散压力的效果。
26.47.136.14 | 26.47.136.15 | 26.47.136.16 |
route server(端口:24017) | route server(端口:24017) | route server(端口:24017) |
config server(端口:24000) | config server(端口:24000) | config server(端口:24000) |
shard server1 主节点(端口:24001) | shard server1 副本节点(端口:24001) | shard server1 副本节点(端口:24001) |
shard server2 副本节点(端口:24002) | shard server2 主节点(端口:24002) | shard server2 副本节点(端口:24002) |
shard server3 副本节点(端口:24003) | shard server3 副本节点(端口:24003) | shard server3 主节点(端口:24003) |
2,安装 MongoDB
(1)首先访问官网(点击跳转),寻找适合 CentOS 系统的下载地址:

(2)分别登录三台服务器,使用 wget 命令下载安装包(后续步骤在三台服务器上都是同样操作):
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-4.4.10.tgz
(3)执行如下命令将安装包解压:
tar -zxvf mongodb-linux-x86_64-rhel62-4.4.10.tgz
(4)将解压出来的文件夹拷贝到指定目录:
mv mongodb-linux-x86_64-rhel62-4.4.10 /usr/local/mongodb
(5)进入 mongodb 文件夹:
cd /usr/local/mongodb
(6)分别在每台机器建立 conf、route、config、shard1、shard2、shard3 六个目录,因为 route 不存储数据,只需要建立日志文件目录即可:
mkdir -p conf mkdir -p route/log mkdir -p config/db mkdir -p config/log mkdir -p shard1/db mkdir -p shard1/log mkdir -p shard2/db mkdir -p shard2/log mkdir -p shard3/db mkdir -p shard3/log
(7)最后各服务器还要执行下面命令配置 firewall 防火墙策略:
firewall-cmd --permanent --add-port=24017/tcp firewall-cmd --permanent --add-port=24000/tcp firewall-cmd --permanent --add-port=24001/tcp firewall-cmd --permanent --add-port=24002/tcp firewall-cmd --permanent --add-port=24003/tcp firewall-cmd --reload
3,配置启动 config server(配置服务器)
(1)分别在三台服务器上添加相关配置文件:提示:mongodb3.4 以后要求配置服务器也创建副本集,不然集群搭建不成功。
vi /usr/local/mongodb/conf/config.conf
(2)在文件中填写如下内容:
pidfilepath = /usr/local/mongodb/config/log/configsrv.pid dbpath = /usr/local/mongodb/config/db logpath = /usr/local/mongodb/config/log/congigsrv.log logappend = true bind_ip = 0.0.0.0 port = 24000 fork = true #标明这是一个配置服务器集群 configsvr = true #副本集名称 replSet = configs #设置最大连接数 maxConns = 20000
(3)保存退出后,执行如下命令启动三台服务器的 config server:
cd /usr/local/mongodb/bin ./mongod -f /usr/local/mongodb/conf/config.conf
(4)接着我们要初始化配置副本集,首先连接任意一个节点的 config server:
/usr/local/mongodb/bin/mongo 26.47.136.14:24000
(5)执行如下命令设置副本集配置:
各参数说明:
- cfg 是可以任意的名字,当然最好不要是 mongodb 的关键字,conf,config 都可以。
- 最外层的 _id 表示配置文件中 replica set 的名字
- members 里包含的是所有节点的地址。
cfg = { _id : "configs", members : [ {_id : 0, host : "26.47.136.14:24000" }, {_id : 1, host : "26.47.136.15:24000" }, {_id : 2, host : "26.47.136.16:24000" } ] }
(6)最后执行如下命令使配置生效:
rs.initiate(cfg)
(7)稍等一会执行如下命令查看副本集状态:
rs.status()
(8)可以看到副本集的启动配置已完成,至此 config server 已部署完毕:

4,配置启动第 1 个分片服务(shard1 server)
(1)分别在三台服务器上添加相关配置文件:
vi /usr/local/mongodb/conf/shard1.conf
(2)在文件中填写如下内容:
pidfilepath = /usr/local/mongodb/shard1/log/shard1.pid dbpath = /usr/local/mongodb/shard1/db logpath = /usr/local/mongodb/shard1/log/shard1.log logappend = true bind_ip = 0.0.0.0 port = 24001 fork = true #副本集名称 replSet = shard1 #标明这是一个分片副本集集群; shardsvr = true #设置最大连接数 maxConns = 20000
(3)保存退出后,执行如下命令启动三台服务器的 shard1 server:
cd /usr/local/mongodb/bin ./mongod -f /usr/local/mongodb/conf/shard1.conf
(4)接着我们要初始化副本集,首先连接任意一个节点的 shard1 server:
/usr/local/mongodb/bin/mongo 26.47.136.14:24001
(5)执行如下命令使用 admin 数据库:
use admin
(6)然后执行如下命令设置副本集配置:
cfg = { _id : "shard1", members : [ {_id : 0, host : "26.47.136.14:24001", priority: 3}, {_id : 1, host : "26.47.136.15:24001", priority: 2}, {_id : 2, host : "26.47.136.16:24001", priority: 1} ] }
(7)最后执行如下命令使配置生效:
rs.initiate(cfg)
(8)稍等一会执行如下命令查看副本集状态:
rs.status()
(9)可以看到副本集的启动配置已完成(其中 26.47.136.14 作为主节点),至此 shard1 server 已部署完毕:

5,配置启动第 2 个分片服务(shard2 server)
(1)分别在三台服务器上添加相关配置文件:
vi /usr/local/mongodb/conf/shard2.conf
(2)在文件中填写如下内容:
pidfilepath = /usr/local/mongodb/shard2/log/shard2.pid dbpath = /usr/local/mongodb/shard2/db logpath = /usr/local/mongodb/shard2/log/shard2.log logappend = true bind_ip = 0.0.0.0 port = 24002 fork = true #副本集名称 replSet = shard2 #标明这是一个分片副本集集群; shardsvr = true #设置最大连接数 maxConns = 20000
(3)保存退出后,执行如下命令启动三台服务器的 shard2 server:
cd /usr/local/mongodb/bin ./mongod -f /usr/local/mongodb/conf/shard2.conf
(4)接着我们要初始化副本集,首先连接任意一个节点的 shard2 server:
/usr/local/mongodb/bin/mongo 26.47.136.14:24002
(5)执行如下命令使用 admin 数据库:
use admin
(6)然后执行如下命令设置副本集配置:
cfg = { _id : "shard2", members : [ {_id : 0, host : "26.47.136.14:24002", priority: 1}, {_id : 1, host : "26.47.136.15:24002", priority: 3}, {_id : 2, host : "26.47.136.16:24002", priority: 2} ] }
(7)最后执行如下命令使配置生效:
rs.initiate(cfg)
(8)稍等一会执行如下命令查看副本集状态:
rs.status()
(9)可以看到副本集的启动配置已完成(这次 26.47.136.15 作为主节点),至此 shard2 server 已部署完毕:

6,配置启动第 3 个分片服务(shard3 server)
(1)分别在三台服务器上添加相关配置文件:
vi /usr/local/mongodb/conf/shard3.conf
(2)在文件中填写如下内容:
pidfilepath = /usr/local/mongodb/shard3/log/shard3.pid dbpath = /usr/local/mongodb/shard3/db logpath = /usr/local/mongodb/shard3/log/shard3.log logappend = true bind_ip = 0.0.0.0 port = 24003 fork = true #副本集名称 replSet = shard3 #标明这是一个分片副本集集群; shardsvr = true #设置最大连接数 maxConns = 20000
(3)保存退出后,执行如下命令启动三台服务器的 shard3 server:
cd /usr/local/mongodb/bin ./mongod -f /usr/local/mongodb/conf/shard3.conf
(4)接着我们要初始化副本集,首先连接任意一个节点的 shard3 server:
/usr/local/mongodb/bin/mongo 26.47.136.14:24003
(5)执行如下命令使用 admin 数据库:
use admin
(6)然后执行如下命令设置副本集配置:
cfg = { _id : "shard3", members : [ {_id : 0, host : "26.47.136.14:24003", priority: 2}, {_id : 1, host : "26.47.136.15:24003", priority: 1}, {_id : 2, host : "26.47.136.16:24003", priority: 3} ] }
(7)最后执行如下命令使配置生效:
rs.initiate(cfg)
(8)稍等一会执行如下命令查看副本集状态:
rs.status()
(9)可以看到副本集的启动配置已完成(这次 26.47.136.16 作为主节点),至此 shard3 server 已部署完毕:

7,配置启动路由服务器(route server)
(1)分别在三台服务器上添加相关配置文件:
vi /usr/local/mongodb/conf/route.conf
(2)在文件中填写如下内容:
pidfilepath = /usr/local/mongodb/route/log/route.pid logpath = /usr/local/mongodb/route/log/route.log logappend = true bind_ip = 0.0.0.0 port = 24017 fork = true #监听的配置服务器 configdb = configs/26.47.136.14:24000,26.47.136.15:24000,26.47.136.16:24000 #设置最大连接数 maxConns = 20000
(3)保存退出后,执行如下命令启动三台服务器的 route server:
注意:这里启动命令使用的是 mongos,而不是上面的 mongod
cd /usr/local/mongodb/bin ./mongos -f /usr/local/mongodb/conf/route.conf
(4)接着将路由服务器和分片副本集串联起来。首先连接任意一个节点的 route server:
/usr/local/mongodb/bin/mongo 26.47.136.14:24017
(5)执行如下命令使用 admin 数据库:
use admin
(6)执行如下命令将副本集添加到分片集中:
sh.addShard("shard1/26.47.136.14:24001,26.47.136.15:24001,26.47.136.16:24001"); sh.addShard("shard2/26.47.136.14:24002,26.47.136.15:24002,26.47.136.16:24002"); sh.addShard("shard3/26.47.136.14:24003,26.47.136.15:24003,26.47.136.16:24003");
(7)然后执行如下命令查看集群状态:
sh.status()
(8)显示如下内容说明整个集群已经搭建完毕:

附:分片测试
(1)由于并不是所有数据库和集合都需要分片。所以要对一个集合分片,首先要对这个集合的数据库启用分片,我们连接任意一个 route server:
/usr/local/mongodb/bin/mongo 26.47.136.14:24017
(2)执行如下命令使用 admin 数据库:
use admin
(3)假设我们需要让 hangge 这个数据库启用分片,只需要执行如下命令,便可以对该数据库内的集合进行分片了:
sh.enableSharding("hangge")
(4)切换到 hangge 数据库:
use hangge
(5)接着执行如下命令往 user 集合中插入 10 万条数据:
for(var i=100000;i<200000;i++){ db.users.insert({"username" : "user"+i , "created_at" : new Date()}); }
(6)查看集群信息,发现目前所有数据都插入到了第 1 个分片(shard1)中,这个是由于我们没有对集合指定分片键(shard key),因此该集合还不是个分片集合:
每个数据库创建时都会有一个 primary shard(不一定都是第 1 个分片):
- 如果该数据库下没有启用分片的集合,其所有数据都会存储到 primary shard。
- 如果该数据库下启用分片(即调用 shardCollection 命令)的集合,刚开始会生成一个 [minKey, maxKey] 的 chunk,该 chunk 初始会存储在 primary shard 上,然后随着数据的写入,不断的发生 chunk 分裂及迁移。
sh.status();

(8)执行如下命令查看 users 集合的分片情况,也提示我们该集合不是一个分片集合:
db.users.getShardDistribution()

(9)对集合分片时,我们要选择一个分片键(shard key)。分片键是集合的一个键,MongoDB 根据这个键拆分数据。由于只有被索引过的键才能够作为分片键,假设我们需要用 username 作为分片键,首先要在该键上创建索引:
(10)接着执行如下命令让 users 集合依据 username 对集合进行分片:
关于分片键(shard key)的注意事项:
- shard key 在分片完毕后是不能修改的,一个集合上只能有一个 shard key。
- shard key 上必须有索引(可以是以 shard key 开头的联合索引),如何集合不存在插入数据时 mongodb 会为 shard key 创建索引。但如果是已经存在的集合那么必须手动为 shard key 创建索引。
- 在分片集合中只有 _id 和 shard key 前缀的索引可以是 unique index,其他索引只能是普通索引。如果一个普通 key 上有 unique index 那么你不能以其他 key 为 shard key 对 collection 进行 sharding。
db.users.ensureIndex({"username" : 1})
(10)接着执行如下命令让 users 集合依据 username 对集合进行分片:
(1)mongodb 提供了 2 种策略来对集合进行分片:
- 范围(range)分片,可以使用多个字段作为分片键,并将数据划分为由分片键确定的连续范围。如:sh.shardCollection("hangge.users", {"username" : 1})
- 哈希(hash)分片:对单列使用 hash 索引作为分片键。如:sh.shardCollection("hangge.users", {"username" : "hashed"})
sh.shardCollection("hangge.users", {"username" : 1})
(11)再次查看 users 集合的分片情况,虽然目前已经是分片集合了,但是所有的数据仍然在 shard1 分片上。这是因为启用分片之后,数据会以 chunk 为单位(默认 64MB)根据片键均匀的分散到后端 1 或多个 shard 上。但目前记录数还没有超过阀值,因此只有 1 个 chunk,不会分裂。
db.users.getShardDistribution()

(12)为了便于观察数据分片,我们这里将 chunk 大小设置为 1MB(这个值大小可以自由设置为 1~1024MB 之间):
(13)虽然我们将 chunk 大小改下了,但 chunk 自动分裂只在插入的时候生效。我们再次切换到 hangge 数据库,并往 users 集合再插入 10 万条记录(执行时间会比较久,需要耐心等待):
(14)再次查看 users 集合的分片情况,可以发现数据已经均匀分布在三个分片上了:
use config db.settings.save({_id:"chunksize",value:1})
(13)虽然我们将 chunk 大小改下了,但 chunk 自动分裂只在插入的时候生效。我们再次切换到 hangge 数据库,并往 users 集合再插入 10 万条记录(执行时间会比较久,需要耐心等待):
use hangge for(var i=200000;i<300000;i++){ db.users.insert({"username" : "user"+i , "created_at" : new Date()}); }
(14)再次查看 users 集合的分片情况,可以发现数据已经均匀分布在三个分片上了:
db.users.getShardDistribution()

(15)而执行如下命令可以看到各个 chunk 包含的数据范围以及对应分片信息:
提示:由于 chunks 数量比较多,所以我们加了个 verbose 参数保证所有信息都能打印出来。否则会报“mongoDB sh.status() too many chunks to print”
sh.status({"verbose":1})
