解开MongoDB神秘的面纱

Posted 毛奇志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解开MongoDB神秘的面纱相关的知识,希望对你有一定的参考价值。

目录

一、前言

二、Mongodb基础知识

2.1 Mongodb数据逻辑结构

2.2 Mongodb数据存储结构

2.3 Mongodb日志系统

2.4 Mongodb元数据存储

2.5 Mongodb数据类型

三、Mongodb基本操作

3.1 mongodb安装与启动(windows+Linux)

3.2 Mongodb基本操作

3.2.1 数据库级别操作(新建数据库、查看数据库、删除数据库)

3.2.2 集合级别操作(新建集合、查看集合、删除集合)

3.2.3 文档级别操作(文档CURD)

四、Mongodb高级操作

4.1 高级查询(单机版Mongodb)

4.1.1 条件查询

4.1.2 $type 操作符——类型限制

4.1.3 sort()方法——排序

4.1.4 aggregate()方法——聚合

4.1.5 文档连接(类似于关系型数据库的表连接)

4.2 高级更新

4.3 高级特性

4.3.1 Capped Collection——固定集合

4.3.2 GridFS——存储静态资源文件,fs.files文件和fs.chunks文件

4.3.3 MapReduce

五、小结


一、前言

引子:什么是Mongdb?Mongdb引入

Mongodb 是一个基于分布式文件存储的数据库,它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。Mongodb由 C++ 语言编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。根据官方文档记载,当数据量达到50GB以上的时候,Mongdb的数据访问速度是mysql的10倍,Mongodb还自带一个出色的分布式文件系统GridFS,可以支持海量数据存储。

本文分为三个部分,分别为:Mongodb基础知识、Mongodb基本操作、Mongodb高级操作,即

Mongodb基础知识包括:数据逻辑结构、数据存储结构、日志系统、元数据存储、数据类型;

Mongodb基本操作包括:mongodb安装与启动、数据库级别操作、集合级别操作、文档级别操作;

Mongodb高级操作包括:高级查询、高级更新、Capped Collection、GridFS、MapReduce。

注意,关于SQL NoSQL 关系型数据库  非关系型数据库等概念及其区别,笔者在一篇关于redis的博文的前言部分介绍过,这里不再赘余,该redis博文链接为:https://blog.csdn.net/qq_36963950/article/details/103359376

只要知道,“Mongodb是一种使用文档来存储数据的非关系型数据库”就好了。

二、Mongodb基础知识

Mongodb是一个可移植的数据库,在主流的操作系统上都可以使用,即跨平台的特性。虽然在不同平台上有细微差别,但从整体结构上,Mongdb在不同平台上的是一致的,如数据逻辑结构和数据存储方式等。

MongodbShell是Mongodb自带的交互式javascript Shell,是一个用来对Mongodb执行操作和管理的交互环境。

所有有关Mongodb的操作,使用"./mongo help"可查看相关参数与说明。

一个运行着的Mongodb数据库可以看做是一个Mongodb Server,该Server由实例和数据库组成,一般情况下,一个Mongodb Server机器上包含一个实例和多个与之对应的数据库。

Mongodb中一系列物理文件(数据文件、日志文件等)的集合或与之对应的逻辑结构(集合、文档等)称为数据库,即数据库是由一系列与磁盘有关的物理文件组成的。

2.1 Mongodb数据逻辑结构

Mongodb的数据逻辑结构是面向用户的,即用户使用Mongodb开发应用程序使用的就是逻辑结构。Mongodb逻辑结构是一种层次结构,由文档document 集合collection 数据库databases 组成。

文档document 集合collection 数据库databases三者的关系是:

对于初学者来说,对关系型数据库比较熟悉,所以这里从关系型数据库理解Mongdb的结构,如下表:

关系型数据库Mongodb
数据库 database数据库 database
表 table集合 collection
行 row文档 document
列 column字段/域 field
索引 index索引 index
表连接 table joins无(Mongodb不支持表连接,无相关对应项)
主键 primary key主键 primary key(MongoDB自动将_id字段设置为主键)

给出实例:

2.2 Mongodb数据存储结构

Mongodb存储数据的默认目录是/data/db,这个目录的负责存储所有mongodb的数据文件,在MongoDB内部,每一个数据库包含一个ns文件和一些数据文件,这些数据文件会随着数据的增多而越来越多。

MongoDB内部有预分配空间的机制,每一个预分配的文件都用0进行填充,这就使Mongodb始终保持空余的空间和额外的数据文件,有效避免了由于数据暴增而带来磁盘压力过大的问题。如果想取消预分配,可以在mongodb启动时,加上参数“--noprealloc”,这样,系统的预分配机制就被取消了。

由于表中数据量的增加,数据文件每分配一次,它的大小都是上一个数据文件的2倍,每个数据文件最大为2GB,这样的机制有利于防止小的数据库浪费磁盘空间,同时又保证大的数据库有相应的预留空间使用。

数据库的每一个张表都对应一个命名空间,每一个索引都有相应的命令空间,这些命名空间的元数据都集中在“*.ns”文件中。

命名空间与盘区的关系:每个命令空间可以包含多个不同的盘区,这些盘区并不是连续的,与数据文件的增长不同,每一个命名空间对应的盘区大小也是随着分配的次数而增长,这样做的目的不仅是为了平衡命名空间浪费的磁盘空间,也是为了保持某个命名空间的数据的连续性。

关于$freelist,这个命名空间用于记录不再使用的盘区(即被删除的collection或索引),每当命名空间需要分配新的盘区的时候,都会先查看$freelist是否有大小合适的盘区可以使用,这样就能回收空间的磁盘空间。

2.3 Mongodb日志系统

Mongodb中包含四种日志:系统日志、Journal日志、oplog主从日志、慢查询日志,这些日志记录着Mongodb数据库不同方面的运行信息。

(1)系统日志

系统日志在mongodb中很重要,记录着mongodb启动和停止的操作,以及服务器在运行过程中发生的任何异常信息。

如何使用系统日志?配置系统日志,只需要在启动mongod时指定一个logpath参数即可,例如:

mongod -logpath=/data/db/log/serverlog.log  -logappend

(2)Journal日志

Journal日志通过预写式的redo日志为mongodb增加了额外的可靠性保障,开启该功能时,数据的更新会先写入Journal日志,定期集中提交,然后在真实的数据上执行这些变更。如果服务器安全关闭,日志会被清除。在服务器启动时,如果存在Journal日志,则会执行提交。这保证了那些已经写入Journal日志但在服务器崩溃前还没有提交的操作能在用户连接服务器前被执行,两次提交之间的时间间隔,通过不断缩小以最大的程度上实现实时提交。

如何使用Journal日志?启动数据库的Journal功能非常简单,只需在mongod后面指定journal参数即可,例如:

mongod -journal

这样,系统的Journal信息都会放到数据库目录(默认是/data/db/)的journal文件夹中。

(3)oplog主从日志

Mongodb的高可用复制策略中有一种叫做Replica Sets。Replica Sets复制过程中一个服务器充当主服务器,而一个或多个服务器充当从服务器,主服务器将更新写入一个本地的collection中,这个collection记录着发生在主服务器的更新操作,并将这些操作分发到从服务器上。

这个日志是一个Capped Collection,且有大小之分,所以最好在mongod启动时配置好大小,例如:

mongod - oplogsize=1024

(4)慢查询日志

慢查询日志记录了执行时间超过所设定时间阈值的操作语句,慢查询日志对于发现性能有问题的语句很有帮助,建议开启此功能并经常分析该日志的内容。

要想配置这个功能只需要在mongod启动时指定profile参数即可。例如,想要超过5秒的操作都记录,可以使用如下语句:

mongod --profile=1 --slowns=5

系统运行一段时间后,可以通过查看db.system.profile这个collection来获取慢日志信息。

2.4 Mongodb元数据存储

元数据是一个预留空间,在对数据库或应用程序结构执行修改时,其内容可以由数据库自动更新。元数据是系统中各类数据描述的集合,是执行详细的数据收集与数据分析的主要途径。

元数据最重要的作用是作为一个分析阶段的工具,任何字典最重要的操作是查询,在结构化分析中,元数据的作用是给每一个结点加上定义与说明。换句话说,数据流图上所有节点的定义和解释的集合就是元数据,而且在元数据中建立严密一致的定义有助于提高需求分析人员和用户沟通的效率。

Mongodb和其他数据库一样,有专门存储元数据的系统表,<dbname>.system.*.namespaces是一个特殊的对象,其中存储数据库的系统信息,通过这些表用户可以大概了解系统中数据库对象的情况,如:

system.namespaces存储命名空间信息
system.indexes存储索引信息
system.profile存储优化器信息
system.users存储用户信息
local.sources存储复制集的配置信息和状态信息

其他的命令空间和索引信息都存储在database.ns文件中,并且对用户是封装的。

‘$’是一个保留字符,不要在命名空间和列名中使用它。

2.5 Mongodb数据类型

Mongodb中的文档可以理解为JSON类型的对象,JSON格式简单,仅支持6种数据类型,但是Mongdb再采用了JSON键值对的同时,还支持很多额外的数据结构,虽然不同的语言支持的类型有所不同,但是还是很多共同的类型,如下表:

Mongodb支持的数据类型
Mongodb数据类型描述/说明/解释形式
Null表示空值或不存在的值"x":null
Booleantrue|false"x":true
String任意UTF-8字符"x":"mystring"
SymbolShell不支持,会转换为string"x":"foobar"
object id保证唯一性的12位ID"x":ObjectId()
date表示从纪元开始到现在的毫秒数,忽略时区"x":new Date()
regular expression采用JavaScript的正则表达式语法"x":/foobar/i
code可以包含JavaScript代码"x":function()/*...*/
array列表"x":["a","b","c"]
embedded document嵌套的文档"x":"foo","bar"

 接下来介绍Mongodb的几种重要的数据类型

(1)number数字

JavaScript有一个number类型,对应的是Mongodb有3个数据类型(4字节整数、8字节整数、8字节浮点数)。Shell中的数字默认为浮点数。

注意,如果从数据库取出一个4字节整数存进去,即使没有改变它,也会变成8字节浮点数。因此,建议不要把整个document插入到mongodb中。但是,一个8字节的整数是不能用8字节的浮点数来表示的。因此,如果保存一个8字节整数,要在Shell里查看它,Shell将会显示为一个嵌套的document来精确地描述它。在shell中查看代码如下:

> doc = db.num.findOne()

"_id":ObjectId("4c0beecfd096a2580fe6fa08"),
"myInteger":"floatApprox":3

这个数字在数据库里不会改变,这个嵌套的文档只是用shell显示一个8字节整数的浮点近似值,并且只有一个key。

如果插入一个不能用浮点数来精确表示的8字节整数,shell会加上2个key:top和bottom,分别表示高位和低位的32位值。

(2)date日期

JavaScript的数据类型中,Date对象用来表示mongodb的日期类型,创建日期对象要用new Date(),而不是Date(),后者只是返回一个字符串,并不是一个Date对象,如下:

> Date()
Fri Feb 14 2020 22:42:48 GMT+0800
> new Date()
ISODate("2020-02-14T14:42:52.226Z")
> typeof(new Date())
object
> typeof(Date())
string
>

这不是一个Mongodb的问题,因为JavaScript就是这样处理的,如果不注意混淆字符串和日期类型,在删除、更新查询时造成问题。

Shell里用本地区域设置显示日期,但在数据库里只有记录从纪元开始到现在所过去的毫秒数,所以并没有把区域设置保存进去。

(3)array数组

数组中的值既可以是有序的,如列表、堆栈、队列;也可以是无序的,如集合。

"things":"pie",3.14

这里看到,数组成员类型可以是不一样的,可以是任意类型,包括数组嵌套数组。

Mongodb的一个好处是,它理解数组里的结构并知道怎样进入内部执行操作,所以可以执行查询操作和索引操作。

(4)embeded document嵌套文档

嵌套文档就是将一个key的值表示为另一个文档,这样可以让document看起来比一个平面的结构更自然。


"name":"mary",
"address":
   "street":"Beijing Park Street",
   "city":"beijing",
   "country":"China"

和数组一样,Mongodb也理解嵌套文档的结构,可以执行内部的查询与更新。

嵌套文档改变了处理数据的方式,传统数据库中两个信息会存储在两个表的两个记录中,但Mongodb可用一个文档表示。从另一个方面来说,这种方式不是很正规,会造成数据冗余。

三、Mongodb基本操作

3.1 mongodb安装与启动(windows+Linux)

windows上安装

下载msi无脑next,就按照安装成功了,管理员权限打开命令行,切换到mongodb根目录/bin,输入mongo启动,如下:

linux上安装

下载压缩包解压,然后

配置环境变量:export PATH=<mongodb-install-directory>/bin:$PATH

新建数据库目录:mkdir -p /data/db

启动mongod服务并指定数据库目录:$ ./mongod --dbpath=/data/db

连接mongo:$ cd mongodb根目录/bin(例如/usr/local/mongodb/bin) $ ./mongo

下图启动成功:

3.2 Mongodb基本操作

3.2.1 数据库级别操作(新建数据库、查看数据库、删除数据库)

一图看懂:

3.2.2 集合级别操作(新建集合、查看集合、删除集合)

db.createCollection(name,options)    mongodb中当前数据库下新建集合

name是必填参数,options是可选参数

字段类型属性
capped布尔类型

该字段可选,如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为 true 时,必须指定 size 参数。默认为false。

下面Capped Collection会具体介绍。

autoIndexId布尔类型该字段可选,如为 true,自动在 _id 字段创建索引。默认为 false。
size数值类型该字段可选,为固定集合指定一个最大值,以千字节计(KB)。如果 capped 为 true,也需要指定该字段。
max数值类型该字段可选,指定固定集合中包含文档的最大数量。

一图搞懂:

注意:在 MongoDB 中,你可以不创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

3.2.3 文档级别操作(文档CURD)

插入文档——insert()方法

MongoDB 使用 insert() 方法向集合中插入文档,语法如下:

更新文档——update()方法和save()方法

db.collection.update(
   <query>,
   <update>,
   
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   
)
update()方法参数说明
queryupdate的查询条件,类似sql update查询内where后面的。
updateupdate的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
upsert可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi

可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。

下面的截图中mongodb没有额外设置,所以默认是false,只修改找到的符合条件的第一条记录。

writeConcern可选,抛出异常的级别。

 实际上,文档更新也可以使用save()方法进行,且看下面。

删除文档——remove()方法

db.collection.remove(
   <query>,
   
     justOne: <boolean>,
     writeConcern: <document>
   
)
remove()方法参数说明
query(可选)删除的文档的条件。
justOne(可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
writeConcern(可选)抛出异常的级别。

查找文档——find()方法

db.col.find(query,projection)

如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:

db.col.find().pretty()
find()方法参数说明
query可选,使用查询操作符指定查询条件
projection可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。

四、Mongodb高级操作

4.1 高级查询(单机版Mongodb)

4.1.1 条件查询

条件查询示例
(>) 大于 - $gtage:$gt:14  年龄大于14,如下图
(<) 小于 - $ltage:$gte:15  年龄大于等于15,如下图
(>=) 大于等于 - $gteage:$lt:15  年龄小于15,如下图
(<= ) 小于等于 - $lteage:$lte:15  年龄小于等于15,如下图

4.1.2 $type 操作符——类型限制

$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。

4.1.3 sort()方法——排序

在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

4.1.4 aggregate()方法——聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。

4.1.5 文档连接(类似于关系型数据库的表连接)

MongoDB 的关系表示多个文档之间在逻辑上的相互联系。MongoDB 中的关系可以是:1:1 (1对1)、1: N (1对多)、N: 1 (多对1)、N: N (多对多);实际上,由于Mongodb本身的特点,不建议使用多collection关联处理,但是如果有业务需求必须进行关联处理,Mongodb提供中两种方法应付这类需求——嵌入与引用。

嵌入:如果用户和用户地址在不断增加,数据量不断变大,会影响读写性能,不好,这里略过。主要看引用关系:

引用:手动引用(Manual References)+ DBRefs

手动引用(Manual References)  col集合和course集合之间没有建立引用关系

DBRefs   col集合和course集合之间建立了引用关系,DBRef就是一个文档的一个属性指向另一个文档的指针。

DBRef就是两个collection集合之间定义的一个关联,比如,把collectionB “_id”列的值存放在collectionA的一个列中,然后通过collectionA这个列所存的值collectionB找到相应的记录。如下:

4.2 高级更新

对于文档的更新操作,之前讲过update()和save()操作,那两个是最常用的,这里在加上一些额外的:$inc、$set、$unset、$push、$addToSet、$pop,且看下面。

(1)$inc

格式:

$inc:field:value

含义:$inc对一个数字字段的某个field增加value

示例: 

(2)$set

格式:

$set:field:value

含义:$set相当于SQL语法的“set field=value”,全部数据类型都支持$set

示例:

(3)$unset

格式:

$unset:field:1

含义:$unset用于删除字段

示例:

(4)$push

格式:

$push:field:value

含义:$push把value追加到field中,field一定是数组类型,如果field不存在,会新增一个数组类型加进去

示例:

(5)$addToSet

格式:

$addToSet:field:value

含义:$addToSet用来加一个值到数组内,而且只有当这个值不在数组内才增加。

示例:

(6)$pop

格式:

删除第一个值
$pop:field:-1
删除最后一个值
$pop:field:1

含义:$pop用于删除数组内的一个值

示例:

pop操作符一次只能删除一个值,只能用-1和1表示,不能使用-2和2,-1表示删除数组第一个元素,1表示删除数组最后一个元素。

小结:4.2 展示了一些Mongodb的更新操作的相关的属性值,没有完全展示完全,因为太占用篇幅了,其他也都是大同小异的,对于开发者来说,即使第一次使用,也可以一看就懂,这里略去。

4.3 高级特性

4.3.1 Capped Collection——固定集合

Capped Collection是性能出色的固定大小的集合,以LRU(Least Recently Used,最近最少使用)规则和插入顺序执行age-out(老化移出)处理,自动维护集合中对象的插入顺序。

新建Capped Collection与转换Capped Collection

在创建时要预先指定的大小,如果空间用完,新添加的对象将会取代集合中最旧的对象(这就是LRU,最近最少使用)。更新如果超过collection的大小,更新失败。虽然不允许删除,但是可以调用drop()删除集合中的所有行,drop()后需要显式地重建集合。

如果新建一个非capped集合,可以使用convertToCapped()方法转换为Capped Collection,如图:

Capped Collection的用途(从属性到用途)

Capped Collection是Mongodb中日志机制的首选,Mongodb没有使用日志文件,而是将日志事件存储在数据库中。在一个没有索引的Capped Collection中插入对象的速度与文件系统中记录日志的速度相当。但是,如果在内存中缓存一些对象(比如,计算出来的统计信息一般需要在collection上建立一个索引,因为使用缓存往往是读操作比写操作多)可以利用Capped Collection的age-out特性,省去写crontab脚本执行人工归档的工作。

Capped Collection有几个属性:

属性1:对固定集合进行插入速度极快

属性2:按照插入顺序的查询输出速度极快

属性3:能够在插入最新数据时,淘汰最早的数据

根据这些属性得到Capped Collection的相关用途:

用法1:储存日志信息

用法2:缓存一些少量的文档

4.3.2 GridFS——存储静态资源文件,fs.files文件和fs.chunks文件

问题引入:关系型数据库中,对于图片、音视频这样的静态资源,我们存放在磁盘相关目录下,在数据库字段中存放string路径,那么,mongodb中如何处理静态资源呢?答案是GridFS,且看下面。

GridFS是一种将大型文件存储到Mongodb数据库中的文件规范,所有官方支持的驱动均实现了GridFS规范。GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。GridFS 可以更好的存储大于16M的文件。GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。GridFS 用两个集合来存储一个文件:fs.files与fs.chunks:

files:存储与实际文件相关的元数据信息(meta数据信息,即filename,content_type,还有用户自定义的属性);

chunks:用来存储二进制数据,存储每个文件的实际内容(实际内容分割到各个chunk中),及其他一些相关的二进制块

fs.files文件

使用以下命令来查看fs.files中的内容:

>db.fs.files.find()

以下是简单的 fs.files 集合文档:


   "filename": "test.txt",
   "chunkSize": NumberInt(261120),
   "uploadDate": ISODate("2020-02-14T11:32:33.557Z"),
   "md5": "7b762939321e146569b07f72c62cca4f",
   "length": NumberInt(646)
fs.files文档中字段说明
filename存储的文件名
chunkSizechunks的大小
uploadDate入库时间
md5文件的md5
length文件大小,单位:字节

fs.chunks文件

使用以下命令来查看fs.chunks中的内容:

db.fs.chunks.find()

以下是简单的 fs.chunks 集合文档:


   "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
   "n": NumberInt(0),
   "data": "Mongo Binary Data"

4.3.3 MapReduce

五、小结

本文分为三个部分,分别介绍了Mongodb基础知识、Mongodb基本操作、Mongodb高级操作。

Mongodb基础知识包括:数据逻辑结构、数据存储结构、日志系统、元数据存储、数据类型;

Mongodb基本操作包括:mongodb安装与启动、数据库级别操作、集合级别操作、文档级别操作;

Mongodb高级操作包括:高级查询、高级更新、Capped Collection、GridFS、MapReduce。

天天打码,天天进步!

以上是关于解开MongoDB神秘的面纱的主要内容,如果未能解决你的问题,请参考以下文章

解开Future的神秘面纱之任务执行

解开Kafka神秘的面纱:kafka优雅应用

解开Future的神秘面纱之取消任务

解开SQL注入的神秘面纱-来自于宋沄剑的分享

解开Kafka神秘的面纱

解开Kafka神秘的面纱:kafka stream及interceptor