一 Mongo集群
项目中,将大部分内容存储到了MongoDB中,这部分数据无疑是海量的,那么如何保证数据的容量和安全呢?答案是:集群。
MongoDB有三种集群方式,分别是:主从集群(过时)、副本集群(推荐)、分片式集群。
其中主从集群官方已经不推荐了,一般用副本集群取代主从集群。
1 主从集群(了解)
主从架构一般用于备份或者做读写分离。由两种角色构成:
- 主(Master):可读可写,当数据有修改的时候,会将oplog同步到所有连接的salve上去。
- 从(Slave):只读不可写,自动从Master同步数据。
对于Mongodb来说,并不推荐使用Master-Slave架构,因为Master-Slave其中Master宕机后不能自动恢复,推荐使用Replica Set。
2 副本集群【演示】
介绍
为了防止单点故障就需要副本集群,包含三类角色:
-
主节点(Primary)
接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primary挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。默认读请求也是发到Primary节点处理的.若需要转发到Secondary,需要客户端修改一下连接配置。
-
副本节点(Secondary)
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
-
仲裁者(Arbiter)
不保有数据,不参与选主,只进行选主投票。使用Arbiter可以减轻数据存储的硬件需求,Arbiter跑起来几乎没什么大的硬件资源需求,但重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。
注意,一个自动failover的Replica Set节点数必须为奇数,目的是选主投票的时候要有一个大多数才能进行选主决策。
特殊说明
环境搭建
创建容器
docker run -d --name=master_mongo -p 27011:27017 mongo:4.0.3 --replSet mongo_clus
docker run -d --name=backup_mongo -p 27012:27017 mongo:4.0.3 --replSet mongo_clus
docker run -d --name=arbi_mongo -p 27013:27017 mongo:4.0.3 --replSet mongo_clus
设置副本集名称,也就是设置集群名称,必须要设置,否则没法构建集群
配置
进入master_mongo
容器中
docker exec -it master_mongo /bin/bash
登录Mongo
mongo
创建集群
cfg={
"_id":"mongo_clus",
members:[{
_id:0,
host:"192.168.136.131:27011",
priority:2
},{
_id:1,
host:"192.168.136.131:27012",
priority:1
},{
_id:2,
host:"192.168.136.131:27013",
arbiterOnly:true
}]
}
rs.initiate(cfg)
通过 rs.status() 可以查看集群的状态
客户端测试
使用客户端连接主节点
use demo
db.createCollection("user")
db.user.insert({username:'zhangsan',age:18,address:'北京顺义'})
java代码测试
在之前的工程中创建
mongo-cluster
模块(不要在探花工程中)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>3.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "account")
public class Account {
private ObjectId id; //主键id
private String username; //用户名
private Double money; //金额
}
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MongoClusterApplication {
public static void main(String[] args) {
SpringApplication.run(MongoClusterApplication.class,args);
}
}
application.yml
server:
port: 9999
spring:
data:
mongodb:
uri: mongodb://192.168.136.131:27011,192.168.136.131:27012/demo?connect=replicaSet&replicaSet=mongo_clus
- connect=replicaSet 连接方式是副本集
- replicaSet=myrs 设置副本集的名称为mongo_clus
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoTest {
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void testSave() {
Account account = new Account();
account.setUsername("jack");
account.setMoney(1000D);
mongoTemplate.save(account);
account = new Account();
account.setUsername("tom");
account.setMoney(1000D);
mongoTemplate.save(account);
}
@Test
public void testFindAll() {
List<Account> accountList = mongoTemplate.findAll(Account.class);
for (Account account : accountList) {
System.out.println(account);
}
}
}
事务支持
MongoDB 4.0以上才开始支持事务
。4.0-4.2版只支持副本集群事务,4.2以上版本开始支持所有集群事务
配置事务管理器
事务管理器:已经和Spring进行了整合
@SpringBootApplication
public class MongoClusterApplication {
public static void main(String[] args) {
SpringApplication.run(MongoClusterApplication.class,args);
}
@Bean
public MongoTransactionManager initMongoTransactionManager(MongoDbFactory factory){
return new MongoTransactionManager(factory);
}
}
转账案例
package com.itheima.service;
import com.itheima.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AccountService {
@Autowired
private MongoTemplate mongoTemplate;
@Transactional
public void transfer(String sourceName, String targetName, Double money) {
//1. 更新sourceName的账户金额
Query query = new Query(Criteria.where("username").is(sourceName));
Account source = mongoTemplate.findOne(query, Account.class);
Query query1 = new Query(Criteria.where("username").is(targetName));
Account target = mongoTemplate.findOne(query1, Account.class);
//2. 变动金额
Update update = new Update();
update.set("money", source.getMoney() - money);
Update update1 = new Update();
update1.set("money", target.getMoney() + money);
//3. 更新账户金额
mongoTemplate.updateFirst(query, update, Account.class);
int i=1/0;
mongoTemplate.updateFirst(query1, update1, Account.class);
}
}
测试
@Autowired
private AccountService accountService;
@Test
public void testTransfer() {
accountService.transfer("jack", "tom", 100D);
}
3 分片集群(了解)
介绍
当数据量比较大的时候,我们需要把数据分片运行在不同的机器中,以降低CPU、内存和IO的压力,Sharding就是这样的技术。
分片(sharding)是MongoDB用来将大型集合分割到不同服务器上所采用的方法。
如图所示:
例如,如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256 GB的数据。如果有40个分片,那么每个切分可能只有10TB的数据。
分片集群架构
-
数据分片(Shards)
保存数据,保证数据的高可用性和一致性。可以是一个单独的
mongod
实例,也可以是一个副本集。在生产环境下Shard是一个Replica Set,以防止该数据片的单点故障。所有Shard中有一个PrimaryShard,里面包含未进行划分的数据集合:
-
查询路由(Query Routers)
mongos
的实例,客户端直接连接mongos
,由mongos
把读写请求路由到指定的Shard上去。一个Sharding集群,可以有一个
mongos
,也可以有多mongos
以减轻客户端请求的压力。 -
配置服务器(Config servers)
保存集群的元数据(metadata),包含各个Shard的路由规则。