环境:2组分片+1组config+1个路由mongos 2组分片和1组config为副本集集群
192.168.1.100 shared1_1
192.168.1.101 shared1_2
192.168.1.102 shared1_3
192.168.1.103 shared2_1
192.168.1.104 shared2_2
192.168.1.105 shared2_3
192.168.1.106 config1
192.168.1.107 config2
192.168.1.108 config3
192.168.1.109 mongos
配置展示
config server 配置文件
systemLog: # 目标是文件 destination: file # 日志以追加的方式写入 logAppend: true # 日志路径 path: /usr/local/mongodb/log/mongodb.log # quiet: false storage: # 数据库的存储路径 dbPath: /usr/local/mongodb/data # directoryPerDB: false engine: wiredTiger syncPeriodSecs: 61 # 每个同步周期时间 journal: enabled: true processManagement: fork: true pidFilePath: /usr/local/mongodb/run/mongodb.pid net: port: 27017 # 指定程序绑定IP bindIp: 0.0.0.0 replication: replSetName: configs sharding: clusterRole: configsvr
shared server 配置文件
systemLog: destination: file logAppend: true path: /usr/local/mongodb/log/mongodb.log quiet: false storage: dbPath: /usr/local/mongodb/data directoryPerDB: false engine: wiredTiger syncPeriodSecs: 61 journal: enabled: true processManagement: fork: true pidFilePath: /usr/local/mongodb/run/mongodb.pid net: port: 27017 bindIp: 0.0.0.0 replication: # 多个分片修改分片名称即可 replSetName: shared1 sharding: clusterRole: shardsvr
mongos server 配置文件
systemLog: destination: file logAppend: true path: /usr/local/mongodb/log/mongodb.log quiet: false processManagement: fork: true pidFilePath: /usr/local/mongodb/run/mongodb.pid net: port: 27017 bindIp: 0.0.0.0 # 设置最大连接数 maxIncomingConnections: 1024 sharding: # 设置config server的地址 configDB: configs/192.168.1.106:27017,192.168.1.107:27017,192.168.1.108:27017
分别启动分片集群以及config集群
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/etc/mongod.conf
启动mongos路由
192.168.1.109 mongos
/usr/local/mongodb/bin/mongos -f /usr/local/mongodb/etc/mongos.conf
客户端登录mongos
/usr/local/mongodb/bin/mongo --host 192.168.1.109 --port 27017
此时,写不进去数据,如果写数据会报错:
mongos> use aadb switched to db aadb mongos> db.aa.insert({aa:"aa"}) WriteCommandError({ "ok" : 0, "errmsg" : "unable to initialize targeter for write op for collection aa.aa :: caused by :: Database aa not found :: caused by :: No shards found", "code" : 70, "codeName" : "ShardNotFound", "operationTime" : Timestamp(1564600123, 2), "$clusterTime" : { "clusterTime" : Timestamp(1564600123, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } })
原因: 通过路由节点操作,现在只是连接了配置节点,还没有连接分片数据节点,因此无法写入业务数据。
在路由节点上进行分片配置操作
使用命令添加分片:
(1)添加分片:
语法:
sh.addShard("IP:Port")
将第一套分片副本集添加进来:
mongos> sh.addShard("shared1/192.168.1.100:27017,192.168.1.101:27017,192.168.1.102:27217") { "shardAdded" : "myshardrs01", "ok" : 1, "operationTime" : Timestamp(1564611970, 4), "$clusterTime" : { "clusterTime" : Timestamp(1564611970, 4), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
查看分片情况:
--- Sharding Status --- sharding version: { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5f2a1b69c15b4b6bbf33d894") } shards: { "_id" : "shared1", "host" : "shared1/192.168.1.100:27017,192.168.1.101:27017", "state" : 1 } active mongoses: "4.0.19" : 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: No recent migrations databases: { "_id" : "articledb", "primary" : "hqmongodb", "partitioned" : false, "version" : { "uuid" : UUID("8b93073c-bec6-490c-9d2c-9f960dd9236f"), "lastMod" : 1 } } { "_id" : "config", "primary" : "config", "partitioned" : true }
删除分片
use admin
db.runCommand( { removeShard: "myshardrs02" } )
注意:如果只剩下最后一个 shard,是无法删除的
移除时会自动转移分片数据,需要一个时间过程。
完成后,再次执行删除分片命令才能真正删除。
(2)开启分片功能:sh.enableSharding("库名")、sh.shardCollection("库名.集合名",{"key":1})
在mongos上的articledb数据库配置sharding:
mongos> sh.enableSharding("articledb") { "ok" : 1, "operationTime" : Timestamp(1564612296, 5), "$clusterTime" : { "clusterTime" : Timestamp(1564612296, 5), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
(3)集合分片
对集合分片,你必须使用 sh.shardCollection() 方法指定集合和分片键。
语法:
sh.shardCollection(namespace, key, unique)
分片规则一:哈希策略
对于 基于哈希的分片 ,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块.在使用基于哈希分片的系统中,拥有”相近”片键的文档 很可能不会 存储在同一个数据块中,因此数据的分离性更好一些.
使用nickname作为片键,根据其值的哈希值进行数据分片
mongos> sh.shardCollection("articledb.comment",{"nickname":"hashed"}) { "collectionsharded" : "articledb.comment", "collectionUUID" : UUID("ddea6ed8-ee61-4693-bd16-196acc3a45e8"), "ok" : 1, "operationTime" : Timestamp(1564612840, 28), "$clusterTime" : { "clusterTime" : Timestamp(1564612840, 28), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
分片规则二:范围策略
于 基于范围的分片 ,MongoDB按照片键的范围把数据分成不同部分.假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点.MongoDB把这条直线划分为更短的不重叠的片段,并称之为 数据块 ,每个数据块包含了片键在一定范围内的数据.在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中.
如使用作者年龄字段作为片键,按照点赞数的值进行分片:
mongos> sh.shardCollection("articledb.author",{"age":1}) { "collectionsharded" : "articledb.author", "collectionUUID" : UUID("9a47bdaa-213a-4039-9c18-e70bfc369df7"), "ok" : 1, "operationTime" : Timestamp(1567512803, 13), "$clusterTime" : { "clusterTime" : Timestamp(1567512803, 13), "signature" : { "hash" : BinData(0,"eE9QT5yE5sL1Tyr7+3U8GRy5+5Q="), "keyId" : NumberLong("6732061237309341726") } } }
注意的是:
1)一个集合只能指定一个片键,否则报错。
2)一旦对一个集合分片,分片键和分片值就不可改变。 如:不能给集合选择不同的分片键、不能更新
分片键的值。
3)根据age索引进行分配数据。
查看分片状态:
articledb.author shard key: { "age" : 1 } unique: false balancing: true chunks: myshardrs01 1 { "age" : { "$minKey" : 1 } } -->> { "age" : { "$maxKey" : 1 } } on : myshardrs01 Timestamp(1, 0)
基于范围的分片方式与基于哈希的分片方式性能对比:
基于范围的分片方式提供了更高效的范围查询,给定一个片键的范围,分发路由可以很简单地确定哪个数据块存储了请求需要的数据,并将请求转发到相应的分片中.不过,基于范围的分片会导致数据在不同分片上的不均衡,有时候,带来的消极作用会大于查询性能的积极作用.比如,如果片键所在的字段是线性增长的,一定时间内的所有请求都会落到某个固定的数据块中,最终导致分布在同一个分片中.在这种情况下,一小部分分片承载了集群大部分的数据,系统并不能很好地进行扩展.
与此相比,基于哈希的分片方式以范围查询性能的损失为代价,保证了集群中数据的均衡.哈希值的随机性使数据随机分布在每个数据块中,因此也随机分布在不同分片中.但是也正由于随机性,一个范围查询很难确定应该请求哪些分片,通常为了返回需要的结果,需要请求所有分片.如无特殊情况,一般推荐使用 Hash Sharding。而使用 _id 作为片键是一个不错的选择,因为它是必有的,你可以使用数据文档 _id 的哈希作为片键。这个方案能够是的读和写都能够平均分布,并且它能够保证每个文档都有不同的片键所以数据块能够很精细。似乎还是不够完美,因为这样的话对多个文档的查询必将命中所有的分片。虽说如此,这也是一种比较好的方案了。
显示集群的详细信息:
mongos> db.printShardingStatus()
查看均衡器是否工作(需要重新均衡时系统才会自动启动,不用管它):
mongos> sh.isBalancerRunning() false
查看当前 Balancer状态:
mongos> sh.getBalancerState() true
分片后插入数据测试
测试一(哈希规则):登录mongs后,向comment循环插入1000条数据做测试:
mongos> use articledb switched to db articledb mongos> for(var i=1;i<=1000;i++) {db.comment.insert({_id:i+"",nickname:"BoBo"+i})} WriteResult({ "nInserted" : 1 }) mongos> db.comment.count() 1000
提示: js的语法,因为mongo的shell是一个JavaScript的shell。
注意:从路由上插入的数据,必须包含片键,否则无法插入。
分别登陆两个片的主节点,统计文档数量
测试二(范围规则):登录mongs后,向comment循环插入1000条数据做测试:
mongos> use articledb switched to db articledb mongos> for(var i=1;i<=20000;i++) {db.author.save({"name":"BoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoBoB oBoBoBoBoBoBoBoBo"+i,"age":NumberInt(i%120)})} WriteResult({ "nInserted" : 1 }) mongos> db.comment.count() 20000
插入成功后,仍然要分别查看两个分片副本集的数据情况。
提示:
如果查看状态发现没有分片,则可能是由于以下原因造成了:
1)系统繁忙,正在分片中。
2)数据块(chunk)没有填满,默认的数据块尺寸(chunksize)是64M,填满后才会考虑向其他片的
数据块填充数据,因此,为了测试,可以将其改小,这里改为1M,操作如下:
use config
db.settings.save( { _id:"chunksize", value: 1 } )
测试完改回来:
db.settings.save( { _id:"chunksize", value: 64 } )
注意:要先改小,再设置分片。为了测试,可以先删除集合,重新建立集合的分片策略,再插入数据测试即可。