MongoDB初步使用、视图和上限(固定)集合

注:本地学习基于MongoDB v4.2版本。

一、MongoDB介绍

MongoDB是一个文档数据库,它是为方便开发和扩展而设计的。

MongoDB提供了社区版和企业版本,相较于社区版,MongoDB企业版增加了面向企业的特性,如LDAP和Kerberos支持、磁盘加密和审计。

 

MongoDB属于文档数据库。MongoDB中的记录是一个文档,是一个由字段和值对组成的数据结构。MongoDB文档类似于JSON对象。字段的值可以包括其他文档、数组和文档数组。

MongoDB初步使用、视图和上限(固定)集合_第1张图片

主要特点:

  • MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
  • 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序。
  • 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
  • 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
  • Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
  • MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
  • Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
  • Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
  • Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
  • GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
  • MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
  • MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
  • MongoDB安装简单。

二、简单的介绍一些入门概念

我们先谈一下使用,然后再介绍安装。

1.连接数据库:

MongoDB初步使用、视图和上限(固定)集合_第2张图片

直接输入“db”,返回默认的数据库“test”。 如果我们想使用其它或创建数据库,直接使用“use exec”,则会创建exec数据库,并自动切换到该数据库。

2.数据库和集合

MongoDB在集合中存储BSON文档,即数据记录。一条记录相当于关系数据库的一条数据,一个集合相当于数据表。

MongoDB初步使用、视图和上限(固定)集合_第3张图片

2.1创建数据库

如果数据库不存在,MongoDB将在您第一次为该数据库存储数据时创建数据库。因此,您可以切换到一个不存在的数据库,并在mongo shell中执行以下操作:

use myNewDB

db.myNewCollection1.insertOne( { x: 1 } )

这个时候,使用show dbs命令,即可看到我们创建的myNewDB数据库。

insertOne()操作创建数据库myNewDB和集合myNewCollection1(如果它们还不存在)。确保数据库和集合名称都遵循MongoDB命名限制。

2.2 集合

MongoDB将文档存储在集合中。集合类似于关系数据库中的表。如果一个集合不存在,MongoDB会在您第一次存储该集合的数据时创建该集合。

MongoDB提供了db.createCollection()方法来显式创建具有各种选项的集合,例如设置最大大小或文档验证规则。如果没有指定这些选项,就不需要显式地创建集合,因为当您第一次为集合存储数据时,MongoDB会创建新的集合。

2.3 文档验证

默认情况下,集合不要求其文档具有相同的模式;也就是说,单个集合中的文档不需要具有相同的字段集,而字段的数据类型在集合中的不同文档之间可能有所不同。

三、视图

MongoDB 3.4引入

本章介绍以下几点:

  • 创建视图
  • 行为
  • 删除一个视图
  • 修改视图
  • 支持的操作

3.1 创建视图
为了创建或定义一个视图,MongoDB 3.4引入:
create命令上的viewOn和pipeline选项(db.createCollection助手):

db.runCommand( { create: , viewOn: , pipeline:  } )

或者指定视图的默认排序规则:

db.runCommand( { create: , viewOn: , pipeline: , collation:  } )

或通过db.createView():

db.createView(, , ,  )

字段    解释
create    视图名称
viewOn    源collection或源视图,指定依赖哪个集合或视图而建的
pipeline    聚合管道,作为聚合管道片段的一部分,参与聚合操作
collation    为view指定一个针对不同语言的排序规则
 

使用db.createView创建视图时,需要制定一个排序规则,不指定的话,创建报错。

3.1.1 查看视图

MongoDB初步使用、视图和上限(固定)集合_第4张图片

3.2 行为

视图表现出以下行为:

3.2.1 只读

     视图是只读;对视图的写操作将出错。
     下列读操作可以支持视图:

  • db.collection.find()
  • db.collection.findOne()
  • db.collection.aggregate()
  • db.collection.countDocuments()
  • db.collection.estimatedDocumentCount()
  • db.collection.count()
  • db.collection.distinct()

3.2.2 索引使用和排序操作

  • 视图使用基础集合的索引。
  • 由于索引位于底层集合上,因此不能直接在视图上创建、删除或重新构建索引,也不能在视图上获得索引列表。
  • 不能在视图上指定$natural排序。

例如,以下操作无效:

db.view.find().sort({$natural: 1})

3.2.3 投影的限制

  视图上的find()操作不支持以下投影操作:

  • $
  • $elemMatch
  • $slice
  • $meta

3.2.4 名字不可变

视图的名字不可以重命名。

3.2.5 视图创建

视图在读取操作期间按需计算,MongoDB作为底层聚合管道的一部分对视图执行读取操作。因此,视图不支持以下操作:

  • db.collection.mapReduce(),
  • $text操作符,由于$text操作在聚合中只对第一阶段有效,
  • $geoNear管道阶段。

如果用于创建视图的聚合管道取消了_id字段,则视图中的文档没有_id字段。

3.2.6 分片视图
如果视图的底层集合是分片的,则认为它们是分片的。因此,您不能在$lookup和$graphLookup操作中为from字段指定切分视图。

3.2.7 视图和排序

  • 可以在创建时为视图指定默认排序规则。如果没有指定排序规则,则视图的默认排序规则是“简单”二进制比较排序器。也就是说,视图不继承集合的默认排序规则。
  • 视图上的字符串比较使用视图的默认排序规则。试图更改或重写视图的默认排序规则的操作将失败并出现错误。
  • 如果从另一个视图创建视图,则不能指定与源视图的排序规则不同的排序规则。
  • 如果执行包含多个视图的聚合,例如使用$lookup或$graphLookup,则视图必须具有相同的排序规则。

3.2.8 公共视图定义
列出集合的操作,例如db.getCollectionInfos()和db.getCollectionNames(),在它们的输出中包括视图。

db.getCollectionInfos():显示该数据库所有的集合和视图信息

MongoDB初步使用、视图和上限(固定)集合_第5张图片

db.getCollectionNames():显示该数据库所有的集合和视图名称。

重要:
视图定义是公共的;例如db.getCollectionInfos()和对视图的解释操作将包括定义视图的管道。因此,要避免直接引用视图定义中的敏感字段和值。

3.3 删除视图

要删除视图,请使用视图上的db.collection.drop()方法。

例如删除视图testView:

MongoDB初步使用、视图和上限(固定)集合_第6张图片

3.4 修改视图
您可以通过删除和重新创建视图或使用collMod命令来修改视图。

3.5 支持的操作
除本页所述的限制外,下列操作提供了对视图的支持:

Commands Methods
create

db.createCollection()

db.createView()

collMod  
 

db.getCollection()

db.getCollectionInfos()

db.getCollectionNames()

find

distinct

count

db.collection.aggregate()

db.collection.find()

db.collection.findOne()

db.collection.countDocuments()

db.collection.estimatedDocumentCount()

db.collection.count()

db.collection.distinct()

四、On-Demand Materialized Views

从4.2版开始,MongoDB为聚合管道添加了$merge阶段。此阶段可以将管道结果合并到现有集合中,而不是完全替换该集合。此功能允许用户创建随需应变的物化视图,其中在每次运行管道时都可以更新输出集合的内容。

举例:

假设在2019年1月底,collection bakesales按项目包含销售信息:

db.bakesales.insertMany( [
   { date: new ISODate("2018-12-01"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
   { date: new ISODate("2018-12-02"), item: "Cake - Peanut Butter", quantity: 5, amount: new NumberDecimal("90") },
   { date: new ISODate("2018-12-02"), item: "Cake - Red Velvet", quantity: 10, amount: new NumberDecimal("200") },
   { date: new ISODate("2018-12-04"), item: "Cookies - Chocolate Chip", quantity: 20, amount: new NumberDecimal("80") },
   { date: new ISODate("2018-12-04"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
   { date: new ISODate("2018-12-05"), item: "Pie - Key Lime", quantity: 3, amount: new NumberDecimal("60") },
   { date: new ISODate("2019-01-25"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
   { date: new ISODate("2019-01-25"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
   { date: new ISODate("2019-01-26"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
   { date: new ISODate("2019-01-26"), item: "Cookies - Chocolate Chip", quantity: 12, amount: new NumberDecimal("48") },
   { date: new ISODate("2019-01-26"), item: "Cake - Carrot", quantity: 2, amount: new NumberDecimal("36") },
   { date: new ISODate("2019-01-26"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
   { date: new ISODate("2019-01-27"), item: "Pie - Chocolate Cream", quantity: 1, amount: new NumberDecimal("20") },
   { date: new ISODate("2019-01-27"), item: "Cake - Peanut Butter", quantity: 5, amount: new NumberDecimal("80") },
   { date: new ISODate("2019-01-27"), item: "Tarts - Apple", quantity: 3, amount: new NumberDecimal("12") },
   { date: new ISODate("2019-01-27"), item: "Cookies - Chocolate Chip", quantity: 12, amount: new NumberDecimal("48") },
   { date: new ISODate("2019-01-27"), item: "Cake - Carrot", quantity: 5, amount: new NumberDecimal("36") },
   { date: new ISODate("2019-01-27"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
   { date: new ISODate("2019-01-28"), item: "Cookies - Chocolate Chip", quantity: 20, amount: new NumberDecimal("80") },
   { date: new ISODate("2019-01-28"), item: "Pie - Key Lime", quantity: 3, amount: new NumberDecimal("60") },
   { date: new ISODate("2019-01-28"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
] );

1. 定义随需应变的物化视图
下面的updateMonthlySales函数定义了一个monthlybakesales物化视图,其中包含累积的月度销售信息。在本例中,该函数接受date参数,仅更新从特定日期开始的每月销售信息。

updateMonthlySales = function(startDate) {
   db.bakesales.aggregate( [
      { $match: { date: { $gte: startDate } } },
      { $group: { _id: { $dateToString: { format: "%Y-%m", date: "$date" } }, sales_quantity: { $sum: "$quantity"}, sales_amount: { $sum: "$amount" } } },
      { $merge: { into: "monthlybakesales", whenMatched: "replace" } }
   ] );
};

$match阶段过滤数据,只处理那些大于或等于startDate的销售。
$group阶段按年月对销售信息进行分组。本阶段输出的文件形式为:

{ "_id" : "", "sales_quantity" : , "sales_amount" :  }

$merge阶段将输出写入monthlybakesales集合。
根据_id字段(未分片输出集合的默认字段),阶段检查聚合结果中的文档是否与集合中的现有文档匹配:

  • 当存在匹配时(即具有相同年份-月份的文档已经存在于集合中),该阶段将使用来自聚合结果的文档替换现有文档。
  • 当不存在匹配时,阶段将来自聚合结果的文档插入到集合中(不匹配时的默认行为)。

2. 执行首次运行
对于初始运行,您可以传入一个新的ISODate日期(“1970-01-01”):

updateMonthlySales(new ISODate("1970-01-01"));

初次运行后,monthlybakesales包含以下文档,即:

db.monthlybakesales.find().sort( { _id: 1 } )返回以下:

{ "_id" : "2018-12", "sales_quantity" : 41, "sales_amount" : NumberDecimal("506") }
{ "_id" : "2019-01", "sales_quantity" : 86, "sales_amount" : NumberDecimal("896") }

3.刷新物化视图
假设在2019年2月的第一周,bakesales集合更新了更新的销售信息;具体来说,是1月和2月的额外销售。

db.bakesales.insertMany( [
   { date: new ISODate("2019-01-28"), item: "Cake - Chocolate", quantity: 3, amount: new NumberDecimal("90") },
   { date: new ISODate("2019-01-28"), item: "Cake - Peanut Butter", quantity: 2, amount: new NumberDecimal("32") },
   { date: new ISODate("2019-01-30"), item: "Cake - Red Velvet", quantity: 1, amount: new NumberDecimal("20") },
   { date: new ISODate("2019-01-30"), item: "Cookies - Chocolate Chip", quantity: 6, amount: new NumberDecimal("24") },
   { date: new ISODate("2019-01-31"), item: "Pie - Key Lime", quantity: 2, amount: new NumberDecimal("40") },
   { date: new ISODate("2019-01-31"), item: "Pie - Banana Cream", quantity: 2, amount: new NumberDecimal("40") },
   { date: new ISODate("2019-02-01"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
   { date: new ISODate("2019-02-01"), item: "Tarts - Apple", quantity: 2, amount: new NumberDecimal("8") },
   { date: new ISODate("2019-02-02"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
   { date: new ISODate("2019-02-02"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
   { date: new ISODate("2019-02-03"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") }
] )

要刷新一月和二月的monthlybakesales数据,再次运行该函数以重新运行聚合管道,从new ISODate(“2019-01-01”)开始。

updateMonthlySales(new ISODate("2019-01-01"));

已更新了monthlybakesales的内容,以反映bakesales集合中的最新数据;即:

db.monthlybakesales.find().sort( { _id: 1 } ) 

返回结果:

{ "_id" : "2018-12", "sales_quantity" : 41, "sales_amount" : NumberDecimal("506") }
{ "_id" : "2019-01", "sales_quantity" : 102, "sales_amount" : NumberDecimal("1142") }
{ "_id" : "2019-02", "sales_quantity" : 15, "sales_amount" : NumberDecimal("284") }

4. 额外的信息
 $merge阶段:

  • 可以输出到相同或不同数据库中的集合。
  • 如果输出集合不存在,则创建新集合。
  • 可以将结果(插入新文档、合并文档、替换文档、保留现有文档、操作失败、使用自定义更新管道处理文档)合并到现有集合中。
  • 可以输出到切分的集合。输入集合也可以分片。

五、上限(固定)集合

本节介绍:

  • 概述
  • 行为
  • 限制和建议
  • 程序

1. 概述

上限集合是固定大小的集合,支持基于插入顺序插入和检索文档的高吞吐量操作。Capped集合的工作方式类似于循环缓冲区:一旦集合填满了分配的空间,它就通过覆盖集合中最老的文档为新文档腾出空间。

提示:
可以考虑使用MongoDB的TTL (Time to Live)索引来替代有上限的集合。正如通过设置TTL从集合中获取过期数据中所描述的,这些索引允许您根据日期类型字段的值和索引的TTL值来过期和从常规集合中删除数据。
TTL索引与上限集合不兼容。

2. 行为

2.1 插入顺序

有限集合保证可插入顺序。因此,查询不需要索引来按插入顺序返回文档。没有这种索引开销,有上限的集合可以支持更高的插入吞吐量。

2.2 自动删除最古老的文档

为了给新文档腾出空间,capped集合自动删除集合中最老的文档,而不需要脚本或显式的删除操作。
考虑以下有盖集合的潜在用例:

  • 存储由大容量系统生成的日志信息。在没有索引的capped集合中插入文档的速度接近于将日志信息直接写入文件系统的速度。此外,内置的先进先出属性在管理存储使用的同时维护事件的顺序。
  • 在有上限的集合中缓存少量数据。因为缓存是读的而不是写的,所以您要么需要确保这个集合始终保持在工作集中(即在RAM中),要么接受对所需的一个或多个索引的写损失。

例如,oplog.rs在复制集中存储操作日志使用一个capped集合。从MongoDB 4.0开始,与其他有上限的集合不同,oplog可以超过其配置的大小限制,以避免删除大多数提交点。

2.3 _id索引

默认情况下,上限集合有一个_id字段和_id字段上的索引。

3. 限制和建议

3.1 更新

如果您计划在一个有上限的集合中更新文档,请创建一个索引,以便这些更新操作不需要对集合进行扫描。

3.2 文档个数

在3.2版本中进行了更改。
如果更新或替换操作更改了文档大小,则操作将失败。

3.3 文档删除

不能从一个有上限的集合中删除文档。要从集合中删除所有文档,请使用drop()方法删除集合并重新创建有上限的集合。

3.4 分片

不能分割一个有上限的集合。

3.5 查询效率
使用自然排序从集合中有效地检索最近插入的元素。这与日志文件中的tail有点类似。

3.6 聚合$out
聚合管道阶段$out无法将结果写入有上限的集合。

3.7 事务

从MongoDB 4.2开始,你不能在事务中写集合封顶。在事务中仍然支持从上限集合读取数据。

4. 程序

4.1 创建一个有上限的集合

您必须使用db.createCollection()方法显式地创建有上限的集合,该方法是create命令的mongo shell中的一个助手。当创建一个有上限的集合时,您必须以字节为单位指定集合的最大大小,MongoDB将为集合预先分配该大小。上限集合的大小包括用于内部开销的少量空间。

db.createCollection( "log", { capped: true, size: 100000 } )

如果size字段小于或等于4096,则集合的上限为4096字节。否则,MongoDB将把提供的大小提升为256的整数倍。
此外,你也可以在以下文件中使用max字段指定集合的最大文档数量:

db.createCollection("log", { capped : true, size : 5242880, max : 5000 } )

重要的:
大小参数始终是必需的,即使在指定最大文档数量时也是如此。如果集合在达到最大文档数之前达到最大大小限制,MongoDB将删除旧文档。

4.2 查询一个有上限的集合

如果在没有指定顺序的上限的集合上执行find(), MongoDB保证结果的顺序与插入顺序相同。
要按反向插入顺序检索文档,可以发出find()和sort()方法,其中$natural参数设置为-1,如下面的示例所示:

db.cappedCollection.find().sort( { $natural: -1 } )

4.3 检查集合是否有上限

使用isCapped()方法判断一个集合是否有上限,如下所示:

db.collection.isCapped()

4.4 将集合转换为Capped

您可以使用convertToCapped命令将一个无上限的集合转换为一个有上限的集合:

db.runCommand({"convertToCapped": "mycoll", size: 100000});

size参数指定带帽集合的大小(以字节为单位)。
它在操作期间持有一个数据库独占锁。锁定同一数据库的其他操作将被阻塞,直到操作完成。查看一些常见的客户端操作占用哪些锁?用于锁定数据库的操作。

4.5 Tailable光标
您可以使用带有上限集合的tailable游标。类似于Unix tail -f命令,tailable游标“tail”结束一个有上限的集合。当新文档被插入到capped集合中时,可以使用tailable游标继续检索文档。
有关创建可调整游标的信息,请参阅可调整游标。

上限集合(固定集合)应用场景:

比如日志文件,聊天记录,通话信息记录等只需保留最近某段时间内的应用场景,都会使用到MongoDB的固定集合。

你可能感兴趣的:(MongoDB)