MongoDB 4.2 亮点功能之——按需式物化视图

#开发人员#MongoDB 4.2

$merge,又称按需式物化视图,是MongoDB4.2最强大的新增功能之一。

按需式物化视图的亮点体现在哪里?

基于聚合的创建集合听起来就像是$out,它是聚合框架中的一个执行阶段,从很早的MongoDB 2.6就有了。$out阶段可以获取聚合结果,将其放到新的集合中,并用新的结果完全替换掉集合中原来的内容。这一过程很有用,但会大量消耗CPU和IO资源,因为每次都要重新生成整个集合。至少$out的操作是原子级的,它构建了一个临时集合,而且,只有在聚合管道完成工作后才进行交换。

如果我们只想更新生成的结果集而非对其完全重建,该怎么做呢?4.2版本会提供一个$merge命令。每次运行该命令,会允许你按照增量的方式更新结果集。它是一条有多个选项和功能的命令。我们来看一个样例。

假设我们有一个来自于MongoDB Atlas样本数据的AirBnB数据集,里面存放着全世界的物业数据,而每项物业数据都包含可提供的床位数。假设我们需要获取每个国家的床位数信息。

聚合操作可以很方便地做到这一点。

这样很好,不过,我们并不想在人们每次需要该数据时都运行一遍聚合操作。我们可以使用$merge将结果写入另一个集合,就如同添加{ $merge: { into: "bedcount" } }作为管道的最后一个执行阶段一样简单,如下所示:

这样,开发人员可以引用该集合作为他们的结果,而无需每次都运行聚合操作。为了更新结果,我们只需再运行一次聚合操作,就能就地更新这些值了。

然而,它还不只是简单地将整个结果集全部写出,它使用唯一的结果标识_id与集合中现有的结果相匹配。但只有在默认情况下才使用_id。使用on属性,可以使用任意具有唯一值的字段。

如果匹配上_id,在默认的情况下,$merge执行阶段将提取新的结果文档以及集合中的结果文档,合并这两个文档,生成一个包含它们所有字段的复合文档。如果没有匹配上_id,则将插入新的结果文档。该操作是由$merge的两个属性控制的,分别称为whenMatched和whenNotMatched。默认情况下,将whenMatched属性设置为“merge",表示合并两个文档。该属性也可设置为“replace",表示完全替换为新的文档;如果属性设置为“keepExisting",文档即保持原状;如果属性设置为“fail",则可以在出现重复时中止聚合执行。

管道加强了$merge的更新功能

如果对你来说这些功能仍然不够,系统还提供了利用管道更新命令执行更复杂更新操作的能力。仍以我们所举的床位数为例。假设要求你增加一个when字段,其中包含数值最后变化的时间信息。利用$merge,无需离开聚合操作就能实现这一点。我们可以通过将whenMatched的值设置为一个带$set的新管道来做到。

我们需要定义什么变量构成数据集,如何对其赋值。首先要考虑的是bedcount。

$$new符号表示“从刚刚计算过的新文档中提取数据”。因此,我们准备复制新的bedcount数据。现在,我们需要设置when字段。我们可以通过一个条件运算符实现。如果物化视图中的beccount和新的bedcount相同,我们就保留原来的值, 将旧的$last复制到记录中。如果两个值不同,我们就使用值$$NOW,正如我们之前提到的,它会即时返回当前的时间和日期。结果如下所示:

如果我们第一次运行,检查得到的结果:

进入数据库,将几张床添加到西班牙的物业并重新执行聚合:

你会看到西班牙增加了4张床,时间戳也更新了。

我们不准备在这里设置whenNotMatched属性,也无需这样做,因为默认情况下,“insert"会完成我们想做的事情。但如果我们有其他想法,我们仍然有权选择“discard"(丢弃)新文档或选择“fail”(放弃)。

优化更新操作

我们在此用于举例的数据集是相当静态的,我们的更新依然来源于对集合中所有文档运行的聚合操作。聚合数据的方式将提升实际性能。假设我们希望运行一个集合,该集合包含评级排名在前99位的物业,每次我们可以扫描完整的清单集合并生成新的集合。假设我们不想定期做这件事,如果数据集的字段中有一个last_scraped(最后下载)日期和时间,就可以实现这一点。因此,如果我们使用epoch时间对我们的新集合进行初始化,我们就能对所有记录进行处理和更新:

recentTopRates包含了所有高评级物业。很自然,listingsAndReviews集合将根据最新下载日期被更新。在任何时候,我们都能按照最新的更新日期重新运行聚合操作,快速更新recentTopRates集合。

只有在该日期之后下载的文档才会进行高评级检查,而只有通过这一检查的文档才会发送至$merge执行阶段,对recentTopRates集合进行更新。与通过$out或未过滤的$merge命令重新生成集合的方式相比,这种方式更加快捷。当然,如果某个实体已经不再是高评级物业,它会仍然留存在集合中,但是,对于那些过期的实体或已经明确哪些物业已经脱离排名名单并需要被移除的聚合来说,可以通过TTL(生存时间)索引对它们进行处理。

超越物化视图范畴

这里只是举了一个例子,便于你对如何按需创建物化视图、并对定制过程的灵活性拥有一定的了解。由于它属于不同的集合,你也可以通过不同方式将其索引到源集合,以匹配你的用户或应用的查询需要。

在新的$merge命令和旧的$out命令之间还存在一些其他的不同。在读取和写入的位置方面,$merge有更多的灵活性。例如,它可以读取或写入分片集合的数据($out只能读取分片集合的数据),这就允许你的物化视图能够跨越多个分片,从而水平扩展集合。

它还可以将结果写入不同的数据库,允许你不仅能运行一个可更新的聚合,还能将结果迁移到不同节点上的不同数据库中。这样可以将聚合数据和更新移动到分析节点上,通过将生产环境与报表和图表生成隔离,从而降低生产负荷。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190830A03SKW00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券