分片集群的组成

分片(存储):每个分片包含分片数据的子集。每个分片都可以部署为副本集。

mongos(路由):mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。

config servers(“调度”的配置):配置服务器存储群集的元数据和配置设置。 从MongoDB 3.4开始,必须将配置服务器部署为副本集(CSRS)。

分片集群的原理

基于分片切分后的数据块称为 chunk,一个分片后的集合会包含多个 chunk,每个 chunk 位于哪个分片(Shard) 则记录在 Config Server(配置服务器)上。

Mongos 在操作分片集合时,会自动根据分片键找到对应的 chunk,并向该 chunk 所在的分片发起操作请求。

数据是根据分片策略来进行切分的,而分片策略则由 分片键(ShardKey)+分片算法(ShardStrategy)组成。

MongoDB 支持两种分片算法:哈希分片和范围分片

哈希分片使用哈希索引来在分片集群中对数据进行划分。哈希索引计算某一个字段的哈希值作为索引值,这个值被用作片键

分片集群的配置

架构目标

两个分片节点副本集(3+3)+ 一个配置节点副本集(3)+两个路由节点(2),共11个服务节点。

配置分片(存储)节点副本集

第一套副本集shard 1

创建存放数据和日志的目录
mkdir -p /usr/local/mongodb/sharded_cluster/myshardrs01_27018/log \
/usr/local/mongodb/sharded_cluster/myshardrs01_27018/data/db \
/usr/local/mongodb/sharded_cluster/myshardrs01_27118/log \
/usr/local/mongodb/sharded_cluster/myshardrs01_27118/data/db \
/usr/local/mongodb/sharded_cluster/myshardrs01_27218/log \
/usr/local/mongodb/sharded_cluster/myshardrs01_27218/data/db
建立配置文件

--首先配置27018端口

vim /usr/local/mongodb/sharded_cluster/myshardrs01_27018/mongod.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.log"
    logAppend: true
storage:
    dbPath: "/usr/local/mongodb/sharded_cluster/myshardrs01_27018/data/db"
    journal:
        enabled: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.pid"
net:
    bindIp: localhost,192.168.106.10
    port: 27018
replication:
    replSetName: myshardrs01    #不同副本集名字不同
sharding:
    clusterRole: shardsvr

27118和27218配置文件同理,记得改掉端口

启动第一套副本集:主一副本一仲裁
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myshardrs01_27018/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myshardrs01_27118/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myshardrs01_27218/mongod.conf
初始化副本集和创建主节点,尽量连接主节点
[root@localhost ~]# mongo --port 27018
rs.initiate()
rs.status()
rs.add("192.168.106.10:27118")
rs.addArb("192.168.106.10:27218")  #添加仲裁节点
rs.conf()

第二套副本集 shard 2

第一套副本集不同之处:端口27318、27418、27518,副本集名称:myshardrs02

创建存放数据和日志的目录
mkdir -p /usr/local/mongodb/sharded_cluster/myshardrs02_27318/log \
/usr/local/mongodb/sharded_cluster/myshardrs02_27318/data/db \
/usr/local/mongodb/sharded_cluster/myshardrs02_27418/log \
/usr/local/mongodb/sharded_cluster/myshardrs02_27418/data/db \
/usr/local/mongodb/sharded_cluster/myshardrs02_27518/log \
/usr/local/mongodb/sharded_cluster/myshardrs02_27518/data/db
建立配置文件

--首先配置27318端口

vim /usr/local/mongodb/sharded_cluster/myshardrs02_27318/mongod.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/sharded_cluster/myshardrs02_27318/log/mongod.log"
    logAppend: true
storage:
    dbPath: "/usr/local/mongodb/sharded_cluster/myshardrs02_27318/data/db"
    journal:
        enabled: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/sharded_cluster/myshardrs02_27318/log/mongod.pid"
net:
    bindIp: localhost,192.168.106.10
    port: 27318
replication:
    replSetName: myshardrs02
sharding:
    clusterRole: shardsvr

27418和27518配置文件同理,记得改掉端口

启动第一套副本集:主一副本一仲裁
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myshardrs02_27318/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myshardrs02_27418/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myshardrs02_27518/mongod.conf
初始化副本集和创建主节点,尽量连接主节点
[root@localhost ~]# mongo --port 27318
rs.initiate()
rs.status()
rs.add("192.168.106.10:27418")
rs.addArb("192.168.106.10:27518")  #添加仲裁节点
rs.conf()

配置配置节点副本集 config server

准备存放数据和日志的目录

mkdir -p /usr/local/mongodb/sharded_cluster/myconfigrs_27019/log \
/usr/local/mongodb/sharded_cluster/myconfigrs_27019/data/db \
/usr/local/mongodb/sharded_cluster/myconfigrs_27119/log \
/usr/local/mongodb/sharded_cluster/myconfigrs_27119/data/db \
/usr/local/mongodb/sharded_cluster/myconfigrs_27219/log \
/usr/local/mongodb/sharded_cluster/myconfigrs_27219/data/db

建立配置文件

--首先配置27019端口

因为分片角色设为配置节点,所以clusterRole要改为 configsvr

vim /usr/local/mongodb/sharded_cluster/myconfigrs_27019/mongod.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/sharded_cluster/myconfigrs_27019/log/mongod.log"
    logAppend: true
storage:
    dbPath: "/usr/local/mongodb/sharded_cluster/myconfigrs_27019/data/db"
    journal:
        enabled: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/sharded_cluster/myconfigrs_27019/log/mongod.pid"
net:
    bindIp: localhost,192.168.106.10
    port: 27019
replication:
    replSetName: myconfigr
sharding:
    clusterRole: configsvr

27119和27219配置文件同理,记得改掉端口

启动副本集

[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myconfigrs_27019/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myconfigrs_27119/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/sharded_cluster/myconfigrs_27219/mongod.conf

初始化副本集和创建主节点,尽量连接主节点

[root@localhost ~]# mongo --port 27019
rs.initiate()
rs.status()
rs.add("192.168.106.10:27119")
rs.add("192.168.106.10:27219")  #这里就不用添加仲裁节点了,否则会报错
rs.conf()

配置路由节点

第一个路由节点

准备存放日志的目录
mkdir -p /usr/local/mongodb/sharded_cluster/mymongos_27017/log
建立配置文件
vim /usr/local/mongodb/sharded_cluster/mymongos_27017/mongos.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/sharded_cluster/mymongos_27017/log/mongod.log"
    logAppend: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/sharded_cluster/mymongos_27017/log/mongod.pid"
net:
    bindIp: localhost,192.168.106.10
    port: 27017
sharding:
    configDB: myconfigrs/192.168.106.10:27019,192.168.106.10:27119,192.168.106.10:27219
启动mongos
/usr/local/mongodb/bin/mongos -f /usr/local/mongodb/sharded_cluster/mymongos_27017/mongos.conf
客户端登录mongos

此时,写不进去数据,如果写数据会报错;原因: 通过路由节点操作,现在只是连接了配置节点,还没有连接分片数据节点,因此无法写入业务数据。

mongo --port 27017
mongos> use aabb
switched to db aabb
mongos> db.aa.insert({aa:"aa"})  #会显示"errmsg" ,无法插入
在路由节点上进行分片配置操作
添加分片副本集
mongos>sh.addShard("myshardrs01/192.168.106.10:27018,192.106.175.10:27118,192.168.106.10:27218")
#将第一套分片副本集添加进来
mongos> sh.status()
#查看分片状态情况
mongos>sh.addShard("myshardrs02/192.168.106.10:27318,192.168.106.10:27418,192.168.106.10:27518")
#将第二套分片副本集添加进来
mongos> sh.status()
#查看分片状态情况
#移除分片副本集

如果只剩下最后一个shard,是无法删除的。移除时会自动转移分片数据,需要一个时间过程。完成后,再次执行删除分片命令才能真正删除。

use admin
db.runCommand({removeShard: "myshardrs02"})
mongos> db.runCommand({removeShard: "myshardrs02"})
{
    "msg" : "removeshard completed successfully",
    "state" : "completed",
    "shard" : "myshardrs02",
    "ok" : 1,
    "operationTime" : Timestamp(1624670397, 2),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1624670397, 2),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

sh.status()
#可以看到转移数据过程

第二个路由节点

准备存放数据和日志的目录
mkdir -p /usr/local/mongodb/sharded_cluster/mymongos_27117/log
建立配置文件
systemLog:
    destination: file
    path: "/usr/local/mongodb/sharded_cluster/mymongos_27117/log/mongod.log"
    logAppend: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/sharded_cluster/mymongos_27117/log/mongod.pid"
net:
    bindIp: localhost,192.168.106.10
    port: 27117
sharding:
    configDB: myconfigrs/192.168.106.10:27019,192.168.106.10:27119,192.168.106.10:27219
启动mongos
/usr/local/mongodb/bin/mongos -f /usr/local/mongodb/sharded_cluster/mymongos_27117/mong
客户端登录mongos

使用mongo客户端登录27117,发现,第二个路由无需配置,因为分片配置都保存到了配置服务器中了

mongo --port 27117
mongos> sh.status()

配置 mongos分片功能

开启分片功能:

sh.enableSharding("库名")、sh.shardCollection("库名.集合名",{"key":1})

mongo --port 27017
mongos> sh.enableSharding("articledb")

配置集合分片:

你必须使用 sh.shardCollection() 方法指定集合和分片键。

sh.shardCollection(namespace, key, unique)

参数:

namespace——

要分片共享的目标集合的命名空间

key——

用作分片键的索引规范文档

unique——

当值为true情况下,片键字段上会限制为确保是唯一索引

分片规则一:哈希策略

MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。使用nickname作为片键,根据其值的哈希值进行数据分片。

如无特殊情况,一般推荐使用 Hash Sharding。

例子:

sh.shardCollection("articledb.comment",{"nickname":"hashed"})
#对comment这个集合使用hash方式分片
sh.status()
# 查看分片状态

登录mongs后,向comment循环插入1000条数据做测试

mongos> use articledb
switched to db articledb
mongos> for(var i=1;i<=1000;i++){db.comment.insert({_id:i+"",nickname:"Test"+i})}

WriteResult({ "nInserted" : 1 })
mongos> db.comment.count()
1000

分别登录两个分片(存储)节点的主节点27018和27318,统计文档数量

myshardrs01:

mongo --port 27018
PRIMARY> use articledb
switched to db articledb
myshardrs01:PRIMARY> db.comment.count()
505

myshardrs02:

mongo --port 27318
PRIMARY> use articledb
switched to db articledb
myshardrs02:PRIMARY> db.comment.count()
495
myshardrs02:PRIMARY> db.comment.find()
{ "_id" : "1", "nickname" : "Test1" }
{ "_id" : "3", "nickname" : "Test3" }
{ "_id" : "5", "nickname" : "Test5" }
{ "_id" : "6", "nickname" : "Test6" }
{ "_id" : "7", "nickname" : "Test7" }
{ "_id" : "10", "nickname" : "Test10" }
{ "_id" : "11", "nickname" : "Test11" }
{ "_id" : "12", "nickname" : "Test12" }
{ "_id" : "14", "nickname" : "Test14" }
{ "_id" : "17", "nickname" : "Test17" }
{ "_id" : "22", "nickname" : "Test22" }
{ "_id" : "23", "nickname" : "Test23" }
{ "_id" : "24", "nickname" : "Test24" }
{ "_id" : "28", "nickname" : "Test28" }
{ "_id" : "29", "nickname" : "Test29" }
{ "_id" : "30", "nickname" : "Test30" }
{ "_id" : "34", "nickname" : "Test34" }
{ "_id" : "37", "nickname" : "Test37" }
{ "_id" : "39", "nickname" : "Test39" }
{ "_id" : "44", "nickname" : "Test44" }
#数据是比较分散的
分片规则二:范围策略

对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。假设有一个数字的片键,想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点.MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同 一个分片中。

例子:

mongos> sh.shardCollection("articledb.author",{"age":1})
# 如使用作者年龄字段作为片键,按照年龄的值进行分片

登录mongs后,向author循环插入20000条测试数据

mongos> use articledb
switched to db articledb
mongos> for (var i=1;i<=2000;i++){db.author.save({"name":"test"+i,"age":NumberInt(i%120)})}

WriteResult({ "nInserted" : 1 })
mongos> db.author.count()
2000

分别登录两个分片(存储)节点的主节点27018和27318,统计文档数量

myshardrs02:PRIMARY> db.author.count()
2000
#发现所有的数据都集中在了一个分片副本上