MongoDB

Mongodb

一、介绍

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

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

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

核心概念:

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

二、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('用户名', '密码') //登录认证

MongoDB_第1张图片

在这里插入图片描述

MongoDB_第2张图片

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

MongoDB_第3张图片

MongoDB_第4张图片

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

MongoDB_第5张图片

MongoDB_第6张图片

MongoDB_第7张图片

显示隐藏的库:

在这里插入图片描述

MongoDB_第8张图片

三、基本命令

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

1.库

查询所有库:

show databases
#或者
show dbs

MongoDB_第9张图片

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

显示当前库:

db 

在这里插入图片描述

创建库:

use 库名

MongoDB_第10张图片

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

插入一条文档后显示

MongoDB_第11张图片

删除库:

db.dropDatabase()

MongoDB_第12张图片

2.集合

查看库中所有集合:

show collections;
#或者
show tables;

创建集合:

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

options:

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

MongoDB_第13张图片

MongoDB_第14张图片

删除集合:

db.集合名称.drop()

MongoDB_第15张图片

3.文档

插入文档:

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

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

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

在这里插入图片描述

MongoDB_第16张图片

MongoDB_第17张图片

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

MongoDB_第18张图片

MongoDB_第19张图片

删除文档:

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

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

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

MongoDB_第20张图片

MongoDB_第21张图片

MongoDB_第22张图片

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

更新文档:

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

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

MongoDB_第23张图片

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

MongoDB_第24张图片

更新所有匹配的文档:

MongoDB_第25张图片

不存在更新的文档时插入新的文档:
MongoDB_第26张图片

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:

MongoDB_第27张图片

查询年龄小于或大于19:

MongoDB_第28张图片

AND与连接:

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

在这里插入图片描述

OR或连接:

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

MongoDB_第29张图片

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

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

MongoDB_第30张图片

按数组长度查询:

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

MongoDB_第31张图片

排序:

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

MongoDB_第32张图片

分页:

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

MongoDB_第33张图片

MongoDB_第34张图片

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

查询文档总条数:

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

在这里插入图片描述

去重:

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

在这里插入图片描述

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

MongoDB_第35张图片

按字段类型查询:

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

MongoDB_第36张图片

MongoDB_第37张图片

聚合查询:

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

原始数据:
MongoDB_第38张图片

在这里插入图片描述

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

MongoDB_第39张图片

在这里插入图片描述

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

在这里插入图片描述

MongoDB_第40张图片

5.索引

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

MongoDB_第41张图片

创建索引:

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


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

MongoDB_第42张图片

MongoDB_第43张图片

MongoDB_第44张图片

MongoDB_第45张图片

MongoDB_第46张图片

查看索引的大小:

db.集合名称.totalIndexSize()

在这里插入图片描述

查看索引:

db.集合名称.getIndexes()

删除索引:

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

MongoDB_第47张图片

MongoDB_第48张图片

四、SpringBoot整合

导入依赖:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-testartifactId>
    <scope>testscope>
dependency>
<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
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_第49张图片

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

选举示意图:

MongoDB_第50张图片

搭建一主两从副本集群:

节点 端口
主节点 27017
从节点1 27018
从节点1 27019

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

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

MongoDB_第51张图片

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() //显示副本集配置

MongoDB_第52张图片

MongoDB_第53张图片

MongoDB_第54张图片

测试:

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

MongoDB_第55张图片

在这里插入图片描述

MongoDB_第56张图片

在这里插入图片描述

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

MongoDB_第57张图片

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

在这里插入图片描述

MongoDB_第58张图片

MongoDB_第59张图片

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

在这里插入图片描述
MongoDB_第60张图片

副本集扩缩容

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

MongoDB_第61张图片

添加到副本集:

MongoDB_第62张图片

MongoDB_第63张图片

删除节点:
MongoDB_第64张图片

MongoDB_第65张图片

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

MongoDB_第66张图片

MongoDB_第67张图片

SpringBoot连接副本集日志:

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

在这里插入图片描述

2.分片集群

MongoDB_第68张图片

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

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

结构图:

MongoDB_第69张图片

节点 端口
路由节点 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" }
        ]
    }
)

分片服务器副本集:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

MongoDB_第70张图片

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

MongoDB_第71张图片

MongoDB_第72张图片
MongoDB_第73张图片

配置服务器配置文件:

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 # 作为配置服务器

MongoDB_第74张图片

MongoDB_第75张图片

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范围对集合进行分片

在这里插入图片描述

MongoDB_第76张图片

MongoDB_第77张图片
MongoDB_第78张图片

插入数据测试分片:

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

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

MongoDB_第79张图片

MongoDB_第80张图片

MongoDB_第81张图片

MongoDB_第82张图片

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

MongoDB_第83张图片

MongoDB_第84张图片

MongoDB_第85张图片

MongoDB_第86张图片

你可能感兴趣的:(NoSQL,mongodb,数据库,nosql)