首页 > 数据库 >NoSQL数据库实习头歌实验知识点整理(二)-MongoDB部分

NoSQL数据库实习头歌实验知识点整理(二)-MongoDB部分

时间:2024-11-01 23:20:05浏览次数:7  
标签:知识点 NoSQL -- MongoDB 数据库 db 查询 索引 文档

文章目录

1-1 初识MongoDB

1.1 DOS(Windows)端启动 MongoDB 服务

1.1.1 配置环境变量

在电脑->属性->环境变量->Path中添加 MongoDB 安装路径(根据自己的安装路径添加),如图所示
预览大图

1.1.2 启动服务并进行相关配置

mongod.exe --bind_ip 127.0.0.1 --logpath D:\MongoDB\dblog\mongodb.log --logappend --dbpath D:\MongoDB\db --port 27017 --service
  • bind_ip :绑定服务 IP,绑定127.0.0.1,则只能本机访问,不指定默认本地所有 IP ;
  • logpath :配置日志存放的位置;
  • logappend :日志使用追加的方式;
  • dbpath :配置数据存放的位置;
  • port :配置端口号,27017是默认端口;
  • service :以服务方式启动,即后台启动。

1.2 Linux 端启动 MongoDB 服务

1.2.1 数据存放位置

在 /data 路径下创建文件夹 db_test 来存放 MongoDB 服务的数据。

cd /data        # 进入data路径
mkdir db_test   # 创建db文件夹

1.2.2 日志文件

在 /logs 路径下创建文件夹 mongo 存放日志文件 mongod.log(文件不用创建,到时候会自动生成,但路径即文件夹必须提前创建好)。

mkdir /logs     # 创建/logs路径
cd /logs        # 进入log路径
mkdir mongo     # 创建mongo文件夹

1.2.3配置文件

在 /etc/mongod(没有路径就创建)路径下新建配置文件 mongod.conf,使用配置文件启动 MongoDB 服务(把命令写入配置文件,以后启动服务就不用再输入一长串的命令,直接启动配置文件即可)。

mkdir /etc/mongod		# 创建/etc/mongod路径
cd /etc/mongod			# 进入mongod路径
vim mongod.conf			# 进入vim编辑器编辑mongod.conf文件

\# vim编辑器内容
\# 按 "i" 键切换到输入模式

\# 输入配置内容:
port=27018	# 配置端口号
dbpath=/data/db_test	# 配置数据存放的位置
logpath=/logs/mongo/mongod.log	# 配置日志存放的位置
logappend=true	# 日志使用追加的方式
fork=true	# 设置在后台运行

\# 按 "ESC" 键退出输入模式切换到命令行模式
\# 输入 ":wq" 并按 "Enter"键 ,保存文件并退出 Vim 编辑器

以上工作准备完成,便可以开启服务了,配置文件启动命令如下(在命令行中输入):

mongod -f /etc/mongod/mongod.conf

1.3 启动客户端

输入命令 mongo --port 27017 连接客户端(MongoDB 默认的连接方式无需输入用户名和密码,port 端口根据你服务启动的端口连接)
如图出现最后一行的>符号,说明连接成功。
预览大图

1.4 退出客户端

在客户端输入 exit 便可顺利退出,如图所示:
预览大图

1.5 关闭 MongoDB 服务

1.5.1 能连接到客户端时

use admin     #使用系统数据库admin,只有在admin数据库中才可以进行关闭服务的操作

db.shutdownServer()     #关闭服务

显示如下图所示,则说明服务已成功关闭:
预览大图

1.5.2 客户端无法连接时

有时候数据库服务异常关闭时,我们连不上客户端,就可以使用该方法。
查看 Mongo 相关进程 ps -ef | grep mongo ,如图所示:
预览大图

kill mongod 开头的进程如图所示:
预览大图

1-2 MongoDB 数据库基本操作

2.1 数据库创建

2.1.1 连接数据库

  • MongoDB 安装完成后,可以通过 pgrep mongo -l 命令来查看是否已经启动。如图所示:
    预览大图

  • 在操作数据库之前,需要连接它,连接本地数据库服务器,输入命令:mongo,预期输出如图所示:
    预览大图预览大图

其中连接的警告可以不用管,只要最下方出现“ > ”符号,就说明连接成功。

2.1.2 创建数据库

  • 创建一个名为 Testdb 的数据库,用命令:use Testdb 语句来创建(如果数据库不存在,则创建数据库,否则切换到指定数据库),如图所示:
    预览大图

  • 查看所有数据库我们可以通过 show dbs 命令来查看,如图所示:
    预览大图

  • 然而并没有我们刚创建的 Testdb 数据库。要想显示它,我们需要向数据库插入一些数据:db.Testdb.insert({_id:1,name:"王小明"}),如图所示:
    预览大图
    现在,就可以看到我们创建的数据库 Testdb 了。

2.1.3 删除数据库

  • MongoDB 删除数据库需要先切换到该数据库中:
    use Testdb

  • 然后再执行删除命令:
    db.dropDatabase()

2.2 创建集合

2.2.1 在指定的数据库创建集合

  • 先进入指定数据库

    Testdb :use Testdb;
    
  • 在 Testdb 数据库中创建创建固定集合 test ,整个集合空间大小512000KB,文档最大个数为1000个:

    db.createCollection("test", { capped : true, autoIndexId : true, size : 512000, max : 1000 } )
    
    • capped :是一个布尔类型,true 时创建固定集合,必须指定 size。固定集合指有固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。默认为 false;

    • autoIndexId :也是一个布尔类型,如为 true,自动在_id 字段创建索引。默认为 false ;

    • size :为固定集合指定一个最大值(以字节 KB 计);

    • max :指定固定集合中包含文档的最大数量。

不过,和 MySQL 不同的是,在 MongoDB 中,你不一定需要先创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

2.2.2 默认id

MongoDB 中存储的文档 必须 有一个 _id 键(如果我们插入数据的时候未指定 _id ,系统会自动生成一个默认的 id )。

这个键的值可以是任何类型的,默认是个 ObjectId 对象。

在一个集合里面,每个文档都有唯一的 _id 值,来确保集合里面每个文档都能被唯一标识。

如果有两个集合的话,两个集合可以都有一个值为123的 _id 键,但是每个集合里面只能有一个 _id 是123的文档。

2.2.3 查询集合

命令:db.集合名.find()

由此我们也能看到 MongoDB 默认创建的_id:
预览大图

2.2.4 删除集合

命令:db.集合名.drop()

如图:
预览大图

2.3 文档操作

2.3.1 插入文档

命令:db.集合名.insert(文档)

  • 该命令前文我们也有用到。除了前两关的用法以外,我们还可以将数据定义为一个变量:

    document=({_id:1,
        name: '王小明',
        sex: '男',
        hobbies: ['乒乓球','羽毛球'],
        birthday: '1996-02-14'
    });
    
  • 然后执行插入操作:
    db.person.insert(document)

    person 是集合名,如果该集合不在该数据库中,MongoDB 会自动创建该集合并插入文档,查看 person 集合,如图所示:
    预览大图

2.3.2 更新文档

2.3.2.1 update()方法
<b>更新变量中单个键的值</b>
  • 先把 document 插入到集合 person2:

    db.person2.insert(document)
    
  • 用 update() 方法来更新 person2 的数据,把王小明的出生日期替换成1996,命令如下:

    db.person2.update({birthday:"1996-02-14"},{$set:{birthday:"1996"}})
    

    $set 是一个更新操作符,用于设置指定字段的值。

  • 更新后如图2所示( pretty() 方法的作用是使文档整齐的输出):
    预览大图

可以发现:

  • update() 有两个参数,都是对象,中间用逗号“ ,”间隔;
  • 第一个参数表示需要修改的值;
  • 第二个参数用 $set 操作符指向更新后的值。
2.3.2.2 save()方法
<b>替换集合中的单个变量</b>
  • 我们先把 document 插入到集合 person2:

    db.person3.insert(document)
    
  • 用 save() 方法把王小明的数据修改为李小红的数据:

    db.person3.save({
        "_id" :1,
        "name" : "李小红",
        "sex" : "女",
        "hobbies" : [
                "画画",
                "唱歌",
                "跳舞"
        ],
        "birthday" : "1996-06-14"
    })
    

注:如果 save() 法也指定了_id,则对文档进行更新;未指定_id则会执行插入功能,MongoDB 默认自动生成一个不重复的_id。

  • update() 方法仅适用于修改某条数据中的某个键值;
  • save() 方法适用于修改整条数据。

2.3.2 查询文档

  • 先插入文档到集合 stu1 :

    document=([{
        name:'张小华',
        sex:'男',
        age:20,
        phone:'12356986594',
        hobbies:['打篮球','踢足球','唱歌']
    },{
        name:'李小红',
        sex:'女',
        age:18,
        phone:'12355487536',
        hobbies:['跳舞','唱歌']
    }])
    
    db.stu1.insert(document)
    
  • 然后查看 stu1 ,使用命令find(),结果如图所示(因为我们没有设置_id,所以 MongoDB 会默认生成):
    预览大图

  • 也可以使用 pretty() 方法,使输出更整齐,如图所示:
    预览大图

2.3.2.1 条件查询
操作格式范例关系数据库中类似的语句
等于{<key>:<value>}db.stu1.find({"name":"李小红"}).pretty()where name = '李小红'
小于{<key>:{$lt:<value>}}db.stu1.find({"age":{$lt:18}}).pretty()where age < 18
小于或等于{<key>:{$lte:<value>}}db.stu1.find({"age":{$lte:18}}).pretty()where age <= 18
大于{<key>:{$gt:<value>}}db.stu1.find({"age":{$gt:18}}).pretty()where age > 18
大于或等于{<key>:{$gte:<value>}}db.stu1.find({"age":{$gte:18}}).pretty()where age >= 18
不等于{<key>:{$ne:<value>}}db.stu1.find({"age":{$ne:18}}).pretty()where age != 18
  • 在我们来查找一下 age 大于18岁的数据,命令和效果如图所示:
    预览大图
2.3.2.2 AND 条件

find() 方法可以传入多个键 (key),每个键 (key) 以逗号隔开,即常规 SQL 的 AND 条件。

  • 如查询集合 stu1 中年龄为20岁的男性信息:

    db.stu1.find({"age":20, "sex":"男"}).pretty()
    

    查询结果如图所示:
    预览大图

2.3.3 删除文档数据

2.3.3.1 删除指定的数据
db.stu1.remove({'age':20})     //删除年龄为20的数据
  • 查看该集合的内容,如果只剩下如图所示的信息,说明数据删除成功:
    预览大图
2.3.3.2 删除全部数据(集合并不会删除)
db.remove({})
  • 查看该集合的内容,如果如图所示,无显示或者显示为空,说明数据全部删除:
    预览大图

2-1 MongoDB数据库安全

1.1 创建管理员用户

1.1.2 创建管理员用户

  • 切换到admin数据库(admin数据库是一个具有特殊权限的数据库,用户需要访问它以便执行某些管理命令)

    use admin
    

    在数据库admin中,创建管理员用户 abc ,密码为 123 ,拥有 root 权限。

    db.createUser({user:"abc",pwd:"123",roles:[{role:"root",db:"admin"}]})
    

    结果如下图说明添加管理员用户成功:
    预览大图

  • 验证管理员用户是否存在:

    db.auth("abc","123")
    

    如下图,返回为1说明成功。
    预览大图

1.1.3 查看用户

进入admin数据库,查看我们创建的用户:

use admin
show users

显示如下图所示:
预览大图

1.1.4 启用身份验证

启用身份验证是为了保护数据库的安全。

身份验证默认是禁用的,因此要在启动MongoDB数据库服务mongod时使用参数–auth来启用身份验证(启用身份
验证前,确保你至少已有一个管理员用户)。

  • 先关闭现在有的数据库服务
    use admin 
    db.shutdownServer()
    

    运行结果如下:
    预览大图

  • 重新启动mongod服务

    在命令行输入(下面代码所使用的路径为平台所用路径):

    mongod --auth --port 27017 --dbpath /data/db --logpath /tmp/mongodb.log --fork
    
    • auth: 开启身份验证;
    • dbpath: 指定数据存放路径;
    • logpath: 指定日志文件输出路径;
    • fork: 后台运行。

1.1.5 管理员登录数据库

  • 情况一:在命令行连接数据库(注意选择admin数据库)
    mongo -uabc -p123 admin
    
  • 情况二:在受限的情况下验证身份

    开启身份验证后,我们使用命令mongo直接连接数据库,虽然也能成功连接,但是在进行数据库操作如show dbs时会受到限制:
    预览大图

    这时我们就要进入到admin数据库,去进行身份验证:

    use admin
    db.auth("abc","123")
    

    返回数字1说明验证成功,0说明失败。
    预览大图

    验证成功后将拥有管理员权限。

1.1.6 删除管理员用户

删除管理员用户abc命令如下:

db.system.users.remove({user:"abc"})

返回1说明删除成功,0说明删除失败。

1.2 创建普通用户

1.2.1 常用用户权限介绍

权限说明
Read允许用户读取指定数据库
readWrite允许用户读写指定数据库
dbAdmin允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
readAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
root只在admin数据库中可用。超级账号,超级权限

1.2.2 创建普通用户

  • 创建用户:user1,密码:useu1,拥有数据库test的读写权限

    use test
    db.createUser({user:"user1",pwd:"user1",roles:[{role:"readWrite",db:"test"}]})
    

    注:该用户只对数据库test有权限。

    查看用户命令:show users
    预览大图

  • 创建用户:user2,密码:user2,对数据库test2有读写权限,对数据库test有只读权限

    use test2
    db.createUser({user:"user2",pwd:"user2",roles:[{role:"readWrite",db:"test2"},{role:"read",db:"test"}]})
    

    查看用户:
    预览大图

1.2.3 删除用户

(要先确定自己有root权限)删除user2用户,先进入test2数据库:

use test2
db.dropUser("user2")

结果返回true说明删除成功:
预览大图

1.3 数据库限制访问

1.3.1 限制IP访问

限制 IP 访问,需要重启数据库服务 mongod 时,启用相应的功能,步骤如下:

  • 关闭服务(先用默认方法启动数据库:mongo ):

    use admin            #进入admin数据库
    db.shutdownServer()  #关闭服务
    exit                 #退出数据库
    
  • 启动服务(只有本机 IP 可以连接数据库):

    mongod --dbpath /data/db --logpath /tmp/mongodb.log --bind_ip 127.0.0.1 --fork
    

    binf_ip :限制连接的网络接口,可以设置多个,以逗号隔开。

使用其他 IP 访问被限制,如图所示:
预览大图

1.3.2 限制端口访问

当按照以上方法启动 mongod 时,默认情况下他会等待所有在端口27017上的入站连接,可以用 -port 修改该设置。也需要重新启动 mongod 服务,方法同上。

  • 关闭服务(先用默认方法启动数据库:mongo ):

    use admin            #进入admin数据库
    db.shutdownServer()  #关闭服务
    exit                 #退出数据库
    
  • 启动服务(只有本机 IP 可以连接数据库,且限制只能端口20000连接):

    mongod -port 20000 --dbpath /data/db --logpath /tmp/mongodb.log --bind_ip 127.0.0.1 --fork
    
  • 连接数据库

    mongo 127.0.0.1:20000
    

设置了端口后如果不加端口连接会被拒绝访问,如图所示:
预览大图

注:在数据库实际的应用中,最好不要使用这些默认设置,对这些设置进行一些修改,让你的数据更加安全。

3-1 MongoDB 之聚合函数查询统计

1.1 聚合管道操作符将文档定制格式输出

1.1.1 常用聚合管道操作符

操作符作用
$project修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档
$match用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作
$limit用来限制MongoDB聚合管道返回的文档数
$skip在聚合管道中跳过指定数量的文档,并返回余下的文档
$unwind将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
$group将集合中的文档分组,可用于统计结果
$sort将输入文档排序后输出

注意:以上操作不会修改集合的内容,只是将集合以指定形式输出。

在数据库 mydb1 有集合 educoder 内容如下(后文均用此用例)

_idcourseauthortagslearning_num
1Python表达式问题求解实训李暾Python基础,求解1882
2Java语言之基本语法余跃Java基础,语法814
3Python面向对象编程实训李暾Python基础,面向对象143
4Android综合实训之物联网移动应用开发(1)prophet5Android,物联网,移动开发207

1.1.2 $project 修改文档结构输出

有时候我们并不会用到文档的全部内容,只是使用其中几列,这时候就可以使用 $project 进行操作;或者有时候要重命名键值(列名),也可以使用 $project

对集合 educoder 进行操作:

  • 只输出作者 author 和学习人数 learning_num 信息,_id也不要不显示(_id默认是显示的):

    db.educoder.aggregate({$project:{_id:0,author:1,learning_num:1}})
    

    0 为不显示,非 0 为显示。

    运行结果如图所示:
    预览大图

  • 重命名字段名(把 learning_num 重命名为 num):

    db.educoder.aggregate({$project:{course:1,authoe:1,tags:1,num:'$learning_num'}})
    

    运行结果如图所示:
    预览大图

1.1.3 $match 筛选文档输出

有时候我们要在集合中筛选出符合特定条件的文档,这时候使用 $match 便可以很快实现。

只输出作者为“李暾”的文档:

db.educoder.aggregate({$match:{author:'李暾'}})

运行结果如图所示:
预览大图

1.1.4 $limit 限制文档数量输出

有时候集合中文档数量太大,我们只想选取前几行查看一下,这时候就可以用 $limit,输出前2条文档:

db.educoder.aggregate({$limit:2})

运行结果如图所示:
预览大图

1.1.5 $skip 跳过前 n 条文档输出

与 $limit 相反,$skip 是跳过前 n 条文档,显示剩余文档。

将集合 educoder 中的前两条文档跳过,显示剩余文档:

db.educoder.aggregate({$skip:2})

$skip 接受一个数字 n,表示丢弃结果集中的前 n 个文档;

运行结果如图所示:
预览大图

  • $limit 与 $skip 可以组合使用

    db.educoder.aggregate([{$skip:1},{$limit:2}])
    #跳过第一条,显示前两条,也就是显示第2-3条文档
    db.educoder.aggregate([{$limit:2},{$skip:1}])
    #显示前两条,跳过第一条,也就是显示第2条文档
    

    运行结果如图所示:
    预览大图

1.1.6 $unwind 拆分数组类型字段

将 educoder 中的 tags 字段拆分成多条,每条包含数组中的一个值:

db.educoder.aggregate({$unwind:'$tags'})

运行结果如图所示:
预览大图

1.1.7 $group 分组输出

该操作比较常用,因为文档分组后利于我们的统计。比如,按照作者分组我们就可以统计出该集合总共有几个作者。

在集合 educoder 中,按作者分组:

db.educoder.aggregate({$group:{_id:'$author'}})

运行结果如图所示:
预览大图

1.1.8 $sort 排序输出

按照学习人数 learning_num 降序排序输出文档:

db.educoder.aggregate({$sort:{learning_num:-1}})

-1 代表降序排序, 1 代表升序排序。

运行结果如图所示:
预览大图

1.2 聚合表达式对文档数据进行统计

1.2.1 常用聚合表达式

表达式用法
$sum计算总和
$avg计算平均值
$min获取集合中所有文档对应值的最小值
$max获取集合中所有文档对应值的最大值
$push在结果文档中插入值到一个数组中
$addToSet在结果文档中插入值到一个数组中,但不创建副本
$first根据资源文档的排序获取第一个文档数据
$last根据资源文档的排序获取最后一个文档数据

1.2.3 如何对文档进行统计

用例使用对应集合数据详情见上文(点击跳转)

  • 通过 aggregate() 方法来获取每个作者拥有的实训数量,命名为:num_course:

    db.educoder.aggregate([{$group:{_id:'$author',num_course:{$sum:1}}}])
    

    查询结果如下:
    预览大图

    命令解析:

    • 先通过聚合管道操作符 $group 将 author 字段数据分组;

    • $sum:1 的含义:如果前面的情况出现一次,就加 1 ,如果后面为 $sum:2 ,那么前面条件每满足一次就加 2 ;

  • 通过 aggregate() 方法来获取每个作者的实训学习总人数 learning_sum :

    db.educoder.aggregate([{$group:{_id:'$author',learning_sum:{$sum:'$learning_num'}}}])
    

    查询结果如下:
    预览大图

3-2 MongoDB 之滴滴、摩拜都在用的索引

2.1 了解索引

  • 什么是索引:
    索引本质上是树,最小的值在最左边的叶子上,最大的值在最右边的叶子上,使用索引可以提高查询速度(而不用全表扫描)。

  • 索引的原理:
    对某个键按照升续或降续创建索引,查询时首先根据查询条件查找到对应的索引条目,然后找到索引条目对应的文档指针(文档在磁盘上的存储位置),根据文档指针再去磁盘中找到相应的文档,整个过程不需要扫描全表,速度比较快。

    每个文档被插入集合时,如果没有给它指定索引_id,MongoDB 会自动给它创建一个默认索引_id,是个 ObjectId 对象。如图所示:
    预览大图

2.2 索引的分类

索引类型用途
包括单字段索引(Single Field Index)==针对某一键 key ==创建了单字段索引,其能加速对 key 字段的各种查询请求,是最常见的索引形式,MongoDB 默认创建的 id 索引也是这种类型。
复合索引 (Compound Index)复合索引是单字索引的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推。
多 key 索引 (Multikey Index)索引的字段为数组时,创建出的索引称为多 key 索引。
哈希索引(Hashed Index)按照某个字段的hash值来建立索引,目前主要用于 MongoDB Sharded Cluster 的 Hash 分片,哈希索引只能满足字段完全匹配的查询,不能满足范围查询等。
地理位置索引(Geospatial Index)能很好的解决 O2O 的应用场景,比如:查找附近的美食、查找某个区域内的车站等。
文本索引(Text Index)能解决快速文本查找的需求,比如有一个博客文章集合,需要根据博客的内容来快速查找,则可以针对博客内容建立文本索引。

2.3 索引基本操作

2.3.1 创建索引

db.person.createIndex({key:1})

key :要创建索引的键;
如果为 1 说明是按照升序创建索引,而如果为 -1,则是按降序创建索引。

如图所示,说明创建索引成功:
预览大图

2.3.2 查询索引

  • 查询集合索引:

     db.person.getIndexes()
    

    效果如图所示:
    预览大图

  • 查询系统全部索引:

    db.system.indexes.find()
    

2.3.3 删除索引

  • 通过指定索引名称删除该索引:

    db.person.dropIndex("ageIdx")
    

    效果如图所示:
    预览大图

  • 通过指定集合删除集合中的全部索引:

    db.person.dropIndexes()
    

    效果如图所示:
    预览大图

注:默认索引_id不会且不能被删除

2.4 常见索引的创建

2.4.1 创建复合索引

创建单字段索引的方法差不多,只是选取了多个键一同作为索引,中间以逗号隔开:

db.person.createIndex({age: 1, name: 1})

用法也和单字段索引差不多,就不再赘述。

2.4.2 创建多 key 索引

当索引的字段为数组时,创建出的索引称为多 key 索引,多 key 索引会为数组的每个元素建立一条索引,比如 person 集合加入一个 habbit 字段(数组)用于描述兴趣爱好:

{name : '王小明', age : 19, habbit: ['football', 'runnning']}

需要查询有相同兴趣爱好的人就可以利用 habbit 字段的多 key 索引。

db.person.createIndex( {habbit: 1} )     // 升序创建多key索引
db.person.find({habbit: 'football'})     //查找喜欢足球的人

2.4.3 创建哈希索引

创建命令如下:

db.person.createIndex( { _id: 'hashed' } )

2.4.4 文本索引的创建与使用

2.4.4.1 什么时候使用文本索引

假如我们用 Mongodb 存储了很多博客文章,那么如何快速找到所有关于 mongodb 这个主题的文章呢?这时候就要用到文本搜索了。

有文章集合 collection,如下:

{
  title: 'enjoy the mongodb articles on educoder',
  tags: [
     'mongodb',
     'educoder'
  ]
}
2.4.4.2 创建文本索引

创建文本索引命令:

db.collection.createIndex({ title: 'text'})
  • 创建全文本索引的字段必须为 string 格式;
  • 每个集合只支持一个文本索引。

运行效果如图所示:
预览大图

2.4.4.3 使用文本索引

现在我们已经创建了 title 的索引,我们来搜索一下含有 educoder.net 的文章:

db.collection.find({$text:{$search:'educoder.net'}})
  • search 后的关键词可以有多个,关键词之间的分隔符可以是多种字符,例如空格、下划线、逗号、加号等,但不能是-和\,因为这两个符号会有其他用途。搜索的多个关键字是 or 的关系,除非你的关键字包含-;
  • 匹配时不是完整的单词匹配,相似的词也可以匹配到;

运行效果如图所示:
预览大图

2.4.4.4 删除文本索引
  • 通过命令获取索引名:

    db.collection.getIndexes()
    
  • 删除命令:

    db.collection.dropIndex('title_text')
    

    运行效果如图所示:
    预览大图

2.5 地理位置索引

2.5.1 GeoJson 数据

并不是所有文档都可以创建地理位置索引,只有拥有特定格式的文档才可以创建。

如果我们用的是 2dsphere 索引,那么插入的应该是 GeoJson 数据。

GeoJson 的格式如是:

{ type: 'GeoJSON type' , coordinates: 'coordinates' }
  • type :指的是类型,可以是 Point (本例中用的)、LineString、 Polygon 等;
  • coordinates :指的是一个坐标数组。

我们有如下几个位置的坐标信息(具体详情如图所示),先把它们写入集合 locations 中。

db.locations.insert({_id:1,name:'长沙站',location:{type:'Point',coordinates:[113.018987,28.201215]}})
db.locations.insert({_id:2,name:'湖南师范大学',location:{type:'Point',coordinates:[112.946045,28.170968]}})
db.locations.insert({_id:3,name:'中南大学',location:{type:'Point',coordinates:[112.932175,28.178291]}})
db.locations.insert({_id:4,name:'湖南女子学院',location:{type:'Point',coordinates:[113.014675,28.121163]}})
db.locations.insert({_id:5,name:"湖南农业大学",location:{type:'Point',coordinates:[113.090852,28.187461]}})

location:索引名

预览大图

2.5.1 创建地理位置索引

有了规范的文档后,我们就可以使用以下命令,在 location 键上创建一个地理位置索引:

db.locations.createIndex({location:'2dsphere'})
  • location:索引名

  • 2d :平面坐标索引,适用于基于平面的坐标计算,也支持球面距离计算,不过官方推荐使用 2dsphere 索引;

  • 2dsphere :几何球体索引,适用于球面几何运算;

  • 默认情况下,地理位置索引会假设值的范围是从−180到180(根据经纬度设置)。

如索引创建成功,效果如图所示:
预览大图

2.5.2 地理位置索引的使用

  • 查询命令:

    db.runCommand({
        geoNear:'locations',
        near:{type:'Point',coordinates:[113.018987,28.201215]},
        spherical:true,
        minDistance:1000,
        maxDistance:8000
        })
    
    • geoNear :我们要查询的集合名称;

    • near :就是基于哪个点进行搜索,这里是我们的搜索点“长沙站”;

    • spherical :是个布尔值,如果为 true,表示将计算实际的物理距离,比如两点之间有多少 km,若为 false,则会基于点的单位进行计算 ;

    • minDistance :搜索的最小距离,这里的单位是米 ;

    • maxDistance :搜索的最大距离。

  • 查询结果分析:

    result 中,查到了“湖南农业大学”和“湖南师范大学”,每个文档都加上了一个 dis 字段,它表示这个点离你搜索点的距离

    比如说,在结果中 name 为“湖南农业大学”的点的 dis 为7215.061630510019。表示“湖南农业大学”距离搜索点“长沙站”的距离是7215米。这个结果对于 LBS 应用是非常有用的。

    具体详情如图所示:
    预览大图

3-3 MongoDB 文档的高级查询操作

3.1 数据的导入导出

3.1.1 向数据库导入数据

数据导入工具:mongoimport

这是 MongoDB 自带的数据导入工具,我们在未连接客户端时使用(前提要启动服务)。

mongoimport -d Testdb1 -c score --type csv --headerline --ignoreBlanks --file test.csv
  • -d Testdb1 :指定将数据导入到 Testdb1 数据库

  • -c score :将数据导入到集合 score ,如果这个集合之前不存在,会自动创建一个(如果省略 --collection 这个参数,那么会自动新建一个以 CSV 文件名为名的集合);

  • --type csv文件类型,这里是 CSV;

  • --headerline :这个参数很重要,加上这个参数后创建完成后的内容会以 CSV 文件第一行的内容为字段名导入json文件不需要这个参数);

  • --ignoreBlanks :这个参数可以忽略掉 CSV 文件中的空缺值导入json文件不需要这个参数);

  • --file 1.csv :这里就是 CSV 文件的路径了,需要使用绝对路径

3.1.2 从数据库导出数据

数据导出工具: mongoexport

  • 导出 json 格式文件:

    mongoexport -d Testdb1 -c score -o /file.json --type json
    
    • -o /file.json :输出的文件路径/(根目录下)和文件名
    • --type json :输出的格式,默认为 json。
  • 导出 csv 格式的文件:

    mongoexport -d Testdb1 -c score -o /file.json --type csv -f "_id,name,age,sex,major"
    

    -f :当输出格式为 csv 时,需要指定输出的字段名

3.2 高级查询

假设有集合 hobbies 内容如下:

_idnamesexhobbies
1小红唱歌,跳舞,羽毛球
2小明唱歌,乒乓球,羽毛球
3小亮乒乓球,羽毛球

集合 student 如下:

_idnameagesexmajor
1王晓丽19计算机
2张明21计算机
3秋雅20播音主持
4张欣欣18表演

3.2.1 $all 匹配所有

查询集合 hobbies 中所有喜欢“唱歌”和“羽毛球”的人:

db.hobbies.find({hobbies:{$all:["唱歌","羽毛球"]})

查询结果如图所示:
预览大图

$all 会查询满足方括号中所有条件的文档,如果只有其中一项满足是不会被查询出来的。

3.2.2 $exists 判断字段是否存在

在集合 hobbies 中更新文档:

db.hobbies.save({_id:1,name:"小红",age:18,sex:"女",hobbies:["唱歌","跳舞","羽毛球"]})     #添加小红年龄18
  • 查询 hobbies 集合中存在 age 字段的文档,如图所示:
    预览大图
  • hobbies 集合中不存在 age 字段的文档,如图所示:
    预览大图

3.2.3 $mod 取模运算

在集合 hobbies 中更新文档:

db.hobbies.update({_id:2},{$set:{age:20}})     #添加小明年龄20
db.hobbies.update({_id:3},{$set:{age:22}})     #添加小亮年龄22

查询 age 取模7等于4的文档:

db.hobbies.find({age:{$mod:[7,4]}})

查询结果如如图所示:
预览大图

3.2.4 $in 包含

查询 age =17或 age =20的文档 :

db.hobbies.find({age:{$in:[17,20]}})

查询结果如图所示:
预览大图

3.2.5 $nin 不包含

查询 age !=17且 age !=20的文档:

db.hobbies.find({age:{$nin:[17,20]}})

查询结果如图所示:
预览大图

3.2.6 $size 数组元素个数

可以查询特定长度的数组,比如 hobbies 这一字段,查询有两个爱好的文档:

db.hobbies.find({hobbies:{$size:2}})

查询结果如图所示:
预览大图

3.2.7 查询结果排序

查询结果排序语法如下:

db.collection.find().sort({_id:1})     #将查询结果按照_id升序排序
db.collection.find().sort({_id:-1})    #将查询结果按照_id降序排序

collection :需进行排序操作的集合名

3.2.8 $or 条件之间的或查询

$or 表示多个查询条件之间是或的关系,比如查询性别 sex 为 男 或年龄 age 为18的文档信息:

db.student.find({$or:[{sex:"男"},{age:18}]})

查询结果,如图所示:
预览大图

3.2.9 $and 条件之间的且查询

$and 表示多个查询条件之间是且的关系,比如查询年龄 age 大于18且小于21(18 < age < 21)的信息:

db.student.find({$and:[{age:{$gt:18}},{age:{$lt:21}}]})

查询结果,如图所示:
预览大图

  • gt: greater than 大于

  • gte: greater than or equal 大于等于

  • lt: less than 小于

  • lte: less than or equal 小于等于

3.2.10 $not 条件取反查询

$not 用来执行取反操作,比如查询年龄 age 大于等于20岁,然后进行取反(即查询年龄小于20岁的文档):

db.student.find({age:{$not:{$gte:20}}})

查询结果,如图所示:
预览大图

3.2.11 正则表达式匹配查询

查询不符合 major=计* 开头文档:

db.student.find({major:{$not:/^计.*/}})

查询结果,如图所示:
预览大图

3.2.12 count() 返回结果集总数

比如返回上一步正则查询到的结果集有几条:

db.student.find({major:{$not:/^计.*/}}).count()

查询结果,如图所示:
预览大图

3.3 游标

3.3.1 什么是游标

通俗的说,游标不是查询结果,而是查询的返回资源,或者接口。通过这个接口,你可以逐条读取。就像 fopen 打开文件,得到一个资源一样,通过资源,可以一行一行的读文件。

3.3.2 使用循环插入数据

我们首先插入10000条数据到集合 items,因为 mongodb 底层是 javascript 引擎,所以我们可以使用 js 的语法来插入数据:

for(var i=0;i<10000;i++)db.items.insert({_id:i,text:"Hello MongoDB"+i})

插入后查看效果,如图所示:
预览大图

3.3.3 声明游标

定义一个变量来保存这个游标,find 的查询结果(_id<=5)赋值给了游标 cursor 变量,代码如下:

var cursor=db.items.find({_id:{$lte:5}})

3.3.4 打印游标中的数据信息

  • printjson(cursor.next()) 打印下一条数据,如图所示:
    预览大图

    注意:当取完游标中的数据,又进行打印,会报错。

  • 使用 jswhile 语法来循环打印,具体步骤如图所示:
    预览大图

  • 使用 for 循环打印,具体步骤如图所示:
    预览大图

  • 使用 forEach 打印,具体步骤如图所示:
    预览大图

3.3.5 游标的使用场景

我们可以在分页的情况下使用游标。

假设每页有10行,我们查询第701页,可以配合 skip()limit() 来实现,具体步骤如图所示:
预览大图

如果不想全部取出,只取出某一个,可以使用如下方法,取出数组下标,具体步骤如图所示:
预览大图

4-1 MongoDB 实验——java 和 MongoDB

1.1 Java 连接数据库

1.1.1 获取 Java 的 Mongodb 驱动包

Java 的 MongoDB 驱动包获取网址 下载获取最新版本的驱动包,放在响应环境目录下。

1.1.2 Java 代码连接无密码数据库

通过以下两条代码,进行数据库的连接:

MongoClient mongoClient = new MongoClient("localhost",27020);     //启动本地服务,端口号为27020
MongoDatabase mongoDatabase = mongoClient.getDatabase("databaseName");     //连接名为databaseName数据库

1.1.3 编译代码进行数据库连接

此步骤无需操作,平台会完成编译:

javac -cp .:mongo-java-driver-3.9.1.jar Mongoconnect.java
java -cp .:mongo-java-driver-3.9.1.jar Mongoconnect

1.2 创建集合

我们可以使用驱动 com.mongodb.client.MongoDatabase 类中的 createCollection() 来创建集合:

mongoDatabase.createCollection("test");     //创建集合test

1.3 获取集合

使用驱动 com.mongodb.client.MongoDatabase 类的 getCollection() 方法来获取集合:

MongoCollection<Document> collection = mongoDatabase.getCollection("test");     //获取集合test

1.4 插入文档

使用驱动 com.mongodb.client.MongoCollection 类的 insertMany() 方法来插入一个文档:

//注意,在插入文档前,先选好集合再进行操作,选择test集合
MongoCollection<Document> collection = mongoDatabase.getCollection("test");

Document document1 = new Document();     //创建一条文档 document1,以下代码为向文档 document1 中追加数据
document1.append("title", "MongoDB");
document1.append("content", "I love MongoDB").
document1.append("nums", 90).
document1.append("tags", "database");

Document document2 = new Document();     //创建一条文档 document2,以下代码为向文档 document1 中追加数据
document2.append("title", "Java");
document2.append("content", "I love Java").
document2.append("nums", 30).
document2.append("tags", "language");

List<Document> documents = new ArrayList<Document>();     //将以上文档打包存放,为文档插入做准备
documents.add(document1);
documents.add(document2);
collection.insertMany(documents);     //插入多条文档到集合中
  • MongoCollection<Document> 是 MongoDB Java 驱动中的一种类型,表示一个 MongoDB 集合,其中的每个文档以 Document 类型存储
  • 插入一条文档时应使用 insertOne() 方法

1.5 检索文档

使用驱动 com.mongodb.client.MongoCollection 类中的 find() 方法来获取集合中的所有文档

此方法返回一个游标,所以你需要遍历这个游标。

//检索所有文档
/**
 * 1. 获取迭代器FindIterable<Document> 
 * 2. 获取游标MongoCursor<Document> 
 * 3. 通过游标遍历检索出的文档集合 
 **/
FindIterable<Document> findIterable = collection.find();
MongoCursor<Document> mongoCursor = findIterable.iterator();
while(mongoCursor.hasNext()){
System.out.println(mongoCursor.next());
}

1.6 更新文档

使用 com.mongodb.client.MongoCollection 类中的 updateMany() 方法来更新集合中的文档:

//更新文档:将第一关例子文档中nums=90的文档修改为nums=100
collection.updateMany(Filters.eq("nums", 90), new Document("$set",new Document("nums",100)));
  • updateMany :更新符合条件的所有记录
  • updateOne :更新符合条件的第一条记录

1.7 删除文档

要删除集合中的第一个文档,首先你需要使用 com.mongodb.DBCollection 类中的 findOne() 方法来获取第一个文档,然后使用 remove() 方法删除。

//删除符合条件的第一个文档
collection.deleteOne(Filters.eq("nums", 100));
//删除所有符合条件的文档
collection.deleteMany (Filters.eq("nums", 100));

5-1 MongoDB 实验——数据库优化

1.1 查询优化原则

  • 查询条件、排序条件、统计条件的字段上选择创建索引,可以显著提高查询效率;
  • $or 时把匹配最多结果的条件放在最前面,用 $and 时把匹配最少结果的条件放在最前面;
  • 使用 limit() 限定返回结果集的大小,减少数据库服务器的资源消耗,以及网络传输的数据量;
  • 尽量少用 $in,而是分解成一个一个的单一查询。尤其是在分片上,$in 会让你的查询去每一个分片上查一次,如果实在要用的话,先在每个分片上建索引
  • 尽量不用模糊匹配查询,用其它精确匹配查询代替,比如 $in$nin
  • 查询量大、并发大的情况,通过前端加缓存解决;
  • 尽量不用安全模式,这样客户端没必要等待数据库返回查询结果以及处理异常,快了一个数量级;
  • MongoDB 的智能查询优化,判断粒度为 query 条件,而 skiplimit 都不在其判断之中,当分页查询最后几页时,先用 order 反向排序;
  • 尽量减少跨分片查询balance 均衡次数少;
  • 只查询要使用的字段,而不查询所有字段;
  • 更新字段的值时,使用 $incupdate 效率高;
  • apped collections 比普通 collections 的读写效率高;
  • server-side processing 类似于 SQL 查询的存储过程,可以减少网络通讯的开销;
  • 必要时使用 hint() 强制使用某个索引查询
  • 如果有自己的主键列,则使用自己的主键列作为 id,这样可以节约空间,也不需要创建额外的索引;
  • 使用 explain根据 exlpain plan 进行优化
  • 范围查询的时候尽量用 $in$nin 代替;
  • 查看数据库查询日志,具体分析的效率低的操作;
  • mongodb 有一个数据库优化工具 database profiler,能够检测数据库操作的性能。可以发现 query 或者 write 操作中执行效率低的,从而针对这些操作进行优化;
  • 尽量把更多的操作放在客户端,当然这就是 mongodb 设计的理念之一。

1.2 MongoDB 的 Profiling 工具

1.2.1 Profiling 工具

在很多情况下, DBA(数据库管理员)都要对数据库的性能进行分析处理,找出降低性能的根源。而 Profiling 就是 Mongo 自带的一种分析工具来检测并追踪影响性能的慢查询

慢查询日志一般作为优化步骤里的第一步。通过慢查询日志,定位每一条语句的查询时间。比如超过了50ms,那么查询超过50ms 的语句需要优化。然后它通过 .explain() 解析影响行数是不是过多,所以导致查询语句超过50ms。

所以优化步骤一般就是:

  • 用慢查询日志(system.profile)找到超过50ms 的语句;

  • 然后再通过 .explain() 解析影响行数,分析为什么超过50ms;

  • 决定是不是需要添加索引。

Profiling 级别说明:

  • 0:关闭,不收集任何数据;

  • 1:收集慢查询数据,默认是100毫秒;

  • 2:收集所有数据。

1.2.2 启用 Profiling 工具

Profiling 有两种开启方式,一种是启动服务时配置启动,一种是 mongoshell 中进行实时配置。

1.2.2.1 mongo shell 中启动配置
  • 查看状态,如图所示:级别和时间;
    预览大图
  • 查看级别,如图所示:
    预览大图
  • 设置级别,如图所示:
    预览大图
  • 设置级别和时间,如图所示:
    预览大图

注意:

  • 以上要操作要是在 test 集合下面的话,只对该集合里的操作有效,要是需要对整个实例有效,则需要在所有的集合下设置或则在开启的时候开启参数;

  • 每次设置之后返回给你的结果是修改之前的状态(包括级别、时间参数)。

1.2.2.2 全局开启 Profiling
  • 可以在 mongod 启动时加上以下参数:

    mongod --profile=1  --slowms=50
    
  • 或在配置文件里添加两行,如下所示:

    profile = 1
    slowms = 50
    

1.2.3 关闭 Profiling 工具

只需要将收集慢查询数据的时间设置为0就可以关闭,如图所示:
预览大图

1.2.4 慢查询分析

要进行慢查询分析,首先,要启用 Profiling 工具,以下例子 Profiling 级别设置为1,时间设置为50ms。其次,要进行过超过 50ms 的操作才会记录到慢查询日志中,存在记录结果。

  • 首先,我们在 test 数据库启用 Profiling 工具

    use test
    db.setProfilingLevel(1,50)     # 设置级别为1,时间为50ms,意味着只有超过50ms的操作才会记录到慢查询日志中
    
  • 然后在 test 数据库的 items 集合中循环插入100万条数据

    for(var i=0;i<1000000;i++)db.items.insert({_id:i,text:"Hello MongoDB"+i})
    
  • 返回所有结果:

    db.system.profile.find().pretty()
    
{
       "op" : "insert",     #操作类型,有insert、query、update、remove、getmore、command
       "ns" : "test.items",     #操作的集合
       "command" : {
               "insert" : "items",
               "ordered" : true,
               "$db" : "test"
       },
       "ninserted" : 1,
       "keysInserted" : 1,
       "numYield": 0,     #该操作为了使其他操作完成而放弃的次数。通常来说,当他们需要访问还没有完全读入内存中的数据时,操作将放弃。这使得在MongoDB为了放弃操作进行数据读取的同时,还有数据在内存中的其他操作可以完成
       "locks": {     #锁信息,R:全局读锁;W:全局写锁;r:特定数据库的读锁;w:特定数据库的写锁
               "Global" : {
                       "acquireCount" : {
                               "r" : NumberLong(1),
                               "w" : NumberLong(1)
                       }
               },
               "Database" : {
                       "acquireCount" : {
                               "w" : NumberLong(1)
                       }
               },
               "Collection" : {
                       "acquireCount" : {
                               "w" : NumberLong(1)
                       }
               }
       },
       "responseLength" : 45,     #返回字节长度,如果这个数字很大,考虑值返回所需字段
       "protocol" : "op_msg",
       "millis" : 60,     #消耗的时间(毫秒)
       "ts" : ISODate("2018-12-07T08:19:11.997Z"),     #该命令在何时执行
       "client" : "127.0.0.1",     #链接ip或则主机
       "appName" : "MongoDB Shell",
       "allUsers" : [ ],
       "user" : ""
}

profile 部分字段解释:

  • op :操作类型;

  • ns :被查的集合;

  • commond :命令的内容;

  • docsExamined :扫描文档数;

  • nreturned :返回记录数;

  • millis :耗时时间,单位毫秒;

  • ts :命令执行时间;

  • responseLength :返回内容长度。

1.2.5 常用的慢日志查询命令

  • 返回最近的10条记录:

    db.system.profile.find().limit(10).sort({ ts : -1 }).pretty()
    
  • 返回所有的操作,除 command 类型的:

    db.system.profile.find( { op: { $ne : 'command'} }).pretty()
    
  • 返回特定集合:

    db.system.profile.find( { ns : 'test.items' } ).pretty()
    
  • 返回大于5毫秒慢的操作:

    db.system.profile.find({ millis : { $gt : 5 } } ).pretty()
    
  • 从一个特定的时间范围内返回信息:

    db.system.profile.find(
                        {
                         ts : {
                               $gt : new ISODate("2018-12-09T08:00:00Z"),
                               $lt : new ISODate("2018-12-10T03:40:00Z")
                              }
                        }
                       ).pretty()
    
  • 特定时间,限制用户,按照消耗时间排序:

    db.system.profile.find(
                        {
                          ts : {
                                $gt : new ISODate("2018-12-09T08:00:00Z") ,
                                $lt : new ISODate("2018-12-10T03:40:00Z")
                               }
                        },
                        { user : 0 }
                       ).sort( { millis : -1 } ).pretty()
    
  • 查看最新的 Profile 记录:

    db.system.profile.find().sort({$natural:-1}).limit(1).pretty()
    
  • 显示5个最近的事件:

    show profile
    

6-1 MongoDB实验——数据备份和恢复

1.1 数据备份

1.1.1 mongodump 备份工具

mongodump 的参数与 mongoexport(数据导出)的参数基本一致:

参数参数说明
-h指明数据库宿主机的IP
-u指明数据库的用户名
-p指明数据库的密码
-d指明数据库的名字
-c指明collection名字
-o指明到要导出的文件名
-q指明导出数据的过滤条件
--authenticationDatabase验证数据的名称
--gzip备份时压缩
--oploguse oplog for taking a point-in-time snapshot

1.1.2 使用 mongodump 备份数据

备份工具同导入导出工具类似,都是在命令行进行操作,无需进入客户端。

  • 全库备份(如果数据库未设置用户和密码,可以省略 -uroot -proot 参数)

    mongodump -h 127.0.0.1:27300 -uroot -proot --authenticationDatabase admin  -o /home/mongod
    #备份本地27300端口中root用户的所有数据库到/home/mongod目录下
    
  • 单个数据库备份

    mongodump -h 127.0.0.1:27300 -uroot -proot --authenticationDatabase admin  -d test -o /home/mongod/test
    #备份本地27300端口中root用户的test数据库到/home/mongod/test目录下
    
  • 集合备份

    mongodump -h 127.0.0.1:27300 -uroot -proot --authenticationDatabase admin  -d test -c haha -o /home/mongod/test/haha
    #备份27300端口中root用户的test数据库的haha集合到/home/mongod/test/haha目录下
    
  • 压缩备份库

    mongodump -h 127.0.0.1:27300 -uroot -proot --authenticationDatabase admin  -d test -o /home/mongod/test1 --gzip
    #压缩备份本地27300端口中root用户的test数据库到/home/mongod/test1目录下
    
  • 压缩备份集合

    mongodump -h 127.0.0.1:27300 -uroot -proot --authenticationDatabase admin  -d test -c haha -o /home/mongod/test1/haha --gzip
    #压缩备份27300端口中root用户的test数据库的haha集合到/home/mongod/test1/haha目录下
    

1.2 数据恢复

1.2.1 mongorestore 恢复工具

参数参数说明
-h指明数据库宿主机的IP
-u指明数据库的用户名
-p指明数据库的密码
-d指明数据库的名字
-c指明collection名字
-o指明到要导出的文件名
-q指明导出数据的过滤条件
--authenticationDatabase验证数据的名称
--gzip备份时压缩
--oploguse oplog for taking a point-in-time snapshot
--drop恢复的时候把之前的集合 drop 掉

使用 mongorestore 恢复数据

  • 全库备份中恢复单库(基于之前的全库备份)

    mongorestore -h 127.0.0.1:27017 -uroot -proot --authenticationDatabase admin -d test --drop  /home/mongod
    #从/home/mongod目录下恢复全部数据库的数据到本地27300端口中root用户中(基于第一关的备份,下同)
    
  • 恢复 test 库

    mongorestore -h 127.0.0.1:27017 -uroot -proot --authenticationDatabase admin -d test /home/mongod/test
    #从/home/mongod/test目录下恢复名为test的单个数据库的数据到本地27300端口中root用户中的test数据库
    
  • 恢复 test 库下的 haha 集合

    mongorestore -h 127.0.0.1:27017 -uroot -proot --authenticationDatabase admin -d test -c haha /home/mongod/test/haha/haha.bson
    #从/home/mongod/test/haha目录下恢复集合的数据到本地27300端口中root用户的test数据库的haha集合中
    
    
  • –drop 参数实践恢复

    # 恢复单库
    mongorestore -h 127.0.0.1:27017 -uroot -proot --authenticationDatabase admin -d test --drop /home/mongod/test
    # 恢复单表
    mongorestore -h 127.0.0.1:27017 -uroot -proot --authenticationDatabase admin -d test -c vast --drop /home/mongod/test/haha/haha.bson
    
  • 集合备份文件后缀 .bson
  • 压缩备份文件后缀**.gz**
  • 例:压缩备份student集合文件:student.bson.gz

7-1 MongoDB 复制集 &分片

1.1 MongoDB 架构

1.1.1 复制集

复制集对于新手来说不是一个好理解的概念,那么我们就从为什么要使用复制集来理解它吧。

为什么使用复制集:

  • 备份数据
    数据库的数据只有一份的话是极不安全的,一旦数据所在的电脑坏掉,我们的数据就彻底丢失了,所以要有一个备份数据的机制。

  • 故障自动转移
    部署了复制集,当主节点挂了后,集群会自动投票再从节点中选举出一个新的主节点,继续提供服务。而且这一切都是自动完成的,对运维人员和开发人员是透明的。当然,发生故障了还是得人工及时处理,不要过度依赖复制集,万一都挂了,那就连喘息的时间都没有了。

  • 在某些特定的场景下提高读性能
    默认情况下,读和写都只能在主节点上进行。
    下面是 MongoDB 的客户端支持5种复制集读选项:

  • primary :默认模式,所有的读操作都在复制集的主节点进行的;

  • primaryPreferred :在大多数情况时,读操作在主节点上进行,但是如果主节点不可用了,读操作就会转移到从节点上执行;

  • secondary :所有的读操作都在复制集的从节点上执行;

  • secondaryPreferred :在大多数情况下,读操作都是在从节点上进行的,但是当从节点不可用了,读操作会转移到主节点上进行;

  • nearest :读操作会在复制集中网络延时最小的节点上进行,与节点类型无关。

复制集结构,如图所示:
预览大图

1.1.2 分片

**分片(sharding)**是指将数据库拆分,使其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。通过一个名为 mongos 的路由进程进行操作,mongos 知道数据和片的对应关系(通过配置服务器)。

什么时候使用分片:

  • 机器的磁盘不够用了,使用分片解决磁盘空间的问题;
  • 单个 mongod 已经不能满足写数据的性能要求,通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源;
  • 想把大量数据放到内存里提高性能,通过分片使用分片服务器自身的资源。

分片结构,如图所示:
预览大图

上图中主要有如下所述三个主要组件:

  • Shard :用于存储实际的数据块,实际生产环境中一个 shard server 角色可由几台机器组个一个 replica set 承担,防止主机单点故障;

  • Config Server :mongod 实例,存储了整个 ClusterMetadata,其中包括 chunk 信息;

  • Query Routers :前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。

1.2 MongoDB 复制集搭建

我们在一台虚拟机中启动三个 mongodb 服务,来模拟多台服务器协同工作的情况。

选用三个端口连接数据库:27018、27019、27020。

1.2.1 配置文件设置

因为我们要启动三个 mongodb 服务,所以要准备三个数据存放位置,三个日志文件,三个配置文件。

  • 数据存放位置

    在 /data 路径下创建文件夹 db1、db2 和 db3 来存放三个服务的数据。

  • 日志文件

    在 /logs 路径下创建文件夹 mongo 存放日志文件 mongod1.log、mongod2.log 和 mongod3.log(文件不用创建,到时候会自动生成,但路径即文件夹必须提前创建好)。

  • 配置文件

    在 /etc/mongod 路径下新建三个配置文件,使用配置文件启动 mongod 服务(在之前的实训中我们都是用命令启动的)。

    • mongod1.conf 内容如下:

      port=27018     #配置端口号
      dbpath=/data/db1     #配置数据存放的位置
      logpath=/logs/mongo/mongod1.log     #配置日志存放的位置
      logappend=true     #日志使用追加的方式
      fork=true     #设置在后台运行
      replSet=YOURMONGO     #配置复制集名称,该名称要在所有的服务器一致
      
    • mongod2.conf 内容如下:

      port=27019     #配置端口号
      dbpath=/data/db2     #配置数据存放的位置
      logpath=/logs/mongo/mongod2.log     #配置日志存放的位置
      logappend=true     #日志使用追加的方式
      fork=true     #设置在后台运行
      replSet=YOURMONGO     #配置复制集名称,该名称要在所有的服务器一致
      
    • mongod3.conf 内容如下:

      port=27020     #配置端口号
      dbpath=/data/db3     #配置数据存放的位置
      logpath=/logs/mongo/mongod3.log     #配置日志存放的位置
      logappend=true     #日志使用追加的方式
      fork=true     #设置在后台运行
      replSet=YOURMONGO     #配置复制集名称,该名称要在所有的服务器一致
      

以上工作准备完成,便可以开启服务了,配置文件启动命令(在命令行中输入):

mongod -f /etc/mongod/mongod1.conf
mongod -f /etc/mongod/mongod2.conf
mongod -f /etc/mongod/mongod3.conf

具体效果如图所示:
预览大图

1.2.2 配置主从节点

三个端口的服务全部启动成功后,需要进入其中一个进行配置节点。

设置27019为 arbiter 节点。

  • 进入端口号为27018的进行配置,连接数据库:mongo --port 27018

  • 选择数据库 admin

  • 输入配置要求如下:

    config = {
       _id:"YOURMONGO",
       members:[
           {_id:0,host:'127.0.0.1:27018'},
           {_id:1,host:'127.0.0.1:27019',arbiterOnly:true},
           {_id:2,host:'127.0.0.1:27020'},
       ]
    }
    
  • 使用 rs.initiate(config)进行初始化:

    rs.initiate(config)
    

具体效果如图所示:
预览大图

如上所示配置成功后,不同端口的数据库登陆后左边的>可能变为如下两种状态:

  • 说明该端口为主节点,如图所示:
    预览大图
  • 说明该端口为从节点,如图所示:
    预览大图
  • 使用 rs.status() 查看状态。

1.2.3 验证复制集同步

在主数据库插入数据,然后去从数据库查看数据是否一致。

连接主数据库mongo --port 27018

use test
db.person.insert({name:'王小明',age:20})

具体步骤如图所示:
预览大图

连接从数据库mongo --port 27020

从库查询数据需要设置 slaveOk true

use test
rs.slaveOk(true)
db.person.find()

具体步骤如图所示:
预览大图

至此复制集搭建完成,主数据库的数据的更新后,从数据库的数据也会同步更改,一旦主数据库挂掉,从数据库可自动变为主数据库,极大地保障了数据的安全,这也是搭建复制集的必要性。

1.2.4 切换 Primary 节点到指定的节点

在实际应用中,如果想指定某服务器或端口作为主节点,而不是随机选举一个主节点,可以通过以下方法改变 Primary 节点:

  • 先进入主节点中进行操作:

    mongo --port 27018
    
  • 查看目前的节点状态:

    rs.conf()     #查看配置
    rs.status()   #查看状态
    

    其中 priority : 是优先级,默认为 1,优先级 0 为被动节点,不能成为活跃节点。优先级不为 0 则按照由大到小选出活跃节点。

因为默认的都是1,所以只需要把给定的服务器的 priority 加到最大即可。让27020成为主节点,操作如下:
现进入目前的主节点进行操作如下:

cfg=rs.conf()
cfg.members[2].priority=2     #修改priority,members[2]即对应27020端口
rs.reconfig(cfg)     #重新加载配置文件,强制了副本集进行一次选举,优先级高的成为Primary。在这之间整个集群的所有节点都是secondary
rs.status()

这样,给定的服务器或端口就成为了主节点。

1.3 MongoDB 分片集搭建

1.3.1 配置文件设置

  • 同复制集一样,我们要准备目录存放我们的数据和日志:

    mkdir -p /data/shard1/db
    mkdir -p /logs/shard1/log
    mkdir -p /data/shard2/db
    mkdir -p /logs/shard2/log
    mkdir -p /data/shard3/db
    mkdir -p /logs/shard3/log
    mkdir -p /data/config/db
    mkdir -p /logs/config/log
    mkdir -p /logs/mongs/log
    
  • 配置文件 (新建在 /etc/mongo 目录下);

    • mongod1.conf 内容如下:

      dbpath=/data/shard1/db
      logpath=/logs/shard1/log/mongodb.log
      port=10001
      shardsvr=true
      fork=true
      
    • mongod2.conf 内容如下:

      dbpath=/data/shard2/db
      logpath=/logs/shard2/log/mongodb.log
      port=10002
      shardsvr=true
      fork=true
      
    • mongod3.conf 内容如下:

      dbpath=/data/shard3/db
      logpath=/logs/shard3/log/mongodb.log
      port=10003
      shardsvr=true
      fork=true
      

      其中 shardsvr 是用来开启分片的。

  • 从配置文件启动 mongod 服务

    mongod -f /etc/mongo/mongod1.conf
    mongod -f /etc/mongo/mongod2.conf
    mongod -f /etc/mongo/mongod3.conf
    

1.3.2 config 节点

  • 配置启动节点服务:

    mongod --dbpath /data/config/db --logpath /logs/config/log/mongodb.log --port 10004 --configsvr --replSet cs --fork
    
  • 连接 route 节点:

    mongo localhost:10004
    
  • 输入以下命令:

    use admin
    cfg = {
       _id:'cs',
       configsvr:true,
       members:[
           {_id:0,host:'localhost:10004'}
        ]
    }
    
    rs.initiate(cfg)
    

    运行效果如图所示,说明设置成功:
    预览大图

1.3.3 route 节点

  • 配置启动节点服务:

    mongos --configdb cs/localhost:10004 --logpath /logs/mongs/log/mongodb.log --port 10005 --fork
    
  • 连接上 route 节点:

    mongo localhost:10005
    
  • 添加分片:

    sh.addShard('localhost:10001')
    sh.addShard('localhost:10002')
    sh.addShard('localhost:10003')
    
  • 查看集群的状态:分片摘要信息、数据库摘要信息、集合摘要信息等;

    sh.status()
    

    运行效果如图所示,说明设置成功:
    预览大图

1.3.4 分片验证

  • 连接 route 节点:mongo localhost:10005

    数据量太小可能导致分片失败,这是因为 chunksize 默认的大小是 64MB( chunkSize 来制定块的大小,单位是 MB ),使用以下代码把 chunksize 改为 1MB 后,插入数据,便可以分片成功。

    use config
    db.settings.save( { _id:"chunksize", value: 1 } )
    
  • 对集合使用的数据库启用分片

    sh.enableSharding("test")
    
  • 添加索引:

    db.user.ensureIndex({ "uid" : 1})
    
  • 分片:

    sh.shardCollection("test.user",{"uid" : 1})
    
  • 插入10万条数据(大概需要40s 左右):

    use test
    for(i=0;i<100000;i++){db.user.insert({uid:i,username:'test-'+i})}
    
  • 再次运行 sh.status() 会多出如图 3 内容,显示了分片的情况。
    预览大图

  • 去各个节点查看数据分布情况

    以下分别是在端口10001(图4)、10002(图5)和10003(图6)查询的文档条数,加起来正好10万条。
    预览大图
    预览大图
    预览大图

标签:知识点,NoSQL,--,MongoDB,数据库,db,查询,索引,文档
From: https://blog.csdn.net/TluoshangY/article/details/143359856

相关文章

  • javascript 基本语法,变量,运算符【知识点整理】
    JavaScript(ES5)JavaScript的基本语法和变量变量声明与变量赋值的方法:vara=5;vara=5;varb=4;vara=3,b=2;vara,b,c=5;vara=b=c=1;变量的命名规范首字符:英文和下划线组成:英文数字下划线禁忌:关键字、保留字##Unicode在HTML中,Unicode字符......
  • shodan(五)连接Mongodb数据库&Jenkins&org、net、查看waf命令
    声明:学习素材来自b站up【泷羽Sec】,侵删,若阅读过程中有相关方面的不足,还请指正,本文只做相关技术分享,切莫从事违法等相关行为,本人一律不承担一切后果引言:     1.Shodan是一个专门用于搜索连接到互联网的设备的搜索引擎。与传统搜索引擎(如Google)不同,Shodan的焦点不......
  • datax抽取mongodb数据到hive
    {"job":{"setting":{"speed":{"channel":3},"errorLimit":{"record":0,"percentage":0.02}},"content":[{"reader":{"name":"mongodbrea......
  • ETLCloud遇上MongoDB:灵活数据流,轻松管理
    在当今数字化转型的大潮中,企业面临着数据管理的多重挑战。数据种类繁多、格式各异,传统关系型数据库往往难以满足这些复杂的需求。为了提升数据管理的灵活性与效率,越来越多的企业开始选择NoSQL数据库。而在众多NoSQL数据库中,MongoDB凭借其灵活的数据模型和强大的扩展性成为了佼佼者......
  • SSM 框架重要知识点解析
    在JavaWeb开发领域,SSM框架(Spring+SpringMVC+MyBatis)是一套备受青睐的轻量级企业级开发框架组合,它为我们提供了高效、便捷且可维护的开发方式。接下来,让我们深入探讨一下SSM框架中的重要知识点。一、Spring框架核心要点1.IOC(控制反转)与DI(依赖注入)概念解析I......
  • MongoDB关联另一个集合
    MongoDB本身并不支持传统关系数据库中的外键(foreignkey)概念,因为它是一个文档数据库,数据通常是以JSON格式存储的文档,并且不强制要求文档之间的关系。然而,你可以通过以下几种方式在MongoDB中实现类似外键的功能:1.引用(References)你可以在一个文档中存储另一个文档的ID,从而......
  • vue3知识点:reactive对比ref
    @目录二、常用CompositionAPI5.reactive对比ref本人其他相关文章链接二、常用CompositionAPI问题:啥叫“组合式API”?答案:请看官方文档:https://v3.cn.vuejs.org/guide/composition-api-introduction.html5.reactive对比ref从定义数据角度对比:ref用来定义:基本类型......
  • 从零到精通大模型!超详细入门知识点汇总,一篇在手,学习无忧!
    采用提问方式,从个人知识盲点开始,渐进式掌握大模型入门知识点。‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍1、大模型中7b、70B代表什么在讨论人工智能领域特别是大型语言模型(LLMs)时,“7b”和“70B”均用来表示模型所包含的参数数量。这里的“b”是英文“Billion”......
  • MongoDB的ORM 库
    MongoDB的ORM(对象关系映射)库主要有几个常用的选择,以下是一些比较流行的库及其特点:MongoEngine:(360)基于文档的ORM,允许使用Python类来定义MongoDB文档结构。提供了查询构建器,支持丰富的查询语法。适合于快速构建小型到中型应用。示例:frommongoengineimportDocument,......
  • 【解决MongoDB安装难题!】计算机丢失VCRUNTIME140D.dll?一招教你快速修复!
    在安装或运行Java的MongoDB相关程序时,如果遇到“无法启动此程序,因为计算机丢失VCRUNTIME140D.dll”的错误,通常是由于缺少MicrosoftVisualC++Redistributable包。VCRUNTIME140D.dll是VisualC++2015-2019Redistributable的一部分,用于支持C++应用程序的运行时库。以下是解......