文章目录
- MongoDB的基础
- 特点与应用场景
- 核心概念
- 语法及命令行
- MongoDB查询基础
- $Type
- 索引基础
- 聚合函数
- 整合SpringBoot与MongoDB
- 集群(副本集)
- 分片集群
MongoDB的基础
高性能移动数据库。MongoDB 具有映射到开发人员思维和编码方式的文档数据模型,以及功能强大、统一的查询 API,可支持更快、更灵活的应用程序开发。是一个非关系型文档数据库。
特点与应用场景
- 特点:
- 面向集合存储,易存储对象类型的数据。
- 支持查询,及动态查询。
- 支持多种语言,如Java;
- 文件存储格式为BSON(一种json的扩展)
- 支持复制和故障恢复和分片。
- 支持事务(强事务)
- 应用场景:
- 游戏应用
- 物流应用
- 社交应用
- 视频直播
- 大数据应用
核心概念
- 库:
库就类似传统的关系型数据库中的库的概念,通过库来隔离不同应用的数据。可以建立多个数据库,每个库可以有自己的集合和权限,不同的数据库也放置在不同的文件中。默认的数据库为“test”,数据库存储在启动指定的data目录中。
- 集合:
集合就是MongoDB文档组,类似于RDBMS(关系型数据库管理系统)中表的概念。
集合存于库中,毕竟集合不同于表,数据类型比较松散。
- 文档:
是库中一条条的记录内容,是一组键值对(即BSON)。MongoDB的文档不需要设置相同字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是MongoDB的突出特点。
语法及命令行
- 库的操作:
1.创建或使用库:>use 库名;
2.查看当前库: >db;
3.查看库(有数据的库);>show dbs;
4.删除当前库:db.dropDatabase();
- 集合操作:
1.查看当前库的集合:> show tables; 或>show collections;
2.创建集合:>db.createCollection(‘集合名称’,[options]); **options:可以为capped,size,max **
3.插入数据时自动创建不存在的集合:>db.集合名称.insert({此为json格式的BSON字符串});
4.删除集合:>db.集合名称.drop();
- 文档操作:
1.文档的插入:
- 单条文档:>db.集合名称.insert({});
- 多条文档:
1.>db.集合名称.insertMany(
[<BSON格式数据>...<>]
{
writeConcern: 1,//写入策略,默认为1,1即要求确认写操作,0是不要求。
ordered: true //是否按顺序写入,默认true,按顺序写入。
}
);
2.>db.集合名称.insert([
{"name":"111","age":11,"bir":"111111"},
{}
]);
3.脚本方式:
for(let i=0;i<=9;i++){
db.集合名称.insert({name="name"+i,age:i});
};2.查看集合所有文档:>db.集合名称.find();
3.集合内文档删除:
>db.集合名称.remove(
<query>, //可选,条件
{
justOne:<boolean>, //可选,true或1,则只删除一个文档,默认为false,则删除所有匹配条件的文档。
writeConcern:<document> //可选,抛出异常的级别。
}
)4.文档的更新:
>db.集合名称.update(
<query>,//条件,类似SQL查询的where,如果直接写条件,更新操作就会变成先删除后更新,需要使用这样的方式:{$set:{条件}}
<update>,//类似SQL的set后的
{
upsert:<boolean>,//可选,如果不存在update记录,是否插入,默认不插入。
multi:<boolean>,//可选,默认false,只找一条数据,为true则支持更新多条数据
writeConcern:<document>//异常级别
}
);5.文档的查询:(详见下章)
>db.集合名称.find(
<query>,
<projection>
);
//如果需要易读的方式读取数据,可以使用:db.集合名称.find().pretty();MongoDB查询基础
- 对比语法
操作 | 格式 |
等于 | {key:value} |
小于 | {key:{$lt:value}} |
小于等于 | {key:{$lte:value}} |
大于 | {key:{$gt:value}} |
大于等于 | {key:{$gte:value}} |
不等于 | {key:{$ne:value}} |
- AND
多条件时,直接“,”连接。
>db.集合名称.find({key1:value1,key2:value2...});- OR
MongoDB OR条件语句使用关键字$or,语法格式如下:
>db.集合名称.find(
{
$or:[
{key1:value1,key2:value2...}
]
}
).pretty();- 数组查询相当于等值查询,数组长度查询:>.find({“数组字段名”:{$size:3}});//数组长度为3的
- 模糊查询:靠正则表达式实现:> .find({name:/jack/});
- 排序:
.find().sort({name:1,age:-1});//1为升序,-1为降序 - 分页:>.find().skip(0).limit(2);
- 总条数:>.find().count();
- 去重:>.distinct(“字段”);
- 指定返回字段:>bd.name.find({条件},{name:1,age:1});//1为返回,0为不返回,两者不能同时使用
$Type
基于BSON类型未检索集合中匹配的数据类型,并返回结果。
.find({name:{KaTeX parse error: Expected 'EOF', got '}' at position 7: type:1}̲}).pretty();//查…type:1}}).pretty()//查询类型为String的数据
索引基础
“_id”为默认索引,自动创建的。
- 创建索引:
db.集合名称.createIndex('key','options');
db.集合名称.createIndex({'title':1,'yyy':-1});
说明:
语法中key值是你创建的索引字段,1为升序创建索引,-1为降序。
options可选项,可以设置索引有效时间(expireAfterSeconds),建立唯一索引(unique),给索引取名(name),设置权重(weights),设置版本号(v)等功能。- 查看所有索引:>.getIndexes();
- 查看索引大小:>.totalIndexSize();
- 删除索引(全部):>.dropIndexes();
- 删除指定索引:>.dropIndex(“索引名称”)
- 复合索引创建:在key中多个字段。和传统关系型数据库一样,聚集索引遵循前缀原则,即key{a,b,c}时,查询时用a,ab,abc索引都会生效,与顺序无关,因为会自动排序。
排序也会利用到索引,这是MongoDB的数据库索引与传统关系型数据库唯一不同的地方。
聚合函数
例子:
db.集合名称.aggregate([{$group:{_id:"$byy_user",num_tutor:{$sum:1}}}] );//其中_id和num_tutor是自己随便起的名字。分组关键字:$group, 数据需要用$
表达式 | 描述 |
$sum | 求和 |
$avg | 平均值 |
$min | 最小数 |
$max | 最大 |
$push | 将结果加入数组 ,可重复 |
$addToSet | 加入数组,不重复 |
$first | 获取第一个 |
$last | 获取最后一个 |
整合SpringBoot与MongoDB
- 导入依赖
基于spring-data框架的
<!--mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.7</version>
</dependency>- 编写配置文件(yml)
#基于无安全操作的配置
spring:
data:
mongodb:
uri: mongodb://192.168.61.132:27017/tanjiali
#基于有安全操作的
spring:
data:
mongodb:
host: 192.168.61.132
port: 27017
database: tanjiali
username: root
password: root- MongoDB集合的操作(java代码)
/**
* 此方法自动注入只发生一次,因为MongoTemplate被final修饰。
*/
private final MongoTemplate mongoTemplate ;
@Autowired
public MongodbTest(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
/**
* 创建集合——相当于创建表
*/
@Test
public void creatCollection(){
while (true){
System.out.println("输入创建的集合名称:");
Scanner input = new Scanner(System.in);
String collect = input.next();
boolean exists = mongoTemplate.collectionExists(collect);
if (exists){
System.out.println("已存在!!");
return;
}else {
mongoTemplate.createCollection(collect);
System.out.println("成功!");
}
}
}
/**
* 删除集合
*/
@Test
void drop() {
while (true){
System.out.println("输入删除的集合名称:");
Scanner input = new Scanner(System.in);
String collect = input.next();
boolean exists = mongoTemplate.collectionExists(collect);
if (exists){
mongoTemplate.dropCollection(collect);
}else {
System.out.println("不存在!!数据库未修改!!");
}
}
}- 创建实体类(POJO)映射MongoDB的文档
使用过程类似于MyBatisPlus+Lombok
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document("user")
@Data
public class User {
//标记数据库中的_id
@Id
private Integer id;
//可以指定数据库中映射的字段
@Field("user_name")
private String name;
@Field
private Double salary;
@Field
private Integer age;
//表示这个字段不被映射到数据库文档中
@Transient
private String birthDay;
}- 插入数据到MongoDB
/**
* 文档的插入
*/
@Test
void insert() {
Date date = new Date();
User user = new User(2,"mark",9293d,23,"2022-01-02");
//允许插入,但如果存在重复数据的”_id“会失败
mongoTemplate.insert(user);
//保存,不存在重复的就插入,存在则更新
mongoTemplate.save(user);
//批量操作插入
List<User> users = Arrays.asList(new User(1, "mark", 9293d, 23, DateUtil.formatDate(date), "2022-01-02"),
new User(2, "mark", 9293d, 23, DateUtil.formatDate(date), "2022-01-02")
);
mongoTemplate.insert(users,User.class);//参数1:批量文件,参数2:类型或集合名称
}1.insert在插入重复数据时会报错,而使用save时就会更新。
2.在批处理操作时,insert可以一次插入整个数据,效率更高;而save需要遍历整个数据,一次插入或更新,效率更低。
总结:批量插入用insert,单次插入或更新用save
- 文档的查询
/**
* 文档查询
*/
@Test
void query() {
//1.查询全部
List<User> all = mongoTemplate.findAll(User.class);
//查询
mongoTemplate.findById(1,User.class);
//3.等值查询——年龄为25的
mongoTemplate.find(Query.query(Criteria.where("age").is(25)), User.class);
//4.大于小于查询(lt()小于,gt()大于)
mongoTemplate.find(Query.query(Criteria.where("age").lt(25)), User.class);
//5.And查询
mongoTemplate.find(Query.query(Criteria.where("age").is(25).and("user_name").is("mark")), User.class);
//6.OR查询---后可接and条件
Criteria criteria = new Criteria();
criteria.orOperator(Criteria.where("age").is(25),Criteria.where("salary").lte(10000));
criteria.and("user_name").is("mark");
mongoTemplate.find(Query.query(criteria), User.class);
//7.OR和and一起用,此方式相当于在and查询结果中找满足or的
mongoTemplate.find(Query.query(Criteria.where("age").is(25).orOperator(Criteria.where("user_name").is("mark"))), User.class);
//8.排序
Query query = new Query();
query.with((Sort.Order.asc("age")));
query.addCriteria(Criteria.where("user_name").is("mark"));
mongoTemplate.find(query, User.class);
//9.分页
Query query2 = new Query();
query2.with((Sort.Order.asc("age")));
query2.with(Pageable.ofSize(2).withPage(2));//方法1
query2.skip(2).limit(2);//方法2
mongoTemplate.find(query2, User.class);
//10.查询条数
long count = mongoTemplate.count(new Query(), User.class);
//11.去重
mongoTemplate.findDistinct(new Query(), "user_name", User.class, String.class);
//使用JSQN字符串方式查询(只查询名字)
BasicQuery basicQuery = new BasicQuery("{'user_name':'mark'}","{user_name:1}");
List<User> list = mongoTemplate.find(basicQuery, User.class);
//结果遍历输出
list.forEach(System.out::println);
System.out.println(count);
}- 文档的更新与删除
/**
* 文档的更新
*
*/
@Test
void updataAndRemove() {
/**
* 更新
*/
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(8));
Update update = new Update();
update.set("user_name","tanjiali");
//更新符合条件的第一条数据
mongoTemplate.updateFirst(query, update,User.class);
//多条数据更新
mongoTemplate.updateMulti(query,update,User.class);
//更新,若不存在条件的字段,则插入
update.setOnInsert("_id",8);//设置如果插入时,默认字段的值
UpdateResult updateResult = mongoTemplate.upsert(query, update, User.class);
//打印更新后的返回信息
System.out.println(updateResult.getModifiedCount());//匹配数量
System.out.println(updateResult.getMatchedCount());//修改数量
System.out.println(updateResult.getUpsertedId());//插入的id
}
/**
* 删除
*/
@Test
void remove() {
/**
* 删除
*/
Query query = new Query();
//删除小于8的所有数据
query.addCriteria(Criteria.where("_id").gte(4));
DeleteResult remove = mongoTemplate.remove(query, User.class);
//查询并删除
List<User> allAndRemove = mongoTemplate.findAllAndRemove(query, User.class);
allAndRemove.forEach(System.out::println);
}集群(副本集)
通俗的讲,MongoDB的集群设置也是传统的主从复制,但是,使用过程中只对主节点进行控制,当主节点宕机后,为实现高可用,会自动根据各服务器的从节点心跳机制进行选举新的主节点。特别注意当副本集只剩一个服务节点时,服务是不可选举为主节点的。当其他服务恢复时,剩下的那个节点会变成主节点,其他的为从节点。
搭建步骤类似传统服务器集群,这里不做叙述。
模拟搭建流程:
- 虚拟机开启3个端口运行MongoDB服务器,开启时指定另外两个端口进行监听
- 开启后,打开客户端,随便连接一个服务器,初始化副本集:
>var config(){ "_id":"启动时的副本集名称,三个启动时必须一样", "member":[ {"_id":0,"host":ip地址和端口}, {"_id":0,"host":ip地址和端口}, {"_id":0,"host":ip地址和端口} ] } >rs.initiate(config);
- 服务器开始选举主节点,默认会是当前机器为主节点,其他机器为从节点
- 主节点拥有所有读写权力,而从节点没有读写权限
- 若要开启从节点的读写权限,可以进入从节点机器,执行命令:>rs.slaveOk();或rs.sacondaryok();
应用程序连接副本集的配置
spring:
data:
mongodb:
uri: mongodb://192.168.61.132:27017,192.168.61.132:27018,192.168.61.132:27019/tanjiali?replicaSet=副本集的名称分片集群
- 副本集解决问题:
自动故障转移,主从复制,集群。数据冗余备份,架构高可用。但是无法解决单节点压力问题。 - 分片集群 :解决服务器单点压力
分片集群架构的结构:路由,配置服务器,分片节点服务器
















