简介

一个以JSON为数据模型的文档数据库

横向扩展可以支撑很大数据量和并发

MongoDB和关系型数据库的对比

image-20220416135334700

配置

conf/mongodb.conf

1
2
3
dbpath=E:\SDK\mongodb-5.0.7\data\db
logpath=E:\SDK\mongodb-5.0.7\data\log\mongo.log
auth=true

创建用户

使用前创建一个root用户,以免报not authorized on admin to execute command

1
2
3
4
5
6
7
db.createUser(
{
user:"root",
pwd:"123456",
roles:[{role:"root",db:"admin"}]
}
)

登录:

1
db.auth('root','123456')

MongoDB Compass

image-20220416193339946

数据库操作

创建数据库

1
use DATABASE_NAME

使用db查看当前数据库

如果数据库不存在,则创建数据库,否则切换到指定数据库

image-20220416173534151

删除数据库

1
db.dropDatabase()

删除当前数据库

image-20220416173841366

集合操作

创建集合

1
db.createCollection(name, options)

option:

  • capped (true/false)
    • 创建固定集合,当为true时必须指定size参数
  • size
    • 为固定集合指定一个最大值(字节数)
  • max
    • 指定固定集合中包含文档的最大数量

image-20220416174711462

使用show collections查看集合

删除集合

1
db.collection.drop()

image-20220416175228793

文档CRUD

插入

MongoDB 使用 insert() 向集合中插入文档

  • insert(): 若插入的数据主键已经存在,则提示主键重复,不保存当前数据。
1
db.COLLECTION_NAME.insert(document) 

db.collection.insertOne() 用于向集合插入一个新文档

1
2
3
db.inventory.insertOne(
{ item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)

db.collection.insertMany() 用于向集合插入一个多个文档

1
2
3
4
5
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
{ item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
{ item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])
  • 直接插入

image-20220416183540379

如果集合不在数据库中,MongoDB会自动创建该集合并插入文档

  • 间接插入

    image-20220416184011044

查询

1
2
3
db.collection.find(query, projection)
//query指定条件
//projection指定返回的键
1
2
db.collection.find().pretty()
//以格式化的方式来读取数据

image-20220416184346779

query条件

等于 {<key>:<value>} db.col.find({"title":"issak"}).pretty()
小于 {<key>:{$lt:<value>}} db.col.find({"likes":{$lt:50}}).pretty()
小于或等于 {<key>:{$lte:<value>}} db.col.find({"likes":{$lte:50}}).pretty()
大于 {<key>:{$gt:<value>}} db.col.find({"likes":{$gt:50}}).pretty()
大于或等于 {<key>:{$gte:<value>}} db.col.find({"likes":{$gte:50}}).pretty()
不等于 {<key>:{$ne:<value>}} db.col.find({"likes":{$ne:50}}).pretty()
AND {<key1>:<value1><key2>:<value2>} db.col.find({key1:value1, key2:value2}).pretty()
OR {$or: [ {key1: value1}, {key2:value2} ] } db.col.find( { $or: [ {key1: value1}, {key2:value2}]} ).pretty()

or

image-20220417095915200

Projection

1
2
db.collection.find(query, {title: 1, by: 1}) // inclusion模式 指定返回的键,不返回其他键
db.collection.find(query, {title: 0, by: 0}) // exclusion模式 指定不返回的键,返回其他键

by 默认为1

_id键默认返回,需要主动指定_id:0才会隐藏

只能全1或全0,除了在inclusion模式时可以指定_id为0

1
db.collection.find(query, {_id:0, title: 1, by: 1}) // 正确

image-20220417100716145

更新

MongoDB 使用 update()save() 方法来更新集合中的文档

  • update

update只用来更新已存在的文档

1
2
3
4
5
6
7
8
9
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>, //不存在就插入,默认false
multi: <boolean>, //更新多条记录,默认false
writeConcern: <document> //抛出异常级别
}
)

image-20220416185454776

默认只会修改第一条发现的文档,若要修改多条文档,需要设置参数multi:true

1
db.col.update({"title": "issak"}, {$set:{"title": "kenny"}}, {multi:true})
  • save

save方法若_id主键存在就更新,不存在就插入

1
2
3
4
5
6
db.collection.save(
<document>,
{
writeConcern: <document>
}
)

image-20220416190640763

删除

1
2
3
4
db.collection.remove(
<query>,
<justOne> //若设置为true或者1,则只删除一个文档;否走删除所有匹配的文档
)

image-20220416192057812

$type操作符

类型 数字 备注
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined 6 已废弃。
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255 Query with -1.
Max key 127

image-20220417094440396

Limit与Skip

limit指定读取的记录条数

1
>db.COLLECTION_NAME.find().limit(NUMBER)

image-20220417094942991

skip跳过指定数量的数据

1
>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

image-20220417095054370

Sort

sort用来排序

1
>db.COLLECTION_NAME.find().sort({KEY:1})

1为升序,-1为降序

image-20220417100827561

skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit()。

索引

索引能够大大提升查询的效率

创建索引

1
2
>db.collection.createIndex(keys:options)
//Key 值为索引字段,option 1是升序,-1是降序

image-20220417101737008

options:db.collection.createIndex()

查看索引

1
db.col.getIndexes()

image-20220417102142472

查看集合索引的大小

1
db.col.totalIndexSize()

image-20220417102417572

删除索引

删除所有索引:

1
db.col.dropIndexes()

删除指定索引:

1
db.col.dropIndex("索引名称")

聚合查询(aggregate)

功能

  • 将多个文档中的值组合在一起。
  • 对分组数据执行操作以返回单个结果。
  • 分析数据随时间的变化。

管道聚合的大致流程:

image-20220417104402476

image-20220417111753564

image-20220417111847236测试数据:

1
2
3
4
5
6
7
8
9
db.articles.insert([    
{ "_id" : 10, "author" : "dave", "score" : 80, "views" :100 },
{ "_id" : 11, "author" : "dave", "score" : 85, "views" : 521 },
{ "_id" : 12, "author" : "ahn", "score" : 60, "views" : 1000 },
{ "_id" : 13, "author" : "li", "score" : 55, "views" : 5000 },
{ "_id" : 14, "author" : "annT", "score" : 60, "views" : 50 },
{ "_id" : 15, "author" : "1i", "score": 94, "views": 999 },
{ "_id" : 16, "author" : "ty", "score" : 95, "views": 1000 }
]);

image-20220417105646446

image-20220417105934396

image-20220417110027392

image-20220417114546084

表达式 描述 实例
$sum 计算总和。 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$sum : “$likes”}}}])
$avg 计算平均值 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$avg : “$likes”}}}])
$min 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$min : “$likes”}}}])
$max 获取集合中所有文档对应值得最大值。 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$max : “$likes”}}}])
$push 在结果文档中插入值到一个数组中。 db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$push: “$url”}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$addToSet : “$url”}}}])
$first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{$group : {_id : “$by_user”, first_url : {$first : “$url”}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{$group : {_id : “$by_user”, last_url : {$last : “$url”}}}])

复制

复制就是将数据同步在多个服务器上

mongodb的复制通常是一主多从的模式

image-20220417111054375

客户端从主节点读取数据,在客户端写入数据到主节点时,主节点会与从节点进行数据交互保障数据的一致

副本集特征:

  • N 个节点的集群
  • 任何节点可作为主节点
  • 所有写入操作都在主节点上
  • 自动故障转移
  • 自动恢复

mongodb通过选举完成故障恢复

  • 具有投票权的结点之间会两两互相发送心跳
  • 5次心跳未收到时判断为失联
  • 若主节点失联,从节点会发起选举选出新的主节点
  • 选举基于RAFT,选举成功的必要条件是大多数投票结点存活

通过Go语言连接Mongodb

创建mongodb容器

1
$ docker pull mongo:latest

运行容器

1
$ docker run --name mongo -p 27017:27017 -v /home/mongo/mymongo:/data/db -d mongo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
"context"
"fmt"
"log"

"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type Student struct {
Name string
Age int
}

func main() {
// 设置客户端连接配置
clientOptions := options.Client().ApplyURI("mongodb://172.17.0.2:27017")

// 连接到MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}

// 检查连接
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB!")

collection := client.Database("kenny").Collection("issak")
insert(collection)
}

func insert(collection *mongo.Collection) {
s1 := Student{Name: "chiristio", Age: 37}
result, err := collection.InsertOne(context.TODO(), s1)
if err != nil {
log.Fatal(err)
}
fmt.Println("Inserted a single document: ", result.InsertedID)
}