分片就是将数据库分布在多台服务器上
为什么会用到分片?
1 机器的磁盘空间不足
2 单个的mongoDB服务器已经不能满足大量的插入操作
3 想通过把大数据放到内存中来提高性能
1.组成介绍:
1.分片
MongoDB分片集群将数据分布在一个或多个分片上。每个分片都部署成一个MongoDB副本集,该副本集保存了集群整体数据的一部分。因为每个分片都是一个副本集没所以它们拥有自己的复制机制,能够自动进行故障转移。直接连接单个分片只能查看到部分数据。
2.mongos路由器
用于连接整个集群,它将所有的读写请求指引到合适的分片上。这是一个前端路由,客户端由此接入,然后询问 Config Servers 需要到哪个 Shard 上查询或保存记录,再连接相应的 Shard 进行操作,最后将结果返回给客户端。客户端只需要将原本发给 mongod 的查询或更新请求原封不动地发给 Routing Process,而不必关心所操作的记录
存储在哪个 Shard 上。
3.配置服务器
持久化分片集群的元数据。这些数据包括:全局集群配置;每个数据库、集合和特定范围数据的位置;一份变更记录,保存了数据在分片之间进行迁移的历史信息。
2示例
实现一个这样架构的分片集群:
s0Server:18000
s1Server:18001
configServer:18200
route:18010
2.1 启动 Shard Server
--创建数据目录
E:\MyProgram\MongodbDATA\09\s0
E:\MyProgram\MongodbDATA\09\s1
E:\MyProgram\MongodbDATA\09\c
编辑配置文件:
s0Server.conf
dbpath=E:\MyProgram\MongodbDATA\09\s0
bind_ip = 127.0.0.1
port=18000
shardsvr=true
s1Server.conf
dbpath=E:\MyProgram\MongodbDATA\09\s1
bind_ip = 127.0.0.1
port=18001
shardsvr=true
启动 Shard Server 实例 s0Server:
mongod --config s0Server.conf
启动 Shard Server 实例 s1Server:
mongod --config s1Server.conf
2.2 启动 Config Server
1.创建数据目录:
2.配置文件configServer.conf
configsvr=true
dbpath = E:\MyProgram\MongodbDATA\09\c
port = 18200
bind_ip = 127.0.0.1
3.启动 Config Server 实例:
mongod --config configServer.conf
2.3 启动 Route Process
启动 Route Server 实例:
mongos --port 18010 --configdb 127.0.0.1:18200 --chunkSize=1
mongos 启动参数中, chunkSize 这一项是用来指定 chunk 的大小的,单位是 MB,默认大小
为 200MB,为了方便测试 Sharding 效果,我们把 chunkSize 指定为 1MB。
2.4 配置 Sharding
接下来,我们使用 MongoDB Shell 登录到 mongos,添加 Shard 节点
[root@localhost ~]# /Apps/mongo/bin/mongo admin --port 40000 --此操作需要连接 admin 库
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:40000/admin
> db.runCommand({ addshard:"localhost:18000" }) --添加 Shard Server1
{ "shardAdded" : "shard0000", "ok" : 1 }
> db.runCommand({ addshard:"localhost:18001" }) --添加 Shard Server2
{ "shardAdded" : "shard0001", "ok" : 1 }
> db.runCommand({ enablesharding:"test" }) --设置分片存储的数据库
{ "ok" : 1 }
> db.runCommand({ shardcollection: "test.users", key: { _id:1 }}) --设置分片的集合名称,且必须指定 Shard Key,系统会自动创建索引
{ "collectionsharded" : "test.users", "ok" : 1 }
>
2.5 验证 Sharding 正常工作
我们已经对 test.users 表进行了分片的设置,下面我们们插入50w条数据看一下结果
> use test
switched to db test
> for (var i = 1; i <= 500000; i++) db.users.insert({age:i, name:"tom", addr:"Beijing",country:"China"})
>mongos> db.users.stats()
{
"sharded" : true, ##说明此表已经被分片了
"flags" : 1,
"ns" : "test.users",
"count" : 500000,
"numExtents" : 16,
"size" : 44000016,
"storageSize" : 66666496,
"totalIndexSize" : 16253888,
"indexSizes" : {
"_id_" : 16253888
},
"avgObjSize" : 88.000032,
"nindexes" : 1,
"nchunks" : 5,
"shards" : {
"shard0000" : { ##第一个分片大约25M
"ns" : "test.users",
"count" : 289589,
"size" : 25483840,
"avgObjSize" : 88.0000276253587,
"storageSize" : 33333248,
"numExtents" : 8,
"nindexes" : 1,
"lastExtentSize" : 12083200,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 9410576,
"indexSizes" : {
"_id_" : 9410576
},
"ok" : 1
},
"shard0001" : { ##第二个分片大约18M
"ns" : "test.users",
"count" : 210411,
"size" : 18516176,
"avgObjSize" : 88.00003802082591,
"storageSize" : 33333248,
"numExtents" : 8,
"nindexes" : 1,
"lastExtentSize" : 12083200,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 6843312,
"indexSizes" : {
"_id_" : 6843312
},
"ok" : 1
}
},
"ok" : 1
}
2.6我们看一下磁盘上的物理文件情况
看上述结果,表明 test.users 集合已经被分片处理了,但是通过 mongos 路由,我们并感觉不到是数据存放在哪个 shard 的 chunk 上的,这就是 MongoDB 用户体验上的一个优势,即对用户是透明的。
3管理维护 Sharding
3.1 列出所有的 Shard Server
> use admin
> db.runCommand({ listshards: 1 }) --列出所有的 Shard Server
{
"shards" : [
{
"_id" : "shard0000",
"host" : "localhost:18000"
},
{
"_id" : "shard0001",
"host" : "localhost:18001"
}
],
"ok" : 1
}
3.2 查看 Sharding 信息
> printShardingStatus() --查看 Sharding 信息
--- Sharding Status ---
sharding version: { "_id" : 1, "version" : 3 }
shards:
{ "_id" : "shard0000", "host" : "localhost:18000" }
{ "_id" : "shard0001", "host" : "localhost:18001" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : true, "primary" : "shard0000" }
test.users chunks:
shard0000 4
shard0001 1
{ "_id" : { $minKey : 1 } } -->> { "_id" : ObjectId("585
fbdc22730e421633a26a8") } on : shard0000 { "t" : 2000, "i" : 1 }
{ "_id" : ObjectId("585fbdc22730e421633a26a8") } -->> {
"_id" : ObjectId("585fbdc72730e421633a3f3c") } on : shard0000 { "t" : 1000, "i"
: 3 }
{ "_id" : ObjectId("585fbdc72730e421633a3f3c") } -->> {
"_id" : ObjectId("585fbdea2730e421633d7527") } on : shard0001 { "t" : 3000, "i"
: 1 }
{ "_id" : ObjectId("585fbdea2730e421633d7527") } -->> {
"_id" : ObjectId("585fbe432730e4216340ac0c") } on : shard0000 { "t" : 3000, "i"
: 2 }
{ "_id" : ObjectId("585fbe432730e4216340ac0c") } -->> {
"_id" : { $maxKey : 1 } } on : shard0000 { "t" : 3000, "i" : 3 }
3.3 判断是否是 Sharding
> db.runCommand({ isdbgrid:1 })
{ "isdbgrid" : 1, "hostname" : "acer", "ok" : 1 }
>
3.4 对现有的表进行 Sharding
刚才我们是对表 test.users 进行分片了,下面我们将对库中现有的未分片的表 test.users_2 进行分片处理
先建一个表test.users2:
> use test
switched to db test
插入数据:
> for (var i = 1; i <= 500000; i++) db.users.insert({age:i, name:"tom", addr:"Beijing",country:"China"})
表最初状态如下,可以看出他没有被分片过:
> db.users2.stats()
{
"ns" : "test.users_2",
"sharded" : false,
"primary" : "shard0000",
"ns" : "test.users_2",
"count" : 500000,
"size" : 48000016,
"avgObjSize" : 96.000032,
"storageSize" : 61875968,
"numExtents" : 11,
"nindexes" : 1,
"lastExtentSize" : 15001856,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 20807680,
"indexSizes" : {
"_id_" : 20807680
},
"ok" : 1
}
对其进行分片处理:
首先切换到admin用户
> use admin
switched to db admin
执行分片命令:传入参数:1.要被分片的文档 2.指定片键
> db.runCommand({ shardcollection: "test.users2", key: { _id:1 }})
{ "collectionsharded" : "test.users2", "ok" : 1 }
再次查看分片后的表的状态,可以看到它已经被我们分片了
> use test
switched to db test
> db.users2.stats()
{
"sharded" : true,
"ns" : "test.users_2",
"count" : 505462,
……
"shards" : {
"shard0000" : {
"ns" : "test.users2",
……
"ok" : 1
},
"shard0001" : {
"ns" : "test.users2",
……
"ok" : 1
}
},
"ok" : 1
}
>
3.5 新增分片服务器 Shard Server
刚才我们演示的是新增分片表,接下来我们演示如何新增 Shard Server
启动一个新 Shard Server 进程
[root@localhost ~]# mkdir /data/shard/s2
[root@localhost ~]# /Apps/mongo/bin/mongod --shardsvr --port 20002 --dbpath /data/shard/s2
--fork --logpath /data/shard/log/s2.log --directoryperdb
all output going to: /data/shard/log/s2.log
forked process: 6772
配置新 Shard Server
[root@localhost ~]# /Apps/mongo/bin/mongo admin --port 40000
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:40000/admin
## 添加分片
> db.runCommand({ addshard:"localhost:20002" })
{ "shardAdded" : "shard0002", "ok" : 1 }
> printShardingStatus()
--- Sharding Status ---
sharding version: { "_id" : 1, "version" : 3 }
shards:
{ "_id" : "shard0000", "host" : "localhost:20000" }
{ "_id" : "shard0001", "host" : "localhost:20001" }
{ "_id" : "shard0002", "host" : "localhost:20002" }#新增的Shard Server
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : true, "primary" : "shard0000" }
test.users chunks:
shard0002 2
shard0000 21
shard0001 21
too many chunksn to print, use verbose if you want to force print
test.users_2 chunks:
shard0001 46
shard0002 1
shard0000 45
too many chunksn to print, use verbose if you want to force print
查看分片表状态,以验证新 Shard Server
> use test
switched to db test
> db.users_2.stats()
{
"sharded" : true,
"ns" : "test.users_2",
……
"shard0002" : { ## 新的 Shard Server 已有数据
"ns" : "test.users_2",
"count" : 21848,
"size" : 2097408,
"avgObjSize" : 96,
"storageSize" : 2793472,
"numExtents" : 5,
"nindexes" : 1,
"lastExtentSize" : 2097152,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 1277952,
"indexSizes" : {
"_id_" : 1277952
},
"ok" : 1
}
},
"ok" : 1
}
>
3.6 移除 Shard Server
有些时候有于硬件资源有限,所以我们不得不进行一些回收工作,下面我们就要将刚刚启用的 Shard Server 回收
系统首先会将在这个即将被移除的 Shard Server 上的数据先平均分配到其它的 Shard Server 上,然后最终在将这个 Shard Server 踢下线, 我们需要不停的调用db.runCommand({“removeshard” : “localhost:18002”});来观察这个移除操作进行到哪里了:
> use admin
switched to db admin
> db.runCommand({"removeshard" : "localhost:18002"});
{
"msg" : "draining started successfully",
"state" : "started", ##启动
"shard" : "shard0002",
"ok" : 1
}
> db.runCommand({"removeshard" : "localhost:18002"});
{
"msg" : "draining ongoing",
"state" : "ongoing", ##即将开始
"remaining" : {
"chunks" : NumberLong(44), ##块的个数
"dbs" : NumberLong(0)
},
"ok" : 1
}
……
> db.runCommand({"removeshard" : "localhost:18002"});
{
"msg" : "draining ongoing",
"state" : "ongoing",
"remaining" : {
"chunks" : NumberLong(1),
"dbs" : NumberLong(0)
},
"ok" : 1
}
> db.runCommand({"removeshard" : "localhost:18002"});
{
"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "shard0002",
"ok" : 1
}
> db.runCommand({"removeshard" : "localhost:18002"});
{
"assertion" : "can't find shard for: localhost:20002",
"assertionCode" : 13129,
"errmsg" : "db assertion failure",
"ok" : 0
}
最终移除后,当我们再次调用 db.runCommand({“removeshard” : “localhost:18002”});的时候系统
会报错,已便通知我们不存在 18002 这个端口的 Shard Server 了,因为它已经被移除掉了。
接下来我们看一下表中的数据分布:
> use test
switched to db test
> db.users2.stats()
{
"sharded" : true,
"ns" : "test.users_2",
"count" : 500000,
"size" : 48000000,
"avgObjSize" : 96,
"storageSize" : 95203584,
"nindexes" : 1,
"nchunks" : 92,
"shards" : {
"shard0000" : {
"ns" : "test.users_2",
"count" : 248749,
"size" : 23879904,
"avgObjSize" : 96,
"storageSize" : 61875968,
"numExtents" : 11,
"nindexes" : 1,
"lastExtentSize" : 15001856,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 13033472,
"indexSizes" : {
"_id_" : 13033472
},
"ok" : 1
},
"shard0001" : {
"ns" : "test.users_2",
"count" : 251251,
"size" : 24120096,
"avgObjSize" : 96,
"storageSize" : 33327616,
"numExtents" : 8,
"nindexes" : 1,
"lastExtentSize" : 12079360,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 10469376,
"indexSizes" : {
"_id_" : 10469376
},
"ok" : 1
}
},
"ok" : 1
}
可以看出数据又被平均分配到了另外 2 台 Shard Server 上了,对业务没什么特别大的影响。
我们可以发现,当我们新增 Shard Server 后数据自动分布到了新 Shard 上,这是由 MongoDB 内部自已实现的。
实际生产环境中最常用的结构–副本集+分片(Replica sets + Sharding)
MongoDB Auto-Sharding 解决了海量存储和动态扩容的问题,但离实际生产环境所需的高可靠、高可用还有些距离,所以有了” Replica Sets + Sharding”的解决方案:
Shard:
使用 Replica Sets,确保每个数据节点都具有备份、自动容错转移、自动恢复能力。
Config:
使用 3 个配置服务器,确保元数据完整性
Route:
使用 3 个路由进程,实现负载平衡,提高客户端接入性能
以下我们配置一个 Replica Sets + Sharding 的环境,架构图如下: