文章目录

  • 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=副本集的名称

分片集群

  • 副本集解决问题:
    自动故障转移,主从复制,集群。数据冗余备份,架构高可用。但是无法解决单节点压力问题。
  • 分片集群 :解决服务器单点压力
    分片集群架构的结构:路由,配置服务器,分片节点服务器