环境
jar版本比较老,存在性能问题。
比如:map-reduce
查询过慢,Java创建线程问题引起,有时间可以写一篇。
MongoDB版本:mongodb-2.6.8
分片知识
从图中可以看到有四个组件:mongos、config server、shard、replica set。
mongos
数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的MongoDB请求都没有办法操作。
config server
顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从config server加载配置信息,以后如果配置服务器信息变化会通知到所有的mongos更新自己的状态,这样mongos就能继续准确路由。在生产环境通常有多个config server配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货,MongoDB集群就不会挂掉。
shard
这就是传说中的分片。上面提到一个机器就算能力再大也有天花板,就像军队打仗一样,一个人再厉害喝血瓶也拼不过对方的一个师。俗话说三个臭皮匠顶个诸葛亮,这个时候团队的力量就凸显出来了。在互联网也是这样,一台普通的机器做不了的多台机器来做,如下图。
一台机器的一个数据表Collection1
存储了1T数据,压力太大了!在分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。也许有人问一台机器硬盘加大一点不就可以了,为什么要分给四台机器呢?不要光想到存储空间,实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在MongoDB集群只要设置好了分片规则,通过mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。在生产环境中分片的片键可要好好设置,这个影响到了怎么把数据均匀分到多个分片机器上,不要出现其中一台机器分了1T,其他机器没有分到的情况,这样还不如不分片!
副本集知识
mongoDB官方已经不建议使用主从模式了,替代方案是采用副本集的模式。
官方最新文档已经移除主从复制,采用副本集。
REMOVED
MongoDB 4.0 removes support for master-slave replication. Before you can upgrade to MongoDB 4.0, if your deployment uses master-slave replication, you must upgrade to a replica set.
To convert your master-slave replication, see Convert a Master-Slave Deployment to a Replica Set.
那什么是副本集呢?打魔兽世界总说打副本,其实这两个概念差不多一个意思。游戏里的副本是指玩家集中在高峰时间去一个场景打怪,会出现玩家暴多怪物少的情况,游戏开发商为了保证玩家的体验度,就为每一批玩家单独开放一个同样的空间同样的数量的怪物,这一个复制的场景就是一个副本,不管有多少个玩家各自在各自的副本里玩不会互相影响。MongoDB的副本也是这个,主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。难怪MongoDB官方推荐使用这种模式。我们来看看MongoDB副本集的架构图:
由图可以看到客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。我们看一下主服务器挂掉后的架构:
副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。
分片副本集拓扑图架构
介绍完分片和副本集的知识,现在开始此次MongoDB分片副本集的拓扑图
设计
- 3个config管理分片的信息,3个mongos作为请求分发的入口,如果一个config,或者mongos断掉,依然还有其他mongos和config在运行。
图1
- 服务器1:分片A的增删查改。
- 服务器2:分片A的副本集同步数据,分片B的增删查改。
- 服务器3:分片B的副本集同步。
图2
- 服务器1:分片A的增删查改。
- 服务器2:分片A的副本集同步数据,分片B的副本集同步。
- 服务器3: 分片B的增删查改。
图1和图2相比较:图2降低了分片B的压力(增删查改),3台服务器,挂掉2个服务器,实际上都是会出现问题。
安装部署(以图1)
端口
- config:1000,1001,1002
- mongos:2000,2001,2002
- shared:3000,3001
- repliSet:4000,4001
- arb:5000,5001
MongoDB安装命令
服务器11
2
3
4mongod.exe --logpath=F:\log\MongoDB-Shared\A\config\log.txt --dbpath=F:\db\MongoDB-Shared\A\config
mongod.exe --logpath=F:\log\MongoDB-Shared\A\mongos\log.txt --dbpath=F:\db\MongoDB-Shared\A\mongos
mongod.exe --logpath=F:\log\MongoDB-Shared\A\mongodb-A-shared\log.txt --dbpath=F:\db\MongoDB-Shared\A\mongodb-A-shared
mongod.exe --logpath=F:\log\MongoDB-Shared\A\mongodb-B-arb\log.txt --dbpath=F:\db\MongoDB-Shared\A\mongodb-B-arb
服务器21
2
3
4mongod.exe --logpath=F:\log\MongoDB-Shared\B\config\log.txt --dbpath=F:\db\MongoDB-Shared\B\config
mongod.exe --logpath=F:\log\MongoDB-Shared\B\mongos\log.txt --dbpath=F:\db\MongoDB-Shared\B\mongos
mongod.exe --logpath=F:\log\MongoDB-Shared\B\mongodb-A-replset\log.txt --dbpath=F:\db\MongoDB-Shared\B\mongodb-A-replset
mongod.exe --logpath=F:\log\MongoDB-Shared\B\mongodb-B-shared\log.txt --dbpath=F:\db\MongoDB-Shared\B\mongodb-B-shared
服务器31
2
3
4mongod.exe --logpath=F:\log\MongoDB-Shared\C\config\log.txt --dbpath=F:\db\MongoDB-Shared\C\config
mongod.exe --logpath=F:\log\MongoDB-Shared\C\mongos\log.txt --dbpath=F:\db\MongoDB-Shared\C\mongos
mongod.exe --logpath=F:\log\MongoDB-Shared\C\mongodb-A-arb\log.txt --dbpath=F:\db\MongoDB-Shared\C\mongodb-A-arb
mongod.exe --logpath=F:\log\MongoDB-Shared\C\mongodb-B-replset\log.txt --dbpath=F:\db\MongoDB-Shared\C\mongodb-B-replset
config部署命令
服务器11
mongod --dbpath=F:\db\MongoDB-Shared\A\config --port 1000
服务器21
mongod --dbpath=F:\db\MongoDB-Shared\B\config --port 1001
服务器31
mongod --dbpath=F:\db\MongoDB-Shared\C\config --port 1002
mongos部署命令
方法1
这样配置,每个mongos只会对应本机的config。
服务器11
mongos --port 2000 --configdb=127.0.0.1:1000
服务器21
mongos --port 2001 --configdb=127.0.0.1:1001
服务器31
mongos --port 2002 --configdb=127.0.0.1:1002
方法2
在每个服务器里配置mongos,这样mongos对应3个服务器的config。1
mongos --port 2000 --configdb=127.0.0.1:1000,127.0.0.1:1001,127.0.0.1:1002
副本集部署命令
服务器1
启动命令
1 | mongod --dbpath=F:\db\MongoDB-Shared\B\mongodb-A-replset --port 4000 --replSet MonitorA/127.0.0.1:3000 |
新增副本集
1 | db.runCommand({"replSetInitiate":{"_id":"MonitorA","members": |
服务器2
启动命令
1 | mongod --dbpath=F:\db\MongoDB-Shared\C\mongodb-B-replset --port 4001 --replSet MonitorB/127.0.0.1:3001 |
新增副本集
1 | db.runCommand({"replSetInitiate":{"_id":"MonitorB","members": |
仲载部署命令
服务器1
启动命令
1 | mongod --dbpath=F:\db\MongoDB-Shared\C\mongodb-A-arb --port 5000 --replSet MonitorA/127.0.0.1:3000 |
rs.addArb(“127.0.0.1:5000”)1
2### 服务器2
#### 启动命令
mongod –dbpath=F:\db\MongoDB-Shared\A\mongodb-B-arb –port 5001 –replSet MonitorB/127.0.0.1:30011
#### 新增仲载服务器
rs.addArb(“127.0.0.1:5001”)1
2
3## 分片部署命令
### 服务器1
#### 启动命令
mongod –dbpath=F:\db\MongoDB-Shared\A\mongodb-A-shared –port 3000 –replSet MonitorA/127.0.0.1:40001
#### 新增分片
db.runCommand( { addshard : “MonitorA/127.0.0.1:3000,127.0.0.1:4000,127.0.0.1:5000”});1
2### 服务器2
#### 启动命令
mongod –dbpath=F:\db\MongoDB-Shared\B\mongodb-B-shared –port 3001 –replSet MonitorB/127.0.0.1:40011
#### 新增分片
db.runCommand( { addshard : “MonitorB/127.0.0.1:3001,127.0.0.1:4001,127.0.0.1:5001”});1
2如里shard是单台服务器,用`db.runCommand( { addshard : "[: ]" } )`,这样的命令加入,如果shard是副本集,用`db.runCommand( { addshard : "replicaSetName/[:port][,serverhostname2[:port],…]" })`,这样的格式表示 。
##### 单个分片
db.runCommand({“addshard”:”127.0.0.1:3000”,allowLocal:true})1
2```
db.runCommand({"addshard":"127.0.0.1:3001",allowLocal:true})
副本集分片
1 | db.runCommand( { addshard : "MonitorA/127.0.0.1:3000,127.0.0.1:4000,127.0.0.1:5000"}); |
1 | db.runCommand( { addshard : "MonitorB/127.0.0.1:3001,127.0.0.1:4001,127.0.0.1:5001"}); |
设置片键
1 | db.runCommand( { shardcollection : "AirMonitor.datagram",key : {rssi: 1}}) |
设置索引
这里设置组合索引1
2
3db.datagram.ensureIndex( {"deviceMac" : 1 } );
db.datagram.ensureIndex( { "rssi" : 1, "deviceMac" : 1 } );
db.datagram.ensureIndex( { "deviceMac" : 1, "terminalMac" : 1, "date" : 1 } );
基本常用命令
查询分片情况
1 | db.printShardingStatus() |
查询集合状态
1 | db.datagram.stats() |
指定AirMonitor分片生效
1 | db.runCommand( { enablesharding :"AirMonitor"}); |
指定数据库里需要分片的集合和片键
1 | db.runCommand( { shardcollection : "AirMonitor.datagram",key : {id: 1} } ) |
组合片键
1 | db.runCommand( { shardcollection : "AirMonitor.datagram",key : {rssi: 1,deviceMac:1} } ) |
删除分片
1 | db.runCommand( { removeshard: "MonitorA" } ) |
测试
插入数据10w条:for (var i = 1; i <= 100000; i++){db.datagram.save({"rssi" : i,"deviceMac":"yujie"});}
数据分片情况
- MonitorA:rssi的0->5900都在A分片。
- MonitorB :5900之后都在B分片。
1
2
3
4
5
6
7
8AirMonitor.datagram
shard key: { "rssi" : 1, "deviceMac" : 1 }
chunks:
MonitorB 2
MonitorA 1
{ "rssi" : { "$minKey" : 1 }, "deviceMac" : { "$minKey": 1 } } -->> { "rssi" : 1, "deviceMac" : "yujie" } on : MonitorB Timestamp(2, 0)
{ "rssi" : 1, "deviceMac" : "yujie" } -->> { "rssi" : 5900, "deviceMac" : "yujie" } on : MonitorA Timestamp(3, 1)
{ "rssi" : 5900, "deviceMac" : "yujie" } -->> { "rssi" :{ "$maxKey" : 1 }, "deviceMac" : { "$maxKey" : 1 } } on : MonitorB Timestamp(3,0)
查询
在java里查询全部的时候,会把A,B分片同时查询出来,放在集合里,并且不会在排序。1
2
3
4
5
6
7
812:22:32.088 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 5900-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 1-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 5901-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 2-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 5902-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 3-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 5903-yujie
12:22:32.089 [http-bio-55555-exec-3] INFO c.m.a.c.Test2Controller - 4-yujie
保存
分片里保存数据方法较慢 ;1w条数据保存到分片B,需要10s。
中断机制(选举)
没有如何问题。
MapReduce
在分片副本集里不能使用MapReduce。
存在问题
- 保存方法太慢。
- 从多个片查询数据时,不会重新排序。
- 分片副本集里不能使用MapReduce。
以上存在的问题,可能是jar版本较低,而造成的影响。期待后续更新jar,在进行测试。基本命令
连接MongoDB
1
mongo 127.0.0.1/Monitor -u -p
设置副本MongoDB,可以查询数据
1 | db.getMongo().setSlaveOk() |
查看同步状态
1 | db.printSlaveReplicationInfo(); |
查看集群节点的状态
1 | rs.status(); |
新增副本
1 | rs.add("ip+端口号") |
注意:这个命令只能用在主库中
删除副本
1 | rs.remove("IP+端口") |