MongoDB

Mongodb

一、介绍

​ MongoDB是一个基于分布式文件存储的非关系型数据库,由C++语言编写,存储bson格式数据,方便WEB应用扩展开发。

​ bson是一种类似于json的二进制形式的存储格式,简称 binary json,支持内嵌的文档对象和数组对象,但是bson相对于json增加了Date和BinData类型。

官网:https://www.mongodb.com/

核心概念:

  • 库(database):用于隔离不同应用数据,默认数据库为test
  • 集合(collection):存在于库中,没有固定的结构,可以插入不同格式类型的数据
  • 文档(document):集合的一条条记录,是一组K-V键值对,每一条文档不需要设置相同的字段并且相同字段数据类型也可以不一样
RDBMSMongoDB
数据库
集合
文档
字段

二、docker安装

docker pull mongo:5.0.5 //拉取mongodb镜像
docker run -d -p 27017:27017 --name mongo --privileged=true 
-v /root/mongodb/data:/data/db //挂载数据卷
-e MONGO_INITDB_ROOT_USERNAME=用户名
-e MONGO_INITDB_ROOT_PASSWORD=密码  
mongo:5.0.5 //运行镜像

docker exec -it mongodb bash //进入容器
mongo //进入客户端

或者
docker exec -it mongodb mongo

use admin 
db.auth('用户名', '密码') //登录认证

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

root用户需要切换到admin库进行认证,认证后才能进行操作:

在这里插入图片描述

在这里插入图片描述

安装可视化工具Navicat for MongoDB,测试加密:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

显示隐藏的库:

在这里插入图片描述

在这里插入图片描述

三、基本命令

参考文档:https://www.mongodb.com/docs/manual/

1.库

查询所有库:

show databases
#或者
show dbs

在这里插入图片描述

  • admin:将一个用户添加到这个数据库,将获得所有数据库的权限
  • local:存储限于单台服务器的数据,这个数据库数据不会被复制
  • config:用于保存mongo分片相关信息

显示当前库:

db 

在这里插入图片描述

创建库:

use 库名

在这里插入图片描述

注意:当数据库中没有数据时默认不显示

插入一条文档后显示

在这里插入图片描述

删除库:

db.dropDatabase()

在这里插入图片描述

2.集合

查看库中所有集合:

show collections;
#或者
show tables;

创建集合:

db.createCollection('集合名称', [options])

options:

字段解释
capped如果为true,创建固定大小集合,必须指定size值
(当集合达到最大值时会覆盖最早的文档)
size指定集合存储的最大值字节数
max指定集合能够存放文档的最大数量

在这里插入图片描述

在这里插入图片描述

删除集合:

db.集合名称.drop()

在这里插入图片描述

3.文档

插入文档:

db.集合名称.insert({})

db.集合名称.insert([{},{}]) #插入多条文档
或者
db.集合名称.insertMany([{},{}]) 

# js脚本方式
for(let i = 0; i < 10; i++)
{
	db.集合名称.insert({});
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:每一个文档都有一个_id作为唯一标识,如果没有指定的话会自动生成

在这里插入图片描述

在这里插入图片描述

删除文档:

db.集合名称.remove({},[option])

db.集合名称.remove({}) #填空 删除全部文档

option
justOne: 如果为true或1,则只删除一个文档,默认为false删除所有匹配的文档

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:如果通过_id匹配删除文档,对于自动生成的id需要加上ObjectId()函数才能删除

更新文档:

db.集合名称.update({},{},[option])

option
upsert: 默认为false 如果设置为true表示不存在更新的文档时插入新的文档
multi: 默认为false 只更新一条文档 如果设置为true表示更新所有匹配的文档

在这里插入图片描述

注意:更新操作相当于先删除匹配的文档再插入新的文档,如果要保存原来的字段需要加上$set:

在这里插入图片描述

更新所有匹配的文档:

在这里插入图片描述

不存在更新的文档时插入新的文档:
在这里插入图片描述

4.查询

文档查询:

db.集合名称.find({}, {projection})

projection: 指定返回的字段 1 返回 0 不返回

db.集合名称.find({}, {projection}).pretty() #以格式化的方式显示所有文档
运算符格式
等于{key:value}
小于{key:{$lt:value}}
小于等于{key:{$lte:value}}
大于{key:{$gt:value}}
大于等于{key:{$gte:value}}
不等于{key:{$ne:value}}

查询年龄等于19:

在这里插入图片描述

查询年龄小于或大于19:

在这里插入图片描述

AND与连接:

db.集合名称.find({ key:value, key:value})

在这里插入图片描述

OR或连接:

db.集合名称.find(
    {
             $or:[
                 { key:value}, { key:value}
             ]
	}
)

在这里插入图片描述

模糊查询(正则表达式):

db.集合名称.find({key:/value/})

在这里插入图片描述

按数组长度查询:

db.集合名称.find({key:{$size:value}})

在这里插入图片描述

排序:

db.集合名称.find().sort({key:1/-1}) # 1 升序 -1 降序

在这里插入图片描述

分页:

db.集合名称.find().skip(start).limit(rows) #start 起始文档 rows 查询几条 类似 limit start rows

在这里插入图片描述

在这里插入图片描述

注意:find()不是查询全部文档,默认只显示20条,输入it查看更多

查询文档总条数:

db.集合名称.find().count()

在这里插入图片描述

去重:

db.集合名称.distinct('字段')

在这里插入图片描述

设置projection不显示age字段(1 返回 0 不返回):

在这里插入图片描述

按字段类型查询:

db.集合名称.find({key:{$type:value}})

在这里插入图片描述

在这里插入图片描述

聚合查询:

db.集合名称.aggregate(
    [
        {
        	$group:{_id:"$分组字段名称", '聚合名称':{$聚合操作}}
        }
    ]
)
聚合操作描述
$sum计算总和
$avg计算平均值
$min计算最小值
$max计算最大值
$push将值加入一个数组(可重复)
$addToSet将值加入一个数组(不可重复)
$first获取第一个文档
$last获取最后一个文档

原始数据:
在这里插入图片描述

在这里插入图片描述

按照性别分组查询最大年龄、最小年龄和平均年龄:

在这里插入图片描述

在这里插入图片描述

按照性别分类将爱好加入一个数组返回、获取第一个和最后一个爱好

在这里插入图片描述

在这里插入图片描述

5.索引

索引用来提高查询的效率,和关系型数据库类似,遵循最左前缀法则

在这里插入图片描述

创建索引:

db.集合名称.createIndex(keys, option) #keys 指定建立索引的字段 1 字段升序 -1 字段降序


option
background: 由于建立索引会阻塞其他数据库操作,background指定以后台方式创建索,默认为false
unique: 默认为false,为true时建立唯一索引
name: 指定索引名称,如果没有指定会自动生成
expireAfterSeconds: 对于索引的数据设置过期时间,过期后数据自动删除
weights: 指定索引的权值,在1~99999之间,表示索引之间的优先

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

查看索引的大小:

db.集合名称.totalIndexSize()

在这里插入图片描述

查看索引:

db.集合名称.getIndexes()

删除索引:

db.集合名称.dropIndexes("索引名称") #删除指定索引
db.集合名称.dropIndexes() #删除所有索引

在这里插入图片描述

在这里插入图片描述

四、SpringBoot整合

导入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

application.yaml:

spring:
  data:
    mongodb:
#      uri: mongodb://ip地址:27017/库名 #未设置账号密码时
      host: ip地址
      port: 27017
      database: 库名
      username: 用户名
      password: 密码

在这里插入图片描述

注意:uri和host、port、username、password、replicaSetName配置冲突,只能使用其中的一种

集合操作:

@SpringBootTest
class MongoDbApplicationTests {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    void createCollection() {
        boolean exists = mongoTemplate.collectionExists("b");
        if(!exists) {
            mongoTemplate.createCollection("b");
        }
    }

    @Test
    void dropCollection() {
        mongoTemplate.dropCollection("b");
    }
}

文档操作:

@Document("users") //User类的对象相当于一条记录
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    @Id //映射文档中的_id
    private Integer id;

    @Field //映射文档中的字段
    private String name;

    @Field
    private Integer age;
}

相关注解:

注解解释
@Document标注在类上,表示该类的对象相当于一条文档,value属性指明集合的名称
@Id标注在成员变量上,表示文档的_id值
@Field标注在成员变量上,表示文档的字段名称
@Transient标注在成员变量上,不参与文档的序列化
@SpringBootTest
class MongoDbApplicationTests {

    @Autowired
    private MongoTemplate mongoTemplate;
    
    @Test
    void insertDocument() {
        User user1 = new User(1, "张三", 18);
        User user2 = new User(1, "李四", 19);
        //mongoTemplate.insert(user1); //当_id存在时报错 DuplicateKeyException
        //mongoTemplate.save(user2); //当_id存在时则会更新数据

        ArrayList<User> users = new ArrayList<>();
        users.add(new User(2, "阿四", 25));
        users.add(new User(3, "小陈", 35));

        mongoTemplate.insert(users, User.class); //insert可以插入多条文档
        //mongoTemplate.insert(users, "users");
    }

    @Test
    void queryDocument() {
        //根据id查询
        System.out.println("_id=1的文档:" + mongoTemplate.findById(1, User.class));
        System.out.println("-----------------------------------");

        //查询所有
        System.out.println("所有文档:" + mongoTemplate.findAll(User.class));
        System.out.println("所有文档:" + mongoTemplate.find(new Query(), User.class)); //query 查询对象为null
        System.out.println("-----------------------------------");

        //条件查询
        System.out.println("名字是张三的文档:" + mongoTemplate.find(Query.query(Criteria.where("name").is("张三")), User.class));
        System.out.println("年龄小于30的文档:" + mongoTemplate.find(Query.query(Criteria.where("age").lt(30)), User.class));
        System.out.println("-----------------------------------");

        //and or
        System.out.println("年龄为30并且id为3的文档:" + mongoTemplate.find(Query.query(Criteria.where("age").gt(30).and("id").is(3)), User.class));

        Criteria criteria = new Criteria();
        criteria.orOperator(Criteria.where("age").gt(30), Criteria.where("age").lt(20));
        System.out.println("年龄大于30或小于20的文档:" + mongoTemplate.find(Query.query(criteria), User.class));
        System.out.println("-----------------------------------");

        //排序
        System.out.println("按年龄降序:" + mongoTemplate.find(new Query().with(Sort.by(Sort.Order.desc("age"))), User.class));
        System.out.println("-----------------------------------");

        //分页
        System.out.println("分页:" + mongoTemplate.find(new Query().skip(0).limit(2), User.class));
        System.out.println("-----------------------------------");

        //总条数
        System.out.println("文档总条数:" + mongoTemplate.count(new Query(), User.class));
        System.out.println("-----------------------------------");

        System.out.println("对于年龄去重:" + mongoTemplate.findDistinct(new Query(), "age", User.class, Integer.class));
    }

    @Test
    void updateDocument() {
        //更新符合条件的第一条文档
        mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(1)), new Update().set("age", 20), User.class);

        //更新符合条件的所有文档
        mongoTemplate.updateMulti(new Query(), new Update().set("age", 18),User.class);

        //不符合条件插入新的文档
        mongoTemplate.upsert(Query.query(Criteria.where("id").is(4)), new Update().setOnInsert("name", "小四").setOnInsert("age", 1), User.class);

    }

    @Test
    void deleteDocument() {
        //删除所有文档
        mongoTemplate.remove(new Query(), User.class);

        //删除年龄为18的所有文档
        mongoTemplate.remove(Query.query(Criteria.where("age").is(18)), User.class);
    }
}

五、集群

1.副本集群

在这里插入图片描述

MongoDB副本集是可以自动故障恢复的主从集群,由一个主节点和多个从节点组成,当主节点故障时,会从从节点中选举出一个新的主节点,保证系统的高可用

选举示意图:

在这里插入图片描述

搭建一主两从副本集群:

节点端口
主节点27017
从节点127018
从节点127019

创建对应的目录存放数据、配置文件和日志信息

tree -d //创建对应的目录存放数据、配置文件和日志信息

在这里插入图片描述

master主节点mongo.conf:

storage:
  dbPath: /data/db # 存储数据目录
  journal:
    enabled: true
    
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log # 存储日志目录

net:
  port: 27017 # 端口号
  bindIp: 0.0.0.0 # 允许外部访问

processManagement:
  timeZoneInfo: /usr/share/zoneinfo

security:
  keyFile: /opt/keyfile # 内部认证文件路径
  authorization: enabled # 开启认证

replication:
  replSetName: "rs0" # 副本集名称

slave从节点配置文件类似,仅修改端口号为27018、27019即可

生成keyfile文件用于节点之间的内部认证:

openssl rand -base64 756 > keyfile
chmod 400 keyfile //提供读取权限
chown 999 keyfile //!设置文件所有者(不然后面会报错)

运行容器:

//运行主节点
docker run -d -p 27017:27017 --name mongoMaster --privileged=true 
-v /root/mongoReplication/master/data:/data/db //挂载数据目录
-v /root/mongoReplication/keyfile:/opt/keyfile //挂载keyfile文件
-v /root/mongoReplication/master/configdb/:/data/configdb  //挂载配置目录
-v /root/mongoReplication/master/logs/:/var/log/mongodb/ //挂载日志目录 
mongo:5.0.5 mongod --config /data/configdb/mongod.conf //通过配置文件运行

//运行从节点
docker run -d -p 27018:27018 --name mongoSlave1 --privileged=true 
-v /root/mongoReplication/slave1/data:/data/db 
-v /root/mongoReplication/keyfile:/opt/keyfile 
-v /root/mongoReplication/slave1/configdb/:/data/configdb 
-v /root/mongoReplication/slave1/logs/:/var/log/mongodb/ 
mongo:5.0.5 mongod --config /data/configdb/mongod.conf

docker run -d -p 27019:27019 --name mongoSlave2 --privileged=true 
-v /root/mongoReplication/slave2/data:/data/db 
-v /root/mongoReplication/keyfile:/opt/keyfile 
-v /root/mongoReplication/slave2/configdb/:/data/configdb 
-v /root/mongoReplication/slave2/logs/:/var/log/mongodb/ 
mongo:5.0.5 mongod --config /data/configdb/mongod.conf

在这里插入图片描述

进入主节点容器内部:

docker exec -it mongoMaster mongosh

var config = {
	_id:"rs0",
	members:[
        {_id:0, host:"外网IP:27017"},
        {_id:1, host:"外网IP:27018"},
        {_id:2, host:"外网IP:27019"}
    ]
}

rs.initiate(config) //启动副本集

//创建用户用于认证
db.createUser(
    {
    	user:"用户名", 
    	pwd:"密码", 
    	roles:[
            {role: "root", db:"admin" }
        ]
    }
)

//从节点暂时可读
rs.secondaryOk() 
db.getMongo().setReadPref("primaryPreferred")

rs.conf() //显示副本集配置

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试:

主节点执行写操作,从节点同步

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

主节点可读写,从节点只读

在这里插入图片描述

当主节点宕机后,从节点会选举出一个新的主节点

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

当主节点恢复后会变成从节点

在这里插入图片描述
在这里插入图片描述

副本集扩缩容

rs.add({host:"IP:端口号"}) # 添加新的节点到副本集

rs.remove("IP:端口号") # 从副本集中删除节点(一次只能删除一个节点)
rs.reconfig(config) # 重新加载配置(一次可删除多个节点)

创建一个新的节点:

docker run -d -p 27020:27020 --name mongoSlave3 --privileged=true 
-v /root/mongoReplication/newNode/data:/data/db 
-v /root/mongoReplication/keyfile:/opt/keyfile 
-v /root/mongoReplication/newNode/configdb/:/data/configdb
-v /root/mongoReplication/newNode/logs/:/var/log/mongodb/ 
mongo:5.0.5 mongod --config /data/configdb/mongod.conf

在这里插入图片描述

添加到副本集:

在这里插入图片描述

在这里插入图片描述

删除节点:
在这里插入图片描述

在这里插入图片描述

使用splice删除节点,重新加载配置:

在这里插入图片描述

在这里插入图片描述

SpringBoot连接副本集日志:

spring:
  data:
    mongodb:
      host: 外网IP # 主节点
      port: 端口   # 主节点
      database: test # 操作数据库test
      authentication-database: admin # 认证数据库admin
      replica-set-name: rs0 # 副本集名称
      username: 用户名
      password: 密码

在这里插入图片描述

2.分片集群

在这里插入图片描述

当客户端访问Router执行读写操作时,Router会去访问Config Servers(配置服务器)获取分片集群的信息,进而执行相应的操作

  • Router:充当查询路由器,提供客户端和分片集群之间的服务,对外透明像单节点一样
  • Shard:每个分片都包含分片数据的子集,可以部署为副本集,保证高可用
  • Config Servers:配置服务器存储集群的元数据和配置设置,必须部署为副本集

结构图:

在这里插入图片描述

节点端口
路由节点27017
分片1主节点27018
分片1从节点27019
分片1从节点27020
分片2主节点27021
分片2从节点27022
分片2从节点27023
分片3主节点27024
分片3从节点27025
分片3从节点27026
配置主节点27027
配置从节点27028
配置从节点27029

分片服务器配置文件(其他分片类似):

storage:
  dbPath: /data/db
  journal:
    enabled: true

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

net:
  port: 27018
  bindIp: 0.0.0.0

processManagement:
  timeZoneInfo: /usr/share/zoneinfo

security:
  keyFile: /opt/keyfile
  authorization: enabled

replication:
  replSetName: "s0" # 分片1
sharding:
  clusterRole: shardsvr # 作为分片
# 分片服务器 
var config = {
	_id:"s0", # 分片1
	members:[
        {_id:0, host:"外网IP:27018"},
        {_id:1, host:"外网IP:27019"},
        {_id:2, host:"外网IP:27020"}
    ]
}

rs.initiate(config) # 启动副本集

var config = {
	_id:"s1", # 分片2
	members:[
        {_id:0, host:"外网IP:27021"},
        {_id:1, host:"外网IP:27022"},
        {_id:2, host:"外网IP:27023"}
    ]
}

rs.initiate(config) # 启动副本集

var config = {
	_id:"s2", # 分片3
	members:[
        {_id:0, host:"外网IP:27024"},
        {_id:1, host:"外网IP:27025"},
        {_id:2, host:"外网IP:27026"}
    ]
}

rs.initiate(config) # 启动副本集

# 配置服务器 
var config = {
	_id:"c0",
	configsvr: true, # 指定副本集用于配置服务器
	members:[
        {_id:0, host:"外网IP:27027"},
        {_id:1, host:"外网IP:27028"},
        {_id:2, host:"外网IP:27029"}
    ]
}

rs.initiate(config) # 启动副本集

db.createUser(
    {
    	user:"用户名", 
    	pwd:"密码", 
    	roles:[
            {role: "root", db:"admin" }
        ]
    }
)

分片服务器副本集:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

配置服务器配置文件:

storage:
  dbPath: /data/db
  journal:
    enabled: true

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

net:
  port: 27027
  bindIp: 0.0.0.0

processManagement:
  timeZoneInfo: /usr/share/zoneinfo

security:
  keyFile: /opt/keyfile
  authorization: enabled
  
replication:
  replSetName: "c0" # 配置服务器副本集名称
sharding:
  clusterRole: configsvr # 作为配置服务器

在这里插入图片描述

在这里插入图片描述

docker run -d -p 27017:27017 --name router --privileged=true
-v /root/cluster/keyfile:/opt/keyfile
-v /root/cluster/router/logs:/var/log/mongodb/
-e MONGO_INITDB_ROOT_USERNAME=用户名
-e MONGO_INITDB_ROOT_PASSWORD=密码  
mongo:5.0.5 mongos --keyFile /opt/keyfile # 这里使用mongos 
--bind_ip=0.0.0.0,:: # 允许外部访问
--configdb 副本集名称/各个节点的IP:端口,... # 配置服务器副本集

# 添加三个分片
sh.addShard("s0/外网IP:27018,外网IP:27019,外网IP:27020")
sh.addShard("s1/外网IP:27021,外网IP:27022,外网IP:27023")
sh.addShard("s2/外网IP:27024,外网IP:27025,外网IP:27026")

sh.status() # 查看分片集群信息

sh.enableSharding("库名") # 数据库开启分片
sh.shardCollection("库名.集合名", { _id: "hashed"}) # 指定分片键按_id哈希散列对集合进行分片
sh.shardCollection("库名.集合名", { _id: 1}) # 指定分片键按_id范围对集合进行分片

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

插入数据测试分片:

# 插入1000数据 对于不同的分片键查看分片情况
for(let i = 0; i < 1000; i++){
	db.users.insert({_id:i, name:"王" + i})
}

分片键_id单调递增,使用范围分片将导致大量数据存入同一个分片,分布不均匀

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

使用哈希分片,将数据尽量平均地分布在每一个分片

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值