다음을 통해 공유

Azure Cosmos DB for NoSQL 中的数据建模

尽管无架构数据库(如 Azure Cosmos DB)可以轻松存储和查询非结构化和半结构化数据,但考虑数据模型以优化性能、可伸缩性和成本。

如何存储数据? 应用程序如何检索和查询数据? 应用程序是读取密集型还是写入密集型?

阅读本文后,可以回答以下问题:

  • 什么是数据建模,我为什么应该关注?
  • Azure Cosmos DB 中的数据建模与关系数据库有何不同?
  • 如何在非关系数据库中表达数据关系?
  • 我应何时嵌入数据和何时链接数据?

JSON 格式的数字

Azure Cosmos DB 将文档保存在 JSON 中,因此在将数字存储在 JSON 中之前,请务必确定是否将数字转换为字符串。 如果数字可能超过String 定义的双精度数字的边界,请将所有数字转换为 aJSON 规范解释了为何使用此边界之外的数字是一种不良做法,因为互作性问题。 这些问题尤其与分区键列相关,因为它不可变,并且需要数据迁移才能在以后进行更改。

嵌入数据

在 Azure Cosmos DB 中为数据建模时,请将实体视为以 JSON 文档表示的 自包含项

为了进行比较,让我们先了解一下关系数据库中的数据建模方式。 下面的示例演示了如何在关系型数据库中存储一个人的信息。

关系数据库模型的屏幕截图。

使用关系数据库时,策略是将所有数据规范化。 规范化数据通常涉及到将一个实体(例如某人)的信息分解为多个离散的组成部分。 在示例中,一个人可以有多个联系人详细信息记录,以及多个地址记录。 可以通过提取常见字段(如类型)来进一步细分联系人详细信息。 相同的方法适用于地址。 每个记录都可以归类为 家庭企业

规范化数据时的指导前提是避免在每个记录中 存储冗余数据 ,而是引用数据。 在本示例中,若要读取某个人的所有联系人详细信息和地址信息,在运行时需要使用 JOINS 有效地重新撰写(或反规范化)数据。

SELECT p.FirstName, p.LastName, a.City, cd.Detail
FROM Person p
JOIN ContactDetail cd ON cd.PersonId = p.Id
JOIN ContactDetailType cdt ON cdt.Id = cd.TypeId
JOIN Address a ON a.PersonId = p.Id

更新单个人员的联系人详细信息和地址需要跨多个单个表执行写入作。

现在让我们了解如何将相同的数据建模为 Azure Cosmos DB 中的自包含实体。

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "addresses": [
        {
            "line1": "100 Some Street",
            "line2": "Unit 1",
            "city": "Seattle",
            "state": "WA",
            "zip": 98012
        }
    ],
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555", "extension": 5555}
    ]
}

使用此方法,我们已将与此人相关的所有信息(例如其联系人详细信息和地址)嵌入单个 JSON 文档中,来非规范化人员记录。 此外,因为我们不受固定架构的限制,所以我们可以灵活地执行一些操作,例如可以具有完全不同类型的联系人详细信息。

从数据库检索完整的人员记录现在针对单个项的单个容器执行单个 读取作 。 更新人员记录的联系人详细信息和地址也是针对单个项的单个写入操作

非规范化数据可能会减少应用程序完成常见作所需的查询数和更新。

何时嵌入

通常在下列情况下使用嵌入式数据模型:

  • 实体之间存在“包含” 关系。
  • 实体之间存在一对多关系。
  • 数据 不常更改
  • 数据 不会在没有绑定的情况下增长
  • 数据经常一 起查询

注意

通常非规范化数据模型具有更好的读取性能。

何时不嵌入

尽管 Azure Cosmos DB 中的经验法则是对所有内容进行非规范化,并将所有数据嵌入单个项,但此方法可能会导致避免这种情况。

以下面的 JSON 代码段为例。

{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "comments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        …
        {"id": 100001, "author": "jane", "comment": "and on we go ..."},
        …
        {"id": 1000000001, "author": "angry", "comment": "blah angry blah angry"},
        …
        {"id": ∞ + 1, "author": "bored", "comment": "oh man, will this ever end?"},
    ]
}

如果建模典型的博客或内容管理系统(CMS),则此示例可能是带有嵌入注释的帖子实体。 此示例中的问题是评论数组没有限制,这意味着任何单个发布的评论数都没有(实际)限制。 此设计可能会导致问题,因为项目的大小可以无限大,因此请避免此问题。

随着项大小的增加,大规模传输、读取和更新数据变得更加具有挑战性。

在此情况下,最好是考虑以下数据模型。

Post item:
{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "recentComments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        {"id": 3, "author": "jane", "comment": "....."}
    ]
}

Comment items:
[
    {"id": 4, "postId": "1", "author": "anon", "comment": "more goodness"},
    {"id": 5, "postId": "1", "author": "bob", "comment": "tails from the field"},
    ...
    {"id": 99, "postId": "1", "author": "angry", "comment": "blah angry blah angry"},
    {"id": 100, "postId": "2", "author": "anon", "comment": "yet more"},
    ...
    {"id": 199, "postId": "2", "author": "bored", "comment": "will this ever end?"}   
]

此模型具有每个注释的项,其中包含帖子标识符的属性。 此模型允许帖子包含任意数量的评论并高效增长。 如果用户想要查看的内容不止是最近的评论,则需通过传递 postId(应为评论容器的分区键)查询此容器。

另一种情况是,嵌入数据不是一个好主意是,嵌入数据经常跨项使用,并且经常更改。

以下面的 JSON 代码段为例。

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        {
            "numberHeld": 100,
            "stock": { "symbol": "zbzb", "open": 1, "high": 2, "low": 0.5 }
        },
        {
            "numberHeld": 50,
            "stock": { "symbol": "xcxc", "open": 89, "high": 93.24, "low": 88.87 }
        }
    ]
}

此示例可以表示人员的股票组合。 我们选择将股票信息嵌入每个项目组合文档中。 在相关数据经常更改的嵌入数据的环境中,经常更改的数据意味着你不断更新每个项目组合。 使用股票交易应用程序的示例,每次交易股票时,都会更新每个投资组合项目。

股票 zbzb 可以在一天内交易数百次,成千上万的用户可以在他们的投资组合中交易 zbzb 。 使用类似示例的数据模型,系统每天必须多次更新数千个项目组合文档,这无法很好地扩展。

参考数据

在许多情况下,嵌入数据非常有效,但在某些情况下,非规范化数据会导致比值得的更多问题。 那么,你能做什么?

可以在文档数据库中的实体之间创建关系,而不仅仅是在关系数据库中创建关系。 在文档数据库中,一项可以包含连接到其他文档中数据的信息。 Azure Cosmos DB 不是针对复杂关系而设计的,例如关系数据库中的关系,但项之间的简单链接是可能的,并且可能会有所帮助。

在 JSON 中,我们使用前面提供的股票组合示例,但这次我们引用了投资组合中的股票项,而不是嵌入它。 这样,当库存项目全天频繁更改时,唯一需要更新的项目是单个股票文档。

Person document:
{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        { "numberHeld":  100, "stockId": 1},
        { "numberHeld":  50, "stockId": 2}
    ]
}

Stock documents:
{
    "id": "1",
    "symbol": "zbzb",
    "open": 1,
    "high": 2,
    "low": 0.5,
    "vol": 11970000,
    "mkt-cap": 42000000,
    "pe": 5.89
},
{
    "id": "2",
    "symbol": "xcxc",
    "open": 89,
    "high": 93.24,
    "low": 88.87,
    "vol": 2970200,
    "mkt-cap": 1005000,
    "pe": 75.82
}

此方法的一个缺点是,应用程序必须发出多个数据库请求来获取有关人员投资组合中每个股票的信息。 此设计使写入数据更快,因为更新经常发生。 但是,它使读取或查询数据的速度变慢,这对于此系统来说不太重要。

注意

规范化的数据模型可能需要更多的往返访问服务器

外键呢?

由于没有约束的概念(如外键),因此数据库不会验证文档中的任何文档间关系;这些链接实际上是“弱的”。如果要确保项引用的数据实际存在,则需要在应用程序中执行此步骤,或者在 Azure Cosmos DB 上使用服务器端触发器或存储过程。

何时引用

通常在下列情况下使用规范化的数据模型:

  • 表示一对多关系。
  • 表示多对多关系。
  • 相关数据频繁更改
  • 引用的数据可能没有限制

注意

通常规范化能够提供更好的写入性能。

将关系数据存储在何处?

关系的增长有助于确定存储引用的项。

让我们看看对出版商和书籍进行建模的 JSON。

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press",
    "books": [ 1, 2, 3, ..., 100, ..., 1000]
}

Book documents:
{"id": "1", "name": "Azure Cosmos DB 101" }
{"id": "2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "3", "name": "Taking over China one JSON doc at a time" }
...
{"id": "100", "name": "Learn about Azure Cosmos DB" }
...
{"id": "1000", "name": "Deep Dive into Azure Cosmos DB" }

如果每个出版商的书籍数量较小且增长有限,则将书籍引用存储在发布者项中可能很有用。 但是,如果每个出版商的书籍数量没有限制,那么此数据模型将产生可变、不断增长的数组,类似于示例中的出版商文档。

切换结构会导致模型表示相同的数据,但避免了大型可变集合。

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press"
}

Book documents:
{"id": "1","name": "Azure Cosmos DB 101", "pub-id": "mspress"}
{"id": "2","name": "Azure Cosmos DB for RDBMS Users", "pub-id": "mspress"}
{"id": "3","name": "Taking over China one JSON doc at a time", "pub-id": "mspress"}
...
{"id": "100","name": "Learn about Azure Cosmos DB", "pub-id": "mspress"}
...
{"id": "1000","name": "Deep Dive into Azure Cosmos DB", "pub-id": "mspress"}

在此示例中,发布者文档不再包含未绑定的集合。 相反,每个书籍文档都包含对其出版商的引用。

如何对多对多关系建模?

在关系数据库中,多对多关系通常使用联接表建模。 这些关系只是将其他表中的记录联接在一起。

显示如何联接表的屏幕截图。

可能想要使用文档复制相同内容,并生成类似以下示例的数据模型。

Author documents:
{"id": "a1", "name": "Thomas Andersen" }
{"id": "a2", "name": "William Wakefield" }

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101" }
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "b3", "name": "Taking over China one JSON doc at a time" }
{"id": "b4", "name": "Learn about Azure Cosmos DB" }
{"id": "b5", "name": "Deep Dive into Azure Cosmos DB" }

Joining documents:
{"authorId": "a1", "bookId": "b1" }
{"authorId": "a2", "bookId": "b1" }
{"authorId": "a1", "bookId": "b2" }
{"authorId": "a1", "bookId": "b3" }

此方法有效,但使用作者的书籍或书籍加载作者始终需要至少两个额外的数据库查询。 对联接项的一个查询,然后另一个查询提取要联接的实际项。

如果此联接只是将两个数据片段粘合在一起,那么为什么不将它完全删除? 请看下面的示例。

Author documents:
{"id": "a1", "name": "Thomas Andersen", "books": ["b1", "b2", "b3"]}
{"id": "a2", "name": "William Wakefield", "books": ["b1", "b4"]}

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101", "authors": ["a1", "a2"]}
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users", "authors": ["a1"]}
{"id": "b3", "name": "Learn about Azure Cosmos DB", "authors": ["a1"]}
{"id": "b4", "name": "Deep Dive into Azure Cosmos DB", "authors": ["a2"]}

使用此模型,你可以通过查看作者的文档轻松查看作者撰写的书籍。 还可以通过检查书籍文档来查看哪些作者撰写了一本书。 无需使用单独的联接表或进行额外的查询。 通过此模型,应用程序可以更快、更简单地获取所需的数据。

混合数据模型

我们探索嵌入(或非规范化)和引用(或规范化)数据。 每个方法都有好处,并涉及权衡。

它并不总是必须是或。 毫不犹豫地混在一起。

根据应用程序的特定使用模式和工作负载,混合嵌入数据和引用的数据可能有意义。 此方法可以简化应用程序逻辑,减少服务器往返,并保持良好的性能。

请考虑以下 JSON。

Author documents:
{
    "id": "a1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "countOfBooks": 3,
    "books": ["b1", "b2", "b3"],
    "images": [
        {"thumbnail": "https://....png"}
        {"profile": "https://....png"}
        {"large": "https://....png"}
    ]
},
{
    "id": "a2",
    "firstName": "William",
    "lastName": "Wakefield",
    "countOfBooks": 1,
    "books": ["b1"],
    "images": [
        {"thumbnail": "https://....png"}
    ]
}

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
        {"id": "a2", "name": "William Wakefield", "thumbnailUrl": "https://....png"}
    ]
},
{
    "id": "b2",
    "name": "Azure Cosmos DB for RDBMS Users",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
    ]
}

在这里,我们(主要是)遵循嵌入的模型,其中来自其他实体的数据嵌入在顶级文档中,但引用了其他数据。

如果查看书籍文档中的作者数组,会看到一些有趣的字段。 某个 id 字段是用来引用作者文档的字段,这是规范化模型中的标准做法,但是我们还使用了 namethumbnailUrl。 我们只能 id 使用,并允许应用程序使用“链接”从相应的作者项检索它所需的任何其他信息。但是,由于应用程序显示作者的姓名和每个书籍的缩略图图片,使作者 的某些数据非 规范化可减少列表中每本书的服务器往返次数。

如果作者的姓名发生更改或更新其照片,则需要更新他们出版的每本书。 但是,对于此应用程序,假设作者很少更改其名称,这种妥协是可接受的设计决策。

在此示例中,有 预先计算的聚合 值,用于在读取作期间节省成本。 在此示例中,嵌入在作者项中的一些数据是在运行时计算的数据。 每次发布新书时,都会创建一个书籍项 ,countOfBooks 字段会根据特定作者存在的书籍文档数设置为计算值。 这种优化对于读取频繁的系统来说是有益的,为了优化读取,我们可以对写入操作执行更多计算。

因为 Azure Cosmos DB 支持多文档事务,所以构建一个具有预先计算字段的模型是可能的。 由于此限制,许多 NoSQL 存储无法跨文档执行事务,因此提倡设计决策,例如“始终嵌入所有内容”。 在 Azure Cosmos DB 中,可以使用服务器端触发器或存储过程在一个 ACID 事务中插入书籍和更新作者信息等。 现在,无需将所有内容嵌入一个项,只需确保数据保持一致。

区分不同的项类型

在某些情况下,你可能想要在同一集合中混合不同的项类型;如果希望多个相关文档位于同一 分区中,通常会出现此设计选择。 例如,可将书籍和书籍评论放入同一个集合,并按 bookId 将此集合分区。 在这种情况下,通常需要向文档添加一个字段,用于标识其类型以区分它们。

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "bookId": "b1",
    "type": "book"
}

Review documents:
{
    "id": "r1",
    "content": "This book is awesome",
    "bookId": "b1",
    "type": "review"
}
{
    "id": "r2",
    "content": "Best book ever!",
    "bookId": "b1",
    "type": "review"
}

适用于 Azure Cosmos DB 的 Azure Synapse Link 是一种云原生混合事务和分析处理 (HTAP) 功能,可用于对 Azure Cosmos DB 中的操作数据运行准实时分析。 Azure Synapse Link 在 Azure Cosmos DB 和 Azure Synapse Analytics 之间创建无缝集成。

这种集成通过 Azure Cosmos DB 分析存储进行,这是事务数据的列式表示形式,可实现大规模分析,而不会影响事务工作负荷。 通过分析存储,可以针对大型数据集运行快速且负担得起的查询。 无需复制数据,也无需担心减慢主数据库的速度。 为容器启用分析存储时,对数据所做的每一项更改几乎都会立即复制到分析存储中。 无需设置更改源或运行提取、转换和加载 (ETL) 作业。 系统将这两个存储保持同步。

借助 Azure Synapse Link,现在可以直接从 Azure Synapse Analytics 连接到 Azure Cosmos DB 容器和访问分析存储,而不会产生任何请求单位(请求单位)成本。 Azure Synapse Analytics 目前支持具有 Synapse Apache Spark 和无服务器 SQL 池的 Azure Synapse Link。 如果你拥有多区域分发的 Azure Cosmos DB 帐户,为容器启用分析存储后,它将适用于该帐户的所有区域。

分析存储自动架构推理

Azure Cosmos DB 事务存储是面向行的半结构化数据,而分析存储使用列式和结构化格式。 系统会使用分析存储的架构推理规则为客户自动完成这种转换。 转换过程存在以下方面的限制:最大嵌套级别数目、最大属性数目、不支持的数据类型,等等。

注意

在分析存储的上下文中,我们将以下结构视为属性:

  • JSON “elements” 或 “string-value pairs separated by a :
  • JSON 对象,由 {}
  • JSON 数组,由 []

可以使用以下技术最大程度地减少架构推理转换的影响,并最大化分析功能。

标准化

规范化变得不那么相关,因为 Azure Synapse Link 允许使用 T-SQL 或 Spark SQL 联接容器。 规范化的预期好处是:

  • 减少事务存储和分析存储中的数据占用空间。
  • 减小事务。
  • 减少每个文档的属性数。
  • 减少数据结构的嵌套级别数。

在数据中拥有更少的属性和更少的级别可加快分析查询的速度。 它还有助于确保数据的所有部分都包含在分析存储中。 如有关自动架构推理规则的文章中所述,在分析存储中表示的级别和属性数目是有限制的。

规范化的另一个重要因素是 Azure Synapse 中的 SQL无服务器池支持最多包含 1,000 列的结果集,并且公开嵌套列也计入该限制。 换言之,分析存储和 Synapse SQL无服务器池都限制为 1,000 个属性。

但是,既然非规范化是 Azure Cosmos DB 的一项重要数据建模技术,我该怎么做呢? 答案是必须为事务和分析工作负载找到适当的平衡点。

分区键

分析存储中不使用 Azure Cosmos DB 分区键(PK)。 现在可以使用所需的任何 PK 对分析存储的副本使用分析存储自定义分区。 由于这种隔离,可为事务数据选择一个 PK 并将重点放在数据引入和点读取上,而跨分区查询可以通过 Azure Synapse Link 来完成。 请看以下示例:

在假设的全局 IoT 方案中,用作良好的分区键, device id 因为所有设备都会生成类似的数据量,从而防止出现热分区问题。 但是,如果要分析多个设备的数据,例如“来自昨天的所有数据”或“每个城市的总数据”,则可能有问题,因为这些查询是跨分区查询。 这些查询可能会损害事务性能,因为它们使用一部分吞吐量(以请求单位为单位)来运行。 但如果使用 Azure Synapse Link,可以运行这些分析查询而不产生请求单位成本。 分析存储列式格式针对分析查询进行优化,Azure Synapse Link 支持使用 Azure Synapse Analytics 运行时实现最佳性能。

数据类型和属性名称

有关自动架构推理规则的文章列出了支持的数据类型。 虽然 Azure Synapse 运行时可能以不同的方式处理受支持的数据类型,但不支持的数据类型会阻止分析存储中的表示形式。 例如:使用遵循 ISO 8601 UTC 标准的 DateTime 字符串时,Azure Synapse 中的 Spark 池将这些列表示为string,Azure Synapse 中的 SQL 无服务器池表示这些列。varchar(8000)

另一个挑战是 Azure Synapse Spark 不接受所有字符。 虽然它接受空格,但不接受冒号、重音符和逗号等字符。 假设项目有一个名为 “名字,姓氏”的属性。 此属性将在分析存储中表示,Synapse SQL 无服务器池也可以正常读取它。 但此属性位于分析存储中,而 Azure Synapse Spark 无法从分析存储中读取任何数据,包括所有其他属性。 总而言之,如果某个属性在其名称中使用了不受支持的字符,你就无法使用 Azure Synapse Spark。

数据平展

Azure Cosmos DB 数据顶层的每个属性都会成为分析存储中的列。 嵌套对象或数组中的属性以 JSON 形式存储在分析存储中,保留其结构。 嵌套结构需要从 Azure Synapse 运行时进行额外的处理,以将数据平展为结构化格式,这在大数据方案中可能是一个挑战。

该项在分析存储中只有两列, id 并且 contactDetails。 所有其他数据 email,以及 phone需要通过 SQL 函数进行额外处理才能单独读取。


{
    "id": "1",
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555"}
    ]
}

该项在分析存储中具有三列,idemail以及phone。 可以列的形式直接访问所有数据。


{
    "id": "1",
    "email": "thomas@andersen.com",
    "phone": "+1 555 555-5555"
}

数据分层

Azure Synapse Link 允许从以下角度降低成本:

  • 减少事务数据库中运行的查询数。
  • 针对数据引入和点读取进行优化的 PK,减少数据占用空间、热分区方案和分区拆分。
  • 由于分析生存时间 (ATTL) 与事务生存时间 (TTTL) 不相关,因此可以实现数据分层。 可在事务存储中将事务数据保留几天、几周、几个月,并在分析存储中将数据保留数年或永久保留。 分析存储列格式融合了自然数据压缩,压缩率为 50% 到 90%。 其每 GB 成本大约为事务存储实际价格的 10%。 有关当前备份限制的详细信息,请参阅分析存储概述
  • 环境中没有运行 ETL 作业,这意味着无需为其分配请求单位。

受控的冗余

对于数据模型已存在且无法更改的情况,此方法是一个很好的替代方法。 当前数据模型不适用于分析存储。 这种优势存在,因为分析存储具有限制可嵌套数据级别和每个文档中可以具有多少个属性的规则。 如果数据过于复杂或字段过多,则分析存储中可能不会包含一些重要信息。 如果这种情况是这样,可以使用 Azure Cosmos DB 更改源 将数据复制到另一个容器中,从而为 Azure Synapse Link 友好数据模型应用所需的转换。 请看以下示例:

场景

容器 CustomersOrdersAndItems 用于存储在线订单,包括客户和商品详细信息:帐单邮寄地址、交付地址、交付方式、交付状态、商品价格等。只表示前 1,000 个属性,关键信息不包含在分析存储中,这会阻止 Azure Synapse Link 的使用。 容器包含 PB 级记录,因此无法更改应用程序并重新建模数据。

问题的另一个方面是大量数据。 分析部门经常使用数十亿行,导致他们无法使用 tttl 来删除旧数据。 由于分析需求,在事务数据库中维护整个数据历史记录迫使他们不断增加预配的请求单位,而这又会影响成本。 事务和分析工作负载同时争用相同的资源。

你可以做什么?

使用更改源的解决方案

  • 工程团队决定使用更改源来填充三个新容器:CustomersOrdersItems。 使用更改源,它们正在规范化和平展数据。 从数据模型中删除不必要的信息,使每个容器包含大约 100 个属性,这可以避免由于存在自动架构推理限制而导致的数据丢失。
  • 这些新容器已启用分析存储,Analytics Department 使用 Synapse Analytics 读取数据。 这减少了请求单元的使用,因为分析查询在 Synapse Apache Spark 和无服务器 SQL 池中运行。
  • 容器 CustomersOrdersAndItems 现在已设置生存时间(TTL)以仅保留六个月的数据,这允许另一个请求单位使用量减少,因为 Azure Cosmos DB 中每个 GB 的请求单位至少有一个请求单位。 数据更少,单位更少请求。

要点

本文中最大的要点是,无架构方案中的数据建模与以往一样重要。

就像有多种方法可在屏幕上表示一个数据片段一样,数据的建模方法也不会只有一种。 你需要了解应用程序及其生成、使用和处理数据的方式。 通过应用此处介绍的准则,可以创建一个模型,以满足应用程序的即时需求。 应用程序更改时,使用无架构数据库的灵活性轻松调整和改进数据模型。