面向 MongoDB 开发者的 CosmosDB 教程(二)

原文:Cosmos DB for MongoDB developers

协议:CC BY-NC-SA 4.0

六、一致性

一致性是数据库事务中一个非常重要的因素。它规定了数据库在读写期间的行为。在分布式数据库中,这是一个更加复杂和关键的因素。在这一章中,你将学习 Azure Cosmos DB 中可用的一致性级别。

分布式数据库中的一致性

由于数据库系统对于数据驱动的应用至关重要,因此确保可用性非常重要。因此,为了确保高可用性(HA),您将最终拥有多个数据库副本(参见图 6-1 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-1

ReplicaSet consists of a leader (primary) and followers (secondaries)

跨区域拷贝将确保业务连续性,以防主区域出现问题。这就是所谓的灾难恢复(DR)。还可以有更多的跨区域用例。最普遍的一种情况是拥有遍布全球的用户群,并希望在离用户更近的地方部署应用,以避免网络延迟(见图 6-2 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-2

Database with ReplicaSet within geographical as well as cross-geographical regions

在这种情况下,确保一致性可能相当麻烦。我们来看一个例子。

如果您执行写入请求以插入项目 A,并立即从主节点和辅助节点读取项目 A,则响应将取决于一致性级别。在跨 geo 中,可能有更多的变量,例如网络延迟、连接故障等。,这将导致进一步的问题(参见图 6-3 )。因此,CAP 定理指出,人们必须在一致性、可用性和分区容差这两个方面中选择任何一个。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-3

Database with ReplicaSet indicating a network failure

前面是一个失败的例子。跨地理区域的网络延迟的成功用例如何?程序是一样的。您必须插入数据,然后尝试跨地理区域执行读取命令(假设间隔为 80 毫秒)。这是否会返回正确的结果?另一个名为 PACELC 的定理在这里出现。它指出,如果系统在正常条件下工作,除了 CAP 之外,还必须考虑延迟与一致性(见图 6-4 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-4

Database with ReplicaSet having network latency across geographic locations

现在,让我们看看不同的一致性级别。

MongoDB 中的一致性

在 MongoDB 中,默认情况下,强一致性适用于本地实例,最终适用于读取副本。这种行为会受到定义事务行为的读写关注点的影响。

在 MongoDB 中,写请求可以指定写关注点,这决定了来自复制实例数量的写确认。这将确保写事务的持久性。对于读取请求,您可以定义四种类型的读取问题:本地、可用、多数和可线性化。在“本地”的情况下,不管写入问题如何,数据将从主实例可用,而不确保对其他复制副本的持久承诺。如果读取与因果一致的会话相关联,则默认为针对主节点的读取问题,默认为辅助节点。对于“可用”,行为保持与“本地”相同,只是当因果一致性会话不存在时,它默认为读取辅助节点的问题,并且当设置了因果一致性时,它不可用。在大多数节点确认写入后,“大多数”读取问题将恢复到更一致的数据。“可线性化”读取将等待,直到大多数副本确认写入,这确保了所有读取问题中最一致的读取。这只能为主要实例/主节点定义。

您可以通过在 MongoDB 中显式指定 read 关注点来执行该命令(参见清单 6-1 )。

db.collection.find().readConcern(<"majority"|"local"|"linearizable"|"available">)

Listing 6-1MongoDB’s Shell Command for Specifying Read Concern

如果您在分布式数据库环境中工作,确保您可以读取您的写入是一个挑战,因为复制您的写入需要一些时间。在实践中,建立可线性化的读取问题通常是不可能的,因为这会增加等待时间。最近,在 MongoDB 3.6 中,引入了一个客户端会话,其中读/写在用户会话的范围内是一致的,这被称为因果一致性。这将确保您不会有性能故障,并且仍然允许您能够读取您的写入。

Azure Cosmos DB 中的一致性

Azure Cosmos DB 有五种类型的一致性:强的、有界的陈旧性、会话、一致前缀和最终。为了完全理解这一点,让我们定义两组一致性行为:一致的读/写和高吞吐量。

一致的读取/写入

Azure Cosmos DB 提供了一致读/写的可能性,具有三个特征:强一致性、有限陈旧性和会话陈旧性。为了理解它们的行为,让我们考虑一下每种行为的几个例子。

清单 6-2 给出了一个样本文档的代码,我们将使用它来探索不同的一致性级别。

{ "_id" : "469", "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "deviceidday" : "03/10/2018" }

Listing 6-2Code for Sample Document

强一致性

为了实现强大的一致性,Azure Cosmos DB 确保只有在写入被主副本和大多数副本提交为持久写入或被中止后,写入才是可见的。客户端永远不会看到未提交或部分提交的写入,并保证会读取最新确认的写入(参见图 6-5 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-5

Write acknowledgment (checkmarks indicate committed writes)

就读取操作消耗的延迟和 ru 而言,这是成本最高的延迟级别(请参见本节后面的示例代码)。要在 Azure 门户中设置强一致性,请参见图 6-6

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-6

Configuration for a strong level of consistency

Azure Cosmos DB 采用了一个"线性化检查器",它持续监控操作并直接以指标报告任何一致性违规。让我们用一个例子来深入研究一下细节。

首先,让我们推送数据并尝试获取它。

db.coll.insert({ "_id" : "469", "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "deviceidday" : "03/10/2018" });

为了更好地理解性能,运行以下命令(清单 6-3 )。插入花费了 13.9 RUs,延迟相当于 55 毫秒。

db.runCommand({getLastRequestStatistics: 1});
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 13.9,
        "RequestDurationInMilliSeconds" : NumberLong(55)
}

Listing 6-3Checking Performance of Linearizability

请求费用是以 RUs 表示的成本。现在,我们来读一下(列表 6-4 )。读取请求的请求费用将为 6.98 RUs,延迟为 4ms。

db.coll.find({"_id" : "469"})
db.runCommand({getLastRequestStatistics: 1});
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 6.98,
        "RequestDurationInMilliSeconds" : NumberLong(4)
}

Listing 6-4Calculating the Request Charge (in RUs)

如果你注意到了,阅读一份文件的成本是 7。

有限的陈旧

这是一个独特的概念,适用于非常高的吞吐量。在这种情况下,读操作可能会滞后于写操作一个配置的时间间隔或操作次数(参见图 6-7 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-7

Write acknowledgment (checkmarks depict committed writes)

您可以根据需要创建尽可能多的地理复制实例,这对于强一致性来说是不可用的。这也是数据丢失保证的默认级别,以防您的主区域所在的 Azure 区域出现问题。就延迟和读取操作消耗的 ru 数量而言,成本与强一致性的成本相同。要在 Azure 门户中配置这个一致性级别,请参考图 6-8

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-8

Configuration for a bounded staleness consistency level

为有界陈旧性配置值有两个约束:

  1. 最大滞后(操作次数):10 到 1,000,000 适用于单个区域,100,000 到 1,000,000 适用于多个区域。
  2. 最大滞后时间:单个地区 5 秒到 1 天,多个地区 5 分钟到 1 天

Note

在写这本书的时候,Azure Cosmos DB–MongoDB API 不支持这个一致性级别。我把它包含在这里供您参考,因为它是一个重要的功能,在不久的将来可能会作为 API 的一部分包含进来。

会议

这种一致性的范围是局部的,在您必须读取写入内容的情况下非常有用。如果您必须在会话中执行立即读取操作,这也很重要,例如,为需要立即检索值的用户会话写入信息,或者任何设备写入需要立即与最新值聚合的数据,等等。详见图 6-9

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-9

Write acknowledgment (checkmarks depict committed writes)

在这种一致性级别下,允许任意数量的地理分布。与其他强一致性相比,它将以较低的成本提供最大的吞吐量。要在 Azure 门户中设置这个一致性级别,请参考图 6-10

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-10

Configuration of the session consistency level Note

在写这本书的时候,Azure Cosmos DB–MongoDB API 不支持这个一致性级别。我把它包含在这里供您参考,因为它是一个重要的功能,在不久的将来可能会作为 API 的一部分包含进来。

高流通量

有一些一致性旨在以最小的成本提供最佳的吞吐量。这些是一致前缀和最终前缀。

一致前缀

这种一致性是基于副本的最终收敛。它确保写入顺序保持不变。如果’ 1 ‘,’ 2 ‘,’ 3 ‘是用相同的序列编写的,那么 Azure Cosmos DB 将确保检索到’ 1 ‘或’ 1 ‘,’ 2 ‘或’ 1 ‘,’ 2 ‘,’ 3 ',而不考虑区域(多/单)。(见图 6-11 。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-11

Configuration of the consistent prefix consistency level

这种一致性的性能也非常接近最佳。要在 Azure 门户中配置它,请参见图 6-12

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-12

Configuration of the consistent prefix consistency level in the Azure portal Note

在写这本书的时候,Azure Cosmos DB–MongoDB API 不支持这个一致性级别。我把它包含在这里供您参考,因为它是一个重要的功能,可能会在不久的将来包含在 API 中。

可能的

最终一致性是最弱的一致性形式,在这种情况下,客户端可能会获得过时的值(比写入时间更早的值)。它确保当没有进一步的写入时,数据最终将会收敛。因为它没有确保读取顺序、提交多数或法定人数等开销。与其他一致性级别一样,最终一致性以更低的成本在读取和写入方面表现最佳(参见图 6-13 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-13

Configuration of eventual consistency level

要在 Azure 门户中配置最终的一致性级别,请参见图 6-14

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-14

Configuration of the eventual consistency level in the Azure portal

一旦一致性级别发生变化,则将文档推送到集合中,并使用db.runCommand()评估结果,请参考清单 6-5

db.coll.insert({ "_id" : "469", "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "TimeStamp" : { "date" : 1520660314835 }, "deviceidday" : "03/10/2018" });

db.runCommand({getLastRequestStatistics: 1});
{
    "_t" : "GetRequestStatisticsResponse",
    "ok" : 1,
    "CommandName" : "insert",
    "RequestCharge" : 13.9,
    "RequestDurationInMilliSeconds" : NumberLong(5)
}

Listing 6-5Insertion Took 13.9 RUs with Latency Equivalent to 5ms

让我们试着阅读文档(参见清单 6-6 )。

db.coll.find({"_id" : "469"})
db.runCommand({getLastRequestStatistics: 1});
{
    "_t" : "GetRequestStatisticsResponse",
    "ok" : 1,
    "CommandName" : "OP_QUERY",
    "RequestCharge" : 3.49,
    "RequestDurationInMilliSeconds" : NumberLong(4)
}

Listing 6-6Request Charge for Read Request

如果您将请求费用与强一致性费用进行比较,就会发现这要少得多。

结论

我已经讨论了各种类型的一致性,并解释了一些给出可比较的结果,一些是性能性的,一些保证一致的读取。没有支配选择一个而不是另一个的经验法则,但是建议您彻底分析用例并选择适当的一致性。

为了确保 Azure Cosmos DB 满足您选择的一致性级别,Azure Cosmos DB 将其包含在 SLA 保证中。它还有一个线性化检查器,可持续监控操作并报告任何违规情况。对于有界陈旧性,它验证在有界陈旧性配置中出现的复制界限,并报告指标中的违规,称为概率有界陈旧性指标。此外,其他一致性级别的违规将在 Azure 门户➤ Azure Cosmos DB 帐户➤度量➤一致性中可用的一致性度量中报告(见图 6-15 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-15

Consistency metrics

七、调整大小

到目前为止,我已经从使用角度介绍了 Azure Cosmos DB 的各个方面。在这一章中,我将解释 Azure Cosmos DB 的规模方面。

与任何传统的实现不同,Azure Cosmos DB 并不要求开发者成为硬件工程师,也不要求数据库架构师无所不知。坦率地说,仅仅基于开发商/建筑师的经验是不可能获得准确的评估的。Azure Cosmos DB 完美地解决了这个问题,并提供了一种基于以下参数配置数据库的自然方式:

  • 文档的大小
  • 文件数量
  • CRUD(创建、读取、更新和删除)操作的数量

请求单位(RUs)

Azure Cosmos DB 是为高吞吐量和预测性能而设计的,这意味着它必须预留资源。然而,它提供了动态增加和减少预留资源的灵活性。保留资源被定义为每秒的请求单位。它是处理每个 CRUD 操作所需的资源的组合,包括 CPU、内存和 IOPS(每秒输入/输出操作数)。Azure Cosmos DB 将 ru 平均分配给分区。因此,如果您在容器级别有 10k RUs,对于五个物理分区,每个分区将接收 2k RUs。

RUs 的分配

在前一节的最后一行,我使用了术语容器,您可能想知道这是什么。这是一个用来索引据库或集合的术语——无论您想在哪里分配 ru。如果您有多个集合,并且您不想为每个集合分配专用的 ru,那么在集合时分配 ru 将是正确的选择。否则,您可以在收集时分配 ru。你也可以两者兼得。一旦在数据库级分配了 RUs,在给定的数据库中提供一个集合时,您有两种选择。一种是不将 RU 分配给被供应的集合,而是从数据库中取出 RU(直到数据库 RU 的最大值)。另一种是将 ru 分配给一个集合,该集合将专用于该集合,并且属于同一数据库的任何其他集合都不能消费它们。请注意,明确分配给集合的 ru 将是您分配给数据库的 ru 之外的 ru。例如,如果我们向一个数据库分配了 50k ru,然后向其中添加了 5 个集合,那么您将被收取 50k ru 的费用,而不管您添加了多少个集合,并且任何集合都可能占用 50k ru。请注意,由于达到了各自使用的峰值,它们可能不得不争夺 RUs。如果我们添加第 6 个集合,并在同一个数据库中为该集合提供 10k ru,我们将总共被收取 50k ru+10k ru = 60k ru,并且我们新添加的集合将享受专用性能。

要在数据库级别添加 ru,通过导航到 Azure Cosmos DB Account ➤数据浏览器➤新数据库创建一个新数据库,然后勾选 Provision Throughput 并填写表单,如图 7-1 所示。图 7-27-5 ,说明了在数据库和收集级别的 ru 分配。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-5

There are no scale options for collections using RUs from a database

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-4

Adding RUs to the database from the collection

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-3

Allocating RUs at the collection level

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-2

Option to scale will appear on the database (continuation of Figure 7-1)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-1

Allocating RUs at the database level

现在,在数据库级别分配 ru 有助于在您有多个集合的情况下,通过降低成本和使它们共享相同数量的 ru。假设您有 80 个收藏,并且所有收藏都属于无限存储。开始时,您至少需要 80k RUs,但是在数据库级别进行分配时,您可以添加一个具有 50k RUs 的数据库,这样您就完成了排序。在这种情况下,最大限制将是 50k。

请注意,一旦在数据库级别选择了 RU 分配,就必须在预配集合时强制选择分区键。

计算 RUs

为了理解 RUs 的计算,让我们考虑一个例子。清单 7-1 中提供了相关 JSON 文档的代码。

{ "_id" : "469", "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "deviceidday" : "03/10/2018" }

Listing 7-1JSON Document

以下是其他统计数据:

  • 一个文档的大小= 231 字节
  • 文档数量= 300,000,000
  • 写操作次数= 200
  • 读取操作次数= 400

让我们执行一些查询来确定我们需要多少个 ru。

globaldb:PRIMARY> db.eventmsgss.find({SensorId: 1001001}).limit(1);
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 3.49,
        "RequestDurationInMilliSeconds" : NumberLong(4)
}

前面的 read 查询使用 S ensorId作为一个标准,其中我们将它视为一个分区键,并且只取 1 条记录,大小为 231 字节。

globaldb:PRIMARY> db.eventmsgss.insertOne({ "_id" : ObjectId(), "SiteId" : 1, "DeviceId" : 1001, "SensorId" : 1001999, "Temperature" : "20.9", "TestStatus" : "Pass", "TimeStamp" : ISODate("2018-05-21T16:23:32.256Z"), "deviceidday" : "15/21/2018" })
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 13.14,
        "RequestDurationInMilliSeconds" : NumberLong(33)
}

前面的插入查询对于 231 字节的一次写入需要 13.14 RUs。有了这些结果,下面将是典型的结果:

  • 总文档大小= 65GB(大约。)
  • 保持以上大小所需的分区数量= 7 个物理分区(10GB/分区)
  • 写操作所需的 ru 数= 5256 RUs
  • 读取操作所需的 ru 数量= 1396 个 ru
  • 操作所需的 ru 总数= 6700 RUs(最接近 100,精确值为 6652 RUs)

Azure Cosmos DB RU 的价格是每 100 RUs,也就是说价格表中提到的价格必须乘以(6700/100) ×每 100 RUs 的价格。对于前面的图,我通过假设负载将分布在所有分区上来考虑这一点。因此,在实践中,每个分区有 6700 个 ru,我们可以预期有 957 个 ru(大约。).(见图 7-6 。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-6

Equal distribution of RUs in partitions

每个地理复制区域将花费独立实例的成本,与您在 Azure Cosmos DB 中配置的实例的成本完全相等。也就是说,如果你在美国西部配置了一个 Azure Cosmos DB 实例,并创建了三个副本,就会产生 3 + 1 = 4 的费用。在 Azure Cosmos DB 被地理复制的情况下,计算必须考虑区域的数量,如下所示:

  • 区域数量= 1 个写区域+ 3 个读区域,这意味着 4 个区域
  • ru 总数= 6700×4 = 26800 ru
  • 对于价格计算= (26,800/100) ×每 100 RU 的价格

让我们看另一个例子。

  • 一个文档的大小= 4KB
  • 文档数量= 1600 万
  • 写操作次数= 400
  • 读取操作次数= 200

平均而言,1 次读取大小最大为 4KB = 4.5 RUs 的文档(约),并写入大小为 4KB = 7 RUs 的文档。典型结果如下:

  • 总文档大小= 61GB(大约。)
  • 保持上述大小所需的分区数量= 7 个分区(10GB/分区)
  • 写操作所需的 ru 数= 6028 RUs
  • 读取操作所需的 ru 数量= 1800 RUs
  • 操作所需的 ru 总数= 7828 RUs

Azure Cosmos DB RUs 的价格是每 100 RUs,也就是说价格表中提到的价格必须计算为(7900/100) ×每 100 RUs 的价格。

在 Azure Cosmos DB 被地理复制的情况下,计算还应该包括区域的数量。

  • 区域数量= 1 个写区域+ 3 个读区域,这意味着 4 个区域
  • ru 总数= 7900×4 = 31600 ru
  • 对于价格计算= (31,600/100) ×每 100 个俄罗斯单位的价格

为了便于参考,Azure Cosmos DB 在 www.documentdb.com/capacityplanner 提供了一个容量规划器。

该计划程序要求您上传一个样本文档,并根据每种操作类型、文档数量等指定值。一旦完成,您必须点击计算按钮,这将在右侧反映计算结果(参见图 7-7 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-7

Azure Cosmos DB capacity planner

请注意,本章中提到的计算和容量规划器中提到的计算是任何特定应用的标准。我建议您使用门户中的查询指标和监控指标。

优化 RU 消耗

RU 是这里的货币。所以优化越好,RUs 烧的越少。有一些技巧可以与其他技巧结合使用,以提高优化。

以下是影响 RUs 优化的一些因素。

文档大小和复杂性

这是计算 RU 消耗的一个关键因素。如果您有较小的文档,消耗的 ru 数量将远远少于较大的文档。更多的字段会增加索引的开销。文档的复杂性也起着重要的作用。如果您有一个包含多个嵌入文档的文档,则一次写入的成本将消耗更高的 ru。该因素会影响读取和写入过程中 ru 的消耗。让我们看一些例子。

插入以下文件(清单 7-2 ),将收取 31.32 RUs 的费用:

db.customer.insertOne( {
  "CustomerKey": 1122,
  "Title": "Mr.",
  "FirstName": "Brian",
  "LastName": "Moore",
  "MaritalStatus": "Single",
  "Gender": "Male",
  "EmailAddress": "xxx@xxx.com",
  "YearlyIncome": 100000,
  "TotalChildren": 2,
  "Education": "Graduate",
  "NumberCarsOwned": 4,
  "AddressLine1": "House no. 4455, First Floor,",
  "AddressLine2": "Sector Zeta A, Delwara, US",

  "Phone": "xxx-xxx-xxx",
  "CustomerType": "New",
  "CompanyName": "Tingo"
});
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 31.32,
        "RequestDurationInMilliSeconds" : NumberLong(3018)
}

Listing 7-2Inserting a Large-Sized Document

如果我们缩小字段名(参见清单 7-3 ,RUs 将被优化为 21.71 RUs。

db.customer.insertOne( {
  "ck": 1122,
  "ttl": "Mr.",
  "fn": "Brian",
  "ln": "Moore",
  "ms": "Single",
  "gn": "Male",
  "ea": "xxx@xxx.com",
  "yi": 100000,
  "tc": 2,
  "edu": "Graduate",
  "nco": 4,
  "add1": "House no. 4455, First Floor,",
  "add2": "Sector Zeta A, Delwara, US",
  "ph": "xxx-xxx-xxx",
  "ct": "New",
  "cn": "Tingo"
});
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 21.71,
        "RequestDurationInMilliSeconds" : NumberLong(31)
}

Listing 7-3Minification of Field Names

如果我们删除我们用例中可能不需要的两个属性,即yi ( YearlyIncome)和tc ( TotalChildren),那么被消耗的 ru 的数量将是 19.81(参见清单 7-4 )。

globaldb:PRIMARY> db.customer.insertOne( {
...   "ck": 1122,
...   "ttl": "Mr.",
...   "fn": "Brian",
...   "ln": "Moore",
...   "ms": "Single",
...   "gn": "Male",
...   "ea": "xxx@xxx.com",
...   "edu": "Graduate",
...   "nco": 4,
...   "add1": "House no. 4455, First Floor,",
...   "add2": "Sector Zeta A, Delwara, US",
...   "ph": "xxx-xxx-xxx",
...   "ct": "New",
...   "cn": "Tingo"
...
... });
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 19.81,
        "RequestDurationInMilliSeconds" : NumberLong(24)

}

Listing 7-4RUs Consumed with Fewer Fields

数据一致性

该因素主要增加或减少读取期间的 RU 消耗。更强的一致性成本更高,更弱的一致性成本更低(参见第 6 章了解更多关于一致性的细节)。让我们看一些例子。

首先,将一致性设置为强,导航到 Azure Cosmos DB Account ➤默认一致性,并设置最终一致性(见图 7-8 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-8

Changing the default consistency in the portal to Eventual

接下来,执行以下代码:

globaldb:PRIMARY> db.customer.insertOne( {
...   "ck": 1122,
...   "ttl": "Mr.",
...   "fn": "Brian",
...   "ln": "Moore",
...   "ms": "Single",
...   "gn": "Male",
...   "ea": "xxx@xxx.com",
...   "edu": "Graduate",

...   "nco": 4,
...   "add1": "House no. 4455, First Floor,",
...   "add2": "Sector Zeta A, Delwara, US",
...   "ph": "xxx-xxx-xxx",
...   "ct": "New",
...   "cn": "Tingo"
...
... });
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 19.81,
        "RequestDurationInMilliSeconds" : NumberLong(24)
}

现在,让我们检索相关的记录(参见清单 7-5 )。将使用 2.35 RUs 检索记录,最终保持一致。

globaldb:PRIMARY> db.customer.find({}).limit(1);
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 2.35,
        "RequestDurationInMilliSeconds" : NumberLong(4)
}

Listing 7-5Retrieving the Record

我们把一致性改成强(见图 7-9 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-9

Changing the default consistency in the portal to strong

现在,如果我们执行相同的查询,消耗的 ru 数量将会增加。注意,对于强一致性,相同的查询将花费 4.7 RUs(参见清单 7-6 )。

globaldb:PRIMARY> db.customer.find({}).limit(1);
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 4.7,
        "RequestDurationInMilliSeconds" : NumberLong(4)
}

Listing 7-6Cost of Query with Strong Consistency

索引

默认情况下,Azure Cosmos DB 支持文档的自动索引,这是为读取而优化的,但写入会更昂贵。如果您需要大量的写操作和少量的读操作,可以随意关闭索引。这将有助于减少写入期间的 RU 消耗。让我们看一些例子。

关闭自动索引并打开自定义索引。将索引的一致性更改为 lazy,并使用excludedPaths来排除被索引的属性。(有关步进的更多信息,请参考第 4 章。)

让我们看一个样本文档。

{ "_id" : "469", "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "deviceidday" : "03/10/2018" }

在默认情况下,注意以下索引设置(列表 7-7 ):

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*",
            "indexes": [
                {
                    "kind": "Range",
                    "dataType": "Number",
                    "precision": -1
                },
                {
                    "kind": "Range",
                    "dataType": "String",
                    "precision": -1
                },
                {
                    "kind": "Spatial",
                    "dataType": "Point"
                },
                {
                    "kind": "Spatial",
                    "dataType": "LineString"
                },
                {
                    "kind": "Spatial",
                    "dataType": "Polygon"
                }
            ]
        }
    ],
    "excludedPaths": []
}

Listing 7-7Default Index Settings

接下来(列表 7-8 )是用于插入的 RU 消耗,花费了 12.9 RUs,延迟相当于 6ms。

db.coll.insert({ "_id" : "469", "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "deviceidday" : "03/10/2018" });
db.runCommand({getLastRequestStatistics: 1});
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 12.9,
        "RequestDurationInMilliSeconds" : NumberLong(6)
}

Listing 7-8RU Consumption for Insertion

以下(列表 7-9 )是读取文档时的 RU 消耗。读取请求的请求费用将为 3.48 RUs,延迟为 5ms。

db.coll.find({_id:ObjectId("5b0546a8512d8c81c1e6bf95")});
db.runCommand({getLastRequestStatistics: 1});
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 3.48,
        "RequestDurationInMilliSeconds" : NumberLong(5)
}

Listing 7-9RU Consumption While Reading the Document

现在,让我们执行自定义索引。索引设置将类似于清单 7-10

{
    "indexingMode": "lazy",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*",
            "indexes": [
                {
                    "kind": "Range",
                    "dataType": "Number",
                    "precision": -1
                },
                {
                    "kind": "Range",
                    "dataType": "String",
                    "precision": -1
                },
                {
                    "kind": "Spatial",
                    "dataType": "Point"
                },
                {
                    "kind": "Spatial",
                    "dataType": "LineString"
                },
                {
                    "kind": "Spatial",
                    "dataType": "Polygon"
                }
            ]
        }
    ],
    "excludedPaths": [
        {
            "path": "/SiteId/?"
        },
        {
            "path": "/DeviceId/?"
        },
        {
            "path": "/Temperature/?"
        },
        {
            "path": "/TestStatus/?"
        },
        {
            "path": "/TimeStamp/?"
        },
        {
            "path": "/deviceidday/?"
        }
    ]
}

Listing 7-10Custom Index Settings

清单 7-11 计算了插入的 RU 消耗,花费了 4.95 RUs,延迟相当于 7ms。

db.coll.insert({ "_id" : ObjectId(), "SiteId" : 2, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "deviceidday" : "03/10/2018" });
db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "insert",
        "RequestCharge" : 4.95,
        "RequestDurationInMilliSeconds" : NumberLong(7)
}

Listing 7-11RU Consumption for Insertion

以下(列表 7-12 )是读取时的 RU 消耗。读请求的请求费用为 3.48 RUs,延迟为 4 ms

globaldb:PRIMARY> db.coll.find({_id:ObjectId("5b0546a8512d8c81c1e6bf95")})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 3.48,
        "RequestDurationInMilliSeconds" : NumberLong(4)
}

Listing 7-12RU Consumption While Reading

请注意,在插入文档时,RU 消耗显著减少,但 read 保持不变。

查询模式

查询的复杂性在这里扮演着重要的角色。如果您使用了索引属性,RU 消耗将得到优化。但是,这在非分区集合中是有效的。在分区集合中,使用PartitionKey值很重要,可以帮助您优化 ru。如果同时使用PartitionKey和索引属性,这将提高 RU 消耗的效率。让我们看一些例子。

假设PartitionKeySensorId上,执行只有一条记录的查询,如下所示:

globaldb:PRIMARY> db.eventmsgss.find({SensorId:8010003}).limit(1);
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 6.98,
        "RequestDurationInMilliSeconds" : NumberLong(5)
}

如果您更改要检索的记录数量,则执行以下操作:

globaldb:PRIMARY> db.eventmsgss.find({SensorId:8010003}).limit(5);
globaldb:PRIMARY> db.runCommand({getLastRequestStatistics:1})
{
        "_t" : "GetRequestStatisticsResponse",
        "ok" : 1,
        "CommandName" : "OP_QUERY",
        "RequestCharge" : 9.64,
        "RequestDurationInMilliSeconds" : NumberLong(6)
}

RU 费用将增加到 9.64 RUs。

结论

在 Azure Cosmos DB 中,您不需要担心硬件规模,相反,您可以使用应用的事务需求作为规模的基础,例如,多少写入、读取、一致性级别、索引等。您自然能够转换成所需的 ru 数量。一旦你指定了 RU,Azure Cosmos DB 将在后台配置你不需要担心的硬件。

在部署之前确定规模很重要,但是不要太紧张,您也可以在部署之后确定规模。您可以继续使用您预期的 ru,然后您可以通过“metrics”下的“throughput”选项卡监控 ru 的消耗,并在不停机的情况下动态增加/减少 ru。

八、迁移到 Azure Cosmos DB–MongoDB API

既然我已经介绍了 Azure Cosmos DB–MongoDB API 的大部分内容,在本章中,我们将深入研究将整个应用迁移到 Azure Cosmos DB–MongoDB API 的实际逻辑。

迁移策略

有许多策略可以将 NoSQL 数据从一种数据库迁移到另一种类型的数据库。开发者在从一种技术迁移到另一种技术时主要担心的是确保目标和源之间的兼容性。有了协议支持,Azure Cosmos DB with Mongo API 试图解决兼容性问题。理想情况下,您应该只更改连接字符串,它主要在配置文件中提供,并且您可以很容易地替换它。但是在某些情况下,您必须更改代码,因为这里不支持某些命令,例如,$text$pull with condition 等。您可以在 https://aka.ms/mongodb-feature-support 访问 MongoDB API 支持页面,获取最新的命令支持列表。

下一个最重要的考虑因素是如何在最短或没有停机时间的情况下迁移数据。如果 Azure Cosmos DB 可以附加到现有的 MongoDB 集群上,并且所有的数据都可以同步,那么这就很容易了。但是不要担心,有其他方法可以简化迁移。我将在下面讨论这些。参考清单8-18-12

mongoexport 和 mongoimport

MongoDB 有两个工具mongoexportmongoimport来简化迁移。顾名思义,它们通常用于将现有数据导出到 JSON 中,并导入到 MongoDB 实例中。您可以通过 Azure Cosmos DB 使用这两种工具,如下所示:从 SSH 或 RDP 连接到 Mongo 服务器或能够访问服务器的客户端,执行特定于操作系统的命令,这些命令将在下面的章节中单独讨论。

对于 Linux
mongoexport --db <name of database> --collection <name of collection> --out <name of json file to export data in>

Listing 8-1mongoexport Command Template

mongoexport --db test --collection sample --out sample.json

Listing 8-2Exporting Data Using the mongoexport Command

现在,让我们使用mongoimport在 Azure Cosmos DB 上执行导入到导入数据。

mongoimport --host <Azure Cosmos DB URI>:10255 -u <Name of Azure Cosmos DB account> -p <primary or secondary key> --db <name of the database> --collection <name of collection> --ssl --sslAllowInvalidCertificates --type json --file <path of json file>

Listing 8-3mongoimport Command Template

mongoimport --host testmongo.documents.azure.com:10255 -u testmongo -p jsF6xFsNXz6lZ3tGVjx7bErkQCzoJUzyI2lj8MAqCD --db test --collection sample --ssl --sslAllowInvalidCertificates --type json --file sample.json

Listing 8-4mongoimport Sample Command

对于 Windows mongodump/mongorestore

mongodump是一个以二进制格式导出数据的 MongoDB 实用程序。它还可以压缩导出的数据,便于移动。mongorestore从转储中恢复数据,并将其推回到非二进制格式。

以下是命令的详细信息:

对于 Linux
mongodump --host <hostname> --port <port> --collection <name of collection> --username <username> --password <password> --out <nameof file> --gzip

Listing 8-5mongodump Command Template

mongodump --host mongodbtest.site.net --port 37017 --collection coll --username test --password "test" --out mongodbtestdmp --gzip

Listing 8-6mongorestore Command Template

现在,让我们将转储恢复到 Azure Cosmos DB。

mongorestore --host <Azure Cosmos DB account name>.documents.azure.com:10255 -u <Azure Cosmos DB account name> -p <account's primary/secondary key> --db <name of database> --collection <name of collection>--ssl --sslAllowInvalidCertificates mongodbtestdmp --gzip

Listing 8-7mongorestore Command Sample

mongorestore --host testmongocosmos.documents.azure.com:10255 -u testmongocosmos -p jsF6xFsNXz6lZ3tGVjx7bErkQCzoJUzyI2lj8MAqC --db test --collection testcoll --ssl --sslAllowInvalidCertificates mongodbtestdmp --gzip

Listing 8-8mongodump Command Template

对于 Windows
Mongodump.exe --host <hostname> --port <port> --collection <name of collection> --username <username> --password <password> --out <nameof file> --gzip

Listing 8-9mongodump Command Template

Mongodump.exe --host mongodbtest.site.net --port 37017 --collection cooll --username test --password "test" --out mongodbtestdmp --gzip

Listing 8-10mongorestore Command Template

现在,让我们将转储恢复到 Azure Cosmos DB。

mongorestore.exe --host <Azure Cosmos DB account name>.documents.azure.com:10255 -u <Azure Cosmos DB account name> -p <account's primary/secondary key> --db <name of database> --collection <name of collection>--ssl --sslAllowInvalidCertificates mongodbtestdmp --gzip

Listing 8-11mongorestore Command Sample

mongorestore.exe --host testmongocosmos.documents.azure.com:10255 -u testmongocosmos -p jsF6xFsNXz6lZ3tGVjx7bErkQCzoJUzyI2lj8MAqC --db test --collection testcoll --ssl --sslAllowInvalidCertificates mongodbtestdmp --gzip

Listing 8-12mongodump Command Template

批量遗嘱执行人

这个工具是最近添加到 Azure Cosmos DB 中的,可以在几分钟内上传数百万个文档。这是一个基于 AIMD 式拥塞控制机制设计的客户端库。这将有助于创建基于键范围的多线程,并以并行方式访问所有分区。正如我们在第 7 章中所解释的,每个分区将具有相同的 ru,因此将所有分区放在一起会将吞吐量消耗增加到 100%。它可以消耗超过 500 K RU/s 的数据,并在一小时内推送数 TB 的数据。API 详情请参考清单 8-138-14

BulkImportResponse bulkImportResponse = await bulkExecutor.BulkImportAsync(
  documents: documentsToImportInBatch,
  enableUpsert: true,
  disableAutomaticIdGeneration: true,  maxConcurrencyPerPartitionKeyRange: null,
  maxInMemorySortingBatchSize: null,
  cancellationToken: token);

Listing 8-13Usage of BulkImport API to create the data

BulkUpdateResponse bulkUpdateResponse = await bulkExecutor.BulkUpdateAsync(
  updateItems: updateItems,
  maxConcurrencyPerPartitionKeyRange: null,
  maxInMemorySortingBatchSize: null,
  cancellationToken: token);

Listing 8-14Usage of BulkImport API to which will update the document if exists

应用开关

现在,是时候改变应用了,通过切换连接字符串并将其连接到 Azure Cosmos DB–MongoDB API(见图 8-18-2 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-1

Copying the connection string from the portal (either primary or secondary)

从门户复制连接字符串(主要的或次要的),然后用现有的连接字符串替换它(在您的应用的app.config or web.config中)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-2

Replacing the connection string in the application’s config file

彻底测试应用,执行功能和负载测试,以确保应用的正确结果。

Note

测试是至关重要的,因为它将让您了解运行应用或处理峰值负载所需的 ru,您可以随时更改这些 ru。这也将利用你在整本书中学到的知识。

最佳化

以下是优化过程:

  1. In Azure Cosmos DB, increase the RUs for the duration of the import/restore and keep an eye on the throttling at Azure Metrics. If an error occurs, increase the RUs further. (See Figure 8-3.)

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 8-3

    Monitoring throughput metrics for throttling errors

  2. 确保在查询字符串级别启用 SSL,因为 Azure Cosmos DB 不允许不安全的连接。

  3. 尝试在配置 Azure Cosmos DB 的同一地区使用 Azure 中的虚拟机。否则,网络延迟会增加恢复/导入时间。

  4. It is possible to determine network latency from the client machine. Execute setVerboseShell(true) in the MongoDB shell (see Figure 8-4). Next, execute the following command:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 8-4

    Identifying the latency from the MongoDB shell

    db.coll.find().limit(1)
    
    
  5. 对于mongoimport,按如下方式配置batchSizenumInsertionWorkers:

    1. batchSize =单个单据消耗的总供应 ru/ru。如果计算出的batchSize < = 24,则将其作为batchSize值;否则,使用 24。
    2. numInsertionWorkers =(调配的吞吐量延迟(秒)/(批处理大小单次写入消耗的 ru)。

下面是一个例子:

batchSize= 24
RUs provisioned=10000
Latency=0.100 s
RU charged for 1 doc write=10 RUs
numInsertionWorkers= (10000 RUs x 0.1 s) / (24 x 10 RUs) = 4.1666

最后的命令将是

mongoimport --host testmongocosmos.documents.azure.com:10255 -u testmongocosmosd -p jsF6xFsNXz6lZ3tGVjx7bErkQCzoJUzyI2lj8 --db test --collection coll --ssl --sslAllowInvalidCertificates --type json --file sample.json --numInsertionWorkers 4 --batchSize 24
Code: Finished mongoimport command

Note

除了上文描述的工具,你还可以使用其他工具,比如mongomirrormongochef,将你的数据从mongodb迁移到 Azure Cosmos DB。

结论

从 Mongo DB 迁移到 Azure Cosmos DB - Mongo DB API 是非常有意义的,因为没有管理开销、高可伸缩性、高弹性、最低延迟、最高可用性,并且所有这些都包含在 SLA 中。现在,接下来的迁移非常具有挑战性,Azure Cosmos DB 的迁移非常简单,因为其强大的 MongoDB 协议支持,只需要很少或不需要任何更改。对于数据,协议再次支持投入,并提供使用 MongoDB 现有工具的可能性。您可以使用 MongoDB 的 shell 命令导入/导出、恢复或他们的 OOTB 工具,如 mongomirror、mongochef 等。最近,推出了 BulkExecutor 工具,它将使数据推送并行化,并将推送数据的时间减少 40 - 50 倍。

下一章,我们将探讨高级功能,如 Spark、聚合管道等。

九、Azure Cosmos DB–MongoDB API 高级服务

最后,我们已经到了旅程的最后一章,我想分享一些适用于日常场景的要点。

聚合管道

这是 MongoDB 的固有特性,对于需要分析和特定聚合的工作负载至关重要。在 Azure Cosmos DB 中,支持数据聚合管道。然而,在写这本书的时候,它是公开预览的,必须通过导航到 Azure portal 中 Azure Cosmos DB blade 下的预览项目来显式启用。

现在,让我们把手弄脏。打开你最喜欢的 MongoDB 控制台,连接 Azure Cosmos DB。

sudo mongo <Azure Cosmos DB Account Name>.documents.azure.com:10255/db -u < Azure Cosmos DB Account Name > -p <primary/secondary key> --ssl --sslAllowInvalidCertificates

以下(列表 9-1 )是示例文档和命令,用于汇总每个传感器的消息数(列表 9-2 ):

{ "_id" : ObjectId("5acafc5e2a90b81dc44b3963"), "SiteId" : 0, "DeviceId" : 0, "SensorId" : 0, "Temperature" : "20.9", "TestStatus" : "Pass", "TimeStamp" : ISODate("2018-03-10T05:38:34.835Z"), "deviceidday" : "03/10/2018" }

Listing 9-1Sample Document

globaldb:PRIMARY> db.book.aggregate([{$match:{TestStatus: "Pass", DeviceId:6} },{$group:{_id: "$SensorId", total: {$sum: 1}}},{ $sort:{SensorId: -1}}]);

Listing 9-2Aggregate Command to Count Messages per Sensor, Where DeviceId Is 6 and TestStatus Is Pass

输出如下:

{ "_id" : 0, "total" : 173 }
{ "_id" : 1, "total" : 173 }
{ "_id" : 2, "total" : 173 }
{ "_id" : 3, "total" : 173 }
{ "_id" : 4, "total" : 173 }
{ "_id" : 5, "total" : 173 }
{ "_id" : 6, "total" : 173 }
{ "_id" : 7, "total" : 173 }
{ "_id" : 8, "total" : 173 }
{ "_id" : 9, "total" : 173 }

现在,让我们创建另一个示例。在这个例子中(清单 9-3 ,我们将使用db.runCommand而不是db.collection.find().count。原因很简单,如果孤立文档存在或者正在进行分区负载平衡,那么db.collection.find().count()会导致不准确的计数。为了避免这种情况,建议在 MongoDB 中使用带有分片集群的db.runCommand。默认情况下,在 Azure Cosmos DB 中,每个实例都由一个分区组成;所以计数用db.runCommand代替db.Collection.find(). count比较合适。

globaldb:PRIMARY> db.runCommand({ count: "book",query: {"DeviceId": {$gte:10} }} )

Listing 9-3Counting the Number of Documents in a Collection Named “book” That Has DeviceId>10

输出如下:

{ "_t" : "CountResponse", "ok" : 1, "n" : NumberLong(81828) }

让我们给count命令列表 9-4 增加一些复杂性。

globaldb:PRIMARY> db.runCommand({ count: "book",query: {"DeviceId": {$gte:10} }, skip: 10} )
output
{ "_t" : "CountResponse", "ok" : 1, "n" : NumberLong(81818) }

Listing 9-4Counting the Number of Documents in a Collection Named “book” That Has DeviceId Greater Than Ten and Skips the First Ten Rows

清单 9-5 中的代码在集合“book”中获得不同的DeviceID

globaldb:PRIMARY> db.runCommand({distinct: "book", key:"DeviceId"})
{
    "_t" : "DistinctResponse",
    "ok" : 1,
    "waitedMS" : NumberLong(0),
    "values" : [
            "20.9"
    ]
}

Listing 9-5Selecting a Distinct Value for a Specified Key

列表 9-6 根据基础对设备进行分组,并计算每个设备中的传感器数量。

globaldb:PRIMARY> db.runCommand({aggregate: "book", pipeline:[{$group: { _id: "$DeviceId", count: {$sum : 1 }} }] })

Listing 9-6Grouping Devices by Basis and Counting the Number of Sensors in Each

上述清单的输出如下:

{   "result" : [
    "_t" : "AggregationPipelineResponse`1",
    "ok" : 1,       "_id" : "DeviceId",
    "waitedMS" : NumberLong(0),051
    "cursor" : {
    ]       "ns" : "db.book",
}           "id" : NumberLong(0),
globaldb:PRIMARY"firstBatch" : [
            {
                    "_id" : 0,
                    "count" : 20
            },
            {
                    "_id" : 1,
                    "count" : 2
                    },
                    {
                            "_id" : 2,
                            "count" : 20
                    },
                    {
                            "_id" : 3,
                            "count" : 2
                    }
                    ]}}

使用$match,您可以聚合命令(清单 9-7 )。

db.book.aggregate( [
  { $match: { "$Temperature": { gte: 0 } } },
  {
    $group: {
      "_id": "$DeviceId",
      "avgTemperature": { "$avg": "$Temperature" }
    }
  }
] )

Listing 9-7Aggregating the Query to Identify the Average Temperature vs. DeviceId

输出如下:

{ "_id" : 0, "avgDevice" : 0 }
{ "_id" : 1, "avgDevice" : 1 }
{ "_id" : 2, "avgDevice" : 2 }

让我们用$project$match来表示条件。

db.eventmsgsd.aggregate( [ {$match:{DeviceId:1001}},  {      $project: {      Temperature: 1,         "DeviceId": 1,         "SensorId" : 1,         "SiteId": {            $cond: {               if: { $eq: [ 1, "$SiteId" ] },               then: "$$REMOVE",               else: "$SiteId"            }         }      }   }] );

输出如下:

{ "_id" : ObjectId("5b0449132a90b84018822f96"), "Temperature" : "20.9", "DeviceId" : 1001, "SensorId" : 1001003 }
{ "_id" : ObjectId("5b0449132a90b84018822f97"), "Temperature" : "20.9", "DeviceId" : 1001, "SensorId" : 1001004 }

正如你所看到的,Azure Cosmos DB 支持大多数聚合表达式和管道阶段,在大多数情况下,允许应用开发者快速迁移到 Azure Cosmos DB,而无需更改任何代码。

火花连接器

这是收集/分析数据的最丰富、最有效的方式。MongoDB 的 Spark 连接器也可以用在这里。

让我们一步一步地经历这个过程。

第 1 步:调配 HD Insight 并为其增添活力。为此,导航至portal.azure.com,点击创建资源,搜索 HDInsight,然后选择适当的选项(参见图 9-1 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-1

Creating HDInsight from the Azure portal (search and select the image)

将出现一个页面,提供有关 HDInsight 的详细信息。在此页面上,点击创建(图 9-2 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-2

HDInsight service details page

现在,会出现一个表格,你必须在上面填写必要的信息。完成后,点击下一步(见图 9-3 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-3

Fill in the basic details

点击集群类型并选择您的首选处理框架(图 9-4 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-4

Click Cluster type and choose Spark (version 2.2.0)

现在返回并单击下一步。在这里,您可以指定与存储相关的信息。现在,您可以保留其默认设置,并点击下一步(见图 9-5 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-5

Specify the storage information

现在,单击 Create 提交带有 Spark 的 HDInsight 集群的部署(参见图 9-6 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-6

Summary form

第二步:让我们使用 SSH 进入 Spark 集群。导航到 SSH ➤集群登录,然后从下拉菜单中选择主机名,并在它下面的框中复制 SSH 命令(参见图 9-7 )。现在打开 SSH 工具,复制粘贴命令连接到头节点(参见图 9-8 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-8

Connect to Head Node using SSH

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-7

Locating the SSH command

步骤 3:使用以下代码下载 Spark 连接器:

wget https://scaleteststore.blob.core.windows.net/mongo/mongo-spark-connector-assembly-2.2.0.jar

第 4 步:运行 Spark shell 命令,用您的 Mongo 端点、数据库和集合细节替换它。输入和输出可以是相同的。

spark-shell --conf "spark.mongodb.input.uri=mongodb://testmongo:jsFlj8MAqCDqjaPBE2DWRhm9jRx5QfMQ3SYf9vwGxElPjZmeQKO1vbA==@testmongo.documents.azure.com:10255/?ssl=true&replicaSet=globaldb" --conf "spark.mongodb.output.uri=mongodb://testmongobook:jsF6xFsNXz6lZ3tGVjx7bErkQCzoJUzyI2lj8MAqCDqjaPBE2DWRhm9jRx5QfMQ3SYf9vwGxElPjZmeQKO1vbA==@testmongobook.documents.azure.com:10255/?ssl=true&replicaSet=globaldb" --conf "spark.mongodb.input.database=db" --conf="spark.mongodb.input.collection=eventmsgss" --conf "spark.mongodb.output.database=db" --conf="spark.mongodb.output.collection=coll" --jars mongo-spark-connector-assembly-2.2.0.jar

现在,在成功执行上述代码后,您将获得 Scala 控制台,这是 SparkSQL 的游乐场。执行以下代码:

scala> import com.mongodb.spark._
import com.mongodb.spark._
scala> import org.bson.Document
import org.bson.Document
scala> val rdd = MongoSpark.load(sc)

现在,让我们执行几个聚合查询(参见清单 9-89-9 )。

scala> val agg = rdd.withPipeline(Seq(Document.parse("{ $match: { DeviceId : { $eq : 1004 } } }")))
scala> println(agg.count)

Listing 9-8Counting the Number of Records on the Basis of the Filter

让我们看另一个例子。

val agg = rdd.withPipeline(Seq(Document.parse("{ $group: { _id : '$SensorId', total:{$sum:1} } } ")))

Listing 9-9Grouping by SensorId and Counting the Values

现在,您可以更高效地运行所有聚合查询。您可以将结果导出到 Azure Cosmos DB 的另一个集合中,这将帮助您分析所有数据,并离线聚合这些数据。然后可以使用这个集合在 UI 上快速展示结果。

结论

现在,您已经了解了 Azure Cosmos DB–Mongo DB API 的大部分特性和功能。协议支持保持了无缝的迁移路径和最小的学习曲线。在单个 Azure Cosmos DB 实例中,如果您配置了最少一个地理复制,您将获得每个分区的高可用性和内置的灾难恢复。此外,您还可以获得针对一致性、可用性、延迟和吞吐量的全面 SLA,这可能是一件非常昂贵的事情。这将帮助您使您的应用全年高效可用,尽可能减少延迟。所有这些都将提高应用的用户体验。考虑到这些事实,选择 Azure Cosmos DB 升级您的应用并添加特色架构工具没有太多麻烦是显而易见的。

Note

Azure Cosmos DB–MongoDB API 受到 MongoDB 中不存在的特性的限制,例如存储过程、函数、变更提要等。然而,我希望所有这些特性将很快成为这个 API 的一部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值