如果说复制集是mongo为了备份数据,将一份数据存储在多台实例上的一种集群架构的话,那么当我们的数据存储过多,最好能将数据分开存储,这个时候就可以使用mongo的另一个多实例部署架构--数据分片
分片的概念就是将数据拆分,将其分散存储在不同机器上的过程。在很多中间件都有类似的概念,有些中间件中叫做'分区'概念,其实也是与分片类似的说法。基本上现代主流数据库都支持,用户手动管理,将数据存储在不同的集合中,连接彼此独立,但是手动维护带来的问题就是维护难度的增大,整体分片的调整,如增加节点,删除节点难度都变得很困难。而在MongoDB中,内部集成了自动分片功能,可以让分片操作对用户不可见,从而简化手动管理的成本。
分片初体验
想要开始搭建一个分片集群,我们先理解一下这个集群中的几个组件以及其作用,由于分片集群的目标一般是想要满足五台、十台甚至更多的实例,对外仅仅是和连接单机mongo一样,因此必须有其他组件帮忙隐藏其中的一些细节,因此整个mongo的分片集群架构就可以分成如下这样:
分片集群的三大组件
我们可以看到整个MongoDB Sharding集群中大体可以分为三个组件:
数据分片 -- shards
数据分片用于保存数据,保证了数据的高可用和一致性,这里每一个shard可以是独立的mongod实例,也可以是一个复制集,防止出现单点的故障问题。当然也可以选择把所有的shard的复制集放在一个服务器中启动多个mongod实例,在sharding中,每个node的database数据库可以选择分片也可以选择不分片,每一个db中都有一个独立的primary shard,在未分片的集合中就是存在于各自的primary shard中的
查询路由 -- mongos
我们可以看到,当我们想要连接分片集群,将数据分开存储,或者想要从分开存储的分片集群中,将我们想要的数据聚集查询出来,为了隐藏其中的细节,这个时候就需要使用查询路由--mongos
了,客户端连接不是直接连接分片集群中,而是连接到mongos,通过mongos进行一次路由过程的操作分发,而mongos通过查询维护的一个“内容列表”,里面记录了每个分片中按照什么规则(分片键)存储数据,每个分片中包含了哪些内容等,而客户端的请求到达mongos以后,mongos会根据记录的内容,选择性将请求分发到对应的一个或者多个分片服务实例上,然后再将所有分片的响应结果进行合并再统一输出给客户端程序,从而实现了屏蔽分片的细节,流程大体如下:
配置服务器 -- Config Server
配置服务器就是用于存储分片集群的配置信息的元数据,其中包含了mongos需要的shard的路由规则,路由键等信息,在mongo 3.2版本开始config server可以配置成为replica set了,在3.4以后官方已经规定config server必须为replica set,并且为了保证生产环境的稳定,rs中至少要有三个副本集成员存在
分片集群上手初体验
现在我们开始搭建一个简单的分片架构,在mongo3.4以后的版本中,一个分片集群,shard节点的数量至少要是两个,而config节点和路由mongos节点则至少需要一个,因此我们这里来通过在一台服务器上启动多个节点的方式 模拟分片架构,搭建两个shards复制集作为两个shard节点,搭建一个config server复制集作为config节点,而mongos我们则使用单节点启动方式,快速搭建一个分片集群架构快速上手体验一下
配置shards
cd /etc;
#首先创建一下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;
#接着创建shard的数据库文件存放目录
mkdir -p /var/lib/mongo_shard;
mkdir -p /var/lib/mongo_shard1;
mkdir -p /var/lib/mongo_shard2;
mkdir -p /var/lib/mongo_shard3;
#创建shard启动配置文件存放目录
mkdir mongo_shard/;
mkdir mongo_shard1/;
mkdir mongo_shard2/;
mkdir mongo_shard3/;
#创建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的是上篇文章中我们配置的复制集的配置文件,否则配置文件中还要配置复制集
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启动参数,主要修改systemLog下的path,storage下的dbPath,processManagement下的pidFilePath,这里我们分别修改为上面创建的log目录,db存储目录,以及pidFilePath存放目录,net下的启动端口这里分别修改为30017、30018和30019、30020,除此之外,由于shard我们要创建成为两个rs,因此,replication下的replSetName参数,mongo_shard和mongo_shard1我们将其指定为shard,mongo_shard和mongo_shard1我们将其指定为shard2,代表几个shard组成的复制集名称分别叫shard和shard2
vim mongo_shard/mongod.conf;
vim mongo_shard1/mongod.conf;
vim mongo_shard2/mongod.conf;
vim mongo_shard3/mongod.conf;
这里我们需要注意,由于当前的角色是shard,因此我们在给常规的配置文件参数修改后,还要添加sharding相关的配置,代表当前的角色为shard,如下:
添加如下配置即可:
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的目录和文件配置准备完成,我们开始将其分别启动,组成rs复制集:
/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;
#查看当前进程是否都存在
ps -ef|grep mongo
#可以看到 已经启动完成了
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复制集的任意一个节点,初始化复制集
/usr/bin/mongo --port 30017;
#初始化复制集
rs.initiate({ _id:"shard", members:[{ _id:0, host:"127.0.0.1:30017" },{ _id:1, host:"127.0.0.1:30018" }] });
#登录shard2复制集的任意一个节点,初始化复制集
/usr/bin/mongo --port 30019;
#初始化复制集
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;
#首先创建一下config_server的log存放的目录
mkdir -p /var/log/mongo/config_master;
mkdir -p /var/log/mongo/config_slave;
#接着创建config_server的数据库文件存放目录
mkdir -p /var/lib/mongo/config_master;
mkdir -p /var/lib/mongo/config_slave;
#创建config_server启动配置文件存放目录
mkdir mongo_config_master/;
mkdir mongo_config_slave/;
#创建pidFilePath存放目录
mkdir -p /var/run/mongo_config_master;
mkdir -p /var/run/mongo_config_slave;
#将mongo的配置文件分别cp到这些目录中,这里我们cp的是上篇文章中我们配置的复制集的配置文件,否则配置文件中还要配置复制集
cp -r mongod1.conf mongo_config_master/mongod.conf;
cp -r mongod1.conf mongo_config_slave/mongod.conf;
#vim修改每一个config_server的config启动参数,主要修改systemLog下的path,storage下的dbPath,processManagement下的pidFilePath,这里我们分别修改为上面创建的log目录,db存储目录,以及pidFilePath存放目录,net下的启动端口这里分别修改为37017和37018,除此之外,由于这里的节点我们要创建成为一个主-从的rs,因此,replication下的replSetName参数我们将其定义成config,代表几个节点组成的复制集名称叫config
vim mongo_config_master/mongod.conf;
vim mongo_config_slave/mongod.conf;
与上面shard的处理方式几乎一样,当然这里唯一的区别就是,分片角色的配置,上面是shard,这里需要配置为config,如下:
#sharding:
sharding:
# 分片角色
clusterRole: configsvr
一切配置准备好以后,我们开始启动config server复制集,完成初始化操作:
/usr/bin/mongod -f /etc/mongo_config_master/mongod.conf;
/usr/bin/mongod -f /etc/mongo_config_slave/mongod.conf;
#登录第一个复制集
/usr/bin/mongo --port 37017;
#初始化复制集
rs.initiate({ _id:"config", members:[{ _id:0, host:"127.0.0.1:37017" },{ _id:1, host:"127.0.0.1:37018" }] });
启动查询路由--mongos
这里我们只配置一个mongos实例,不去做集群启动了,因此我们需要和前面一样,创建四个目录,分别存放log,db文件和pid目录以及config配置文件的目录
cd /etc;
#首先创建一下shard的log存放的目录
mkdir -p /var/log/mongos_db;
#接着创建mongos的数据库文件存放目录
mkdir -p /var/lib/mongos;
#创建mongos启动配置文件存放目录
mkdir mongos/;
#创建pidFilePath存放目录
mkdir -p /var/run/mongos;
#复制 配置文件到目录下
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配置的,因此要注释当前的配置
#storage:
#dbPath: /var/lib/mongos/mongo1
#journal:
# enabled: true
#除此之外,还要删除replication相关的配置
#replication:
# replSetName: mongos
#启动mongos,注意这里用的是mongos启动
/usr/bin/mongos -f /etc/mongos/mongod.conf;
#连接mongos
mongo --port=27050;
这个时候我们连接进去,可以看到,当前的角色已经变了,成为了mongos
,需要注意的是我们要做分片操作,首先需要给需要分片的数据库启动分片,开启命令如下:
sh.enableSharding("db_name");
现在整个db的集合都可以进行分片了,不过需要注意的是,我们要对某个集合做分片,必须要选择一个片键,片键是集合文档内的一个键,Mongo会根据策略和这个片键进行拆分数据,我们要给集合启用分片之前,需要先在片键上创建索引:
db.collection.ensureIndex({"key":1});
然后我们就可以设置根据这个key来作为片键进行分片操作了:
sh.shardCollection("db.collection",{id:"key"});
配置完成后,我们来将shard信息添加进来,进行分片数据插入测试:
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");
#设置test库为允许sharding
sh.enableSharding("test");
#设置test中的tes集合的id按照hash的方式进行sharding
sh.shardCollection("test.tes",{id:"hashed"});
#插入几条数据测试一下
for(i=1;i<=1000;i++){db.tes.insert({id:i,name:"Leo"})}
这里我们插入数据完成以后,分别进入两个shard查看一下当前的数据,看是否生效
#登录第一个shard
/usr/bin/mongo --port 30017;
use test;
db.tes.find();
#结果如下:
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();
#结果如下:
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" }
可见当前我们的分片整个是已经搭建完成了,这个时候我们可以查看当前的分片集群的一个状态,通过如下命令:
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则是一个全局变量,除了可以管理以外,还内置了很多辅助函数,我们也可以选择使用sh.help();
查看可以使用的辅助函数有哪些