Azure CosmosDB (13) CosmosDB数据建模

  《Windows Azure Platform 系列文章目录

  我们在使用NoSQL的时候,如Azure Cosmos DB,可以非常快速的查询非结构化,或半结构化的数据。我们需要花一些时间,研究Cosmos DB的数据建模,来保证查询性能和可扩展性,同事降低成本。

  阅读完这篇文章后,我们将学会:

  1.什么是数据建模,为什么我们要关注数据建模

  2.如何在Azure Cosmos DB进行数据建模,与传统关系型数据库有什么不同

  3.如何在非关系型数据库中,保存关系型数据

  4.什么时候执行嵌入(embed)数据,什么时候执行连接(link)数据

  嵌入(embed)数据

  当我们开始在Cosmos DB进行数据建模的时候,尝试对我们的数据实体(Entity)视为自包含(Self-contained items)并保存在JSON文件中

  为了比较,让我们首先看一下如何在关系型数据库中进行数据建模。下面的案例将介绍我们在关系型数据库中,如何保存用户信息

  

  当我们使用关系型数据库的时候,一般都需要将数据规范化(Normalize)。规划范数据一般都会引入数据实体,比如一个人,我们可以将用户信息分解为不同的属性信息。

  在上面的例子中,一个人有多个联系人,也有多个地址。联系人的详细信息可以进一步进行分解并提取常用字段。我们也可以用同样的方法,对地址进行分解,比如地址的类型可以是家庭地址,或者是公司地址。

  规范化数据的指导方法是避免存储冗余的数据并且应用数据。在上面的示例中,我们如果要读取一个人的所有联系人的详细信息和地址,我们需要使用JOIN方法,查询到所需要的数据:

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

  如果我们更新一个人的联系人信息和地址,需要跨多张表执行更新操作

  现在我们看看如何在Azure Cosmos DB使用自包含(Self-contained items)实体

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

  上面我们使用了非规范化(denormalized)来保存人的记录,我们将与人相关的所有信息,比如联系人的详细信息和地址信息,嵌入到单个JSON文档中,从而对人的记录进行了非规范化。另外,因为我们不局限于固定的Schema,我们可以灵活的使用不同类型的联系人信息

  从Cosmos DB中读取一条记录,现在只需要单个读取操作。更新人的记录,包括联系人信息和地址信息,也只需要一次写入的操作。

  通过使用非规范化(denormalized)保存数据,相比传统的关系型数据库,我们的应用程序的读取和更新的操作可以减少。

  什么时候使用嵌入(embed)数据

  我们一般在以下情况下,使用嵌入数据:

  1.数据实体之间有包含(contained)关系

  2.数据实体之间有1对多的关系

  3.嵌入(embed)数据不经常变化

  4.嵌入的数据不会无限增长

  5.嵌入的数据是频繁集中查询的

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

  什么时候不使用嵌入(embed)数据

  虽然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?"},
    ]
}

  如果我们对一个博客系统进行建模,上面的例子就是采用嵌入(embed)数据方法,存储评论(comments)数据。上面例子的问题是评论的数据是没有限制的,这意味着任何一个发布的POST的内容,都有无限多个评论数据。这会让Cosmos DB的JSON文件变的无限大,可能会产生问题。

  随着Cosmos DB的数据尺寸变的越来越大,读取数据和更新数据可能会产生影响。

  在这种情况下,我们最好可以考虑采用以下的数据建模。

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:
{
    "postId": "1"
    "comments": [
        {"id": 4, "author": "anon", "comment": "more goodness"},
        {"id": 5, "author": "bob", "comment": "tails from the field"},
        ...
        {"id": 99, "author": "angry", "comment": "blah angry blah angry"}
    ]
},
{
    "postId": "1"
    "comments": [
        {"id": 100, "author": "anon", "comment": "yet more"},
        ...
        {"id": 199, "author": "bored", "comment": "will this ever end?"}
    ]
}

  上面的数据模型中,在一个Container中,包含了最新三个评论,且评论具有固定的属性。

  其他的评论信息是保存在单独的Container中,每个container保存100条数据。Batch的大小设置为100,是因为我们假设应用程序允许用户一次加载100条评论数据

  

  另外的场景中,嵌入(embed)数据并不是一个好的主意,比如嵌入的数据需要经常跨项目使用,且经常发生变化

  我们可以参考下面的JSON内容:

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

  这个场景是个人投资的股票信息。我们选择将股票信息嵌入到每个投资组合文档中。在一个相关数据频繁变化的环境中,如股票交易应用程序,嵌入频繁变化的数据意味着您每次交易股票时都会不断更新每个投资组合文档。

  股票zaza可能在一天内被交易数百次,成千上万的用户可以在他们的投资组合中拥有zaza。 使用上述数据模型,我们每天必须多次更新数千个投资组合文档,导致系统无法很好地扩展。

  引用数据 (Referencing data)

  因此,在大多数情况下使用嵌入(embed)数据可以很好的处理业务场景,但是很明显在某些场景下,非规范化数据将导致更多的问题而得不偿失。我们现在应该怎么办?

  关系型数据库不是在数据数据实体之间创建关系的唯一选择。在Document Database中,我们可以在一个Document中创建对另外一个Document的引用。我们并不是说使用 Azure Cosmos DB可以更好的适应关系型数据库,或者其他Document Database。我们仅仅说明在Azure Cosmos DB中也可以使用简单的关系,并且很有用。

  在下面的JSON文档中,我们选择之前的股票投资组合的示例,但是我们采用了引用数据的关系,而不是嵌入数据(embed)。在这种情况下,当一天中股票信息发生频繁变化的时候,我们只需要更新股票的Document。

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

Stock documents:
{
    "id": "1",
    "symbol": "zaza",
    "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
}

  不过, 这种方法的一个直接缺点是, 如果您的应用程序需要显示有关在显示一个人的投资组合时持有的每只股票的信息;在这种情况下, 您需要多次访问数据库以加载每个库存文档的信息。在这里, 我们决定提高写入操作的效率, 这些操作在一天中频繁发生, 但反过来又影响了对此特定系统的性能影响较小的读取操作。

  规范化数据模型可能需要多次访问服务器

  外键在哪里?

  在Document Database中并不存在约束,外键或其他类似概念。所以在Document Database中,任何Document之间的关系都是“弱链接”的关系,并且Document Database不会验证这些关系。如果想要确保文档要引用的数据实际存在,则需在应用程序中进行此验证,或通过使用 Azure Cosmos DB 上的服务器端触发器或存储过程来验证。

  什么时候使用引用?

  我们一般在以下情况下,使用引用数据:

  1.一对多的关系

  2.多对多的关系

  3.数据需要频繁更改

  4.使用数据可能没有限制

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

  将关系存储在哪里?

  关系的增长将有助于确定用于存储引用的文档。

  让我们看看下面的对出版商和书籍进行建模的 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 the world 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 the world one JSON doc at a time"}
...
{"id": "100","name": "Learn about Azure Cosmos DB", "pub-id": "mspress"}
...
{"id": "1000","name": "Deep Dive into Azure Cosmos DB", "pub-id": "mspress"}

  在上面的示例中,我们删除了出版商文档中的无限制集合, 只在每个书籍文档中引用出版商。

  如何处理多对多关系(Many: Many)进行数据建模

  在关系型数据库中,多对多关系通常使用表连接来实现,表连接就是将其他表的记录连接在一起

  

  

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

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 the world 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"]}

  现在,如果我有作者的姓名,我可以立即知道他们所写的哪些书,相反如果我有一个书籍文档加载我可以知道作者的 Id。 这可以省去对联接表的中间查询,从而减少了应用程序需要往返访问服务器的次数。

  混合数据建模

  现在我们已经看了嵌入数据(或非规范化)和引用数据(规范化)的示例,正如我们看到的每种方法都有其优点和缺点。

  不需要始终只使用其中一种方法,可以大胆地将这两种方法结合使用。

  根据应用程序的特定使用模式和工作负载,可能在一些情况下结合使用嵌入式数据和引用数据是有意义的,可产生具有更少的服务器往返访问次数的更简单的应用程序逻辑,同时仍保持较好的性能级别。

  请考虑以下 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 事务中插入书籍和更新作者信息等。 现在无需将所有数据嵌入一个文档,只需确保数据保持一致性。

  区分不同的文档类型

  在一些场景中,我们可能需要在一个Collection中,保存不同类型的文档。这通常是这种情况,如果希望多个相关的文档中保存在相同的分区。 例如,可以将这两个丛书和同一集合中的书评和分区通过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"
}

原文地址:https://www.cnblogs.com/threestone/p/10762019.html

时间: 2024-10-10 09:59:27

Azure CosmosDB (13) CosmosDB数据建模的相关文章

用户画像数据建模方法

作者:百分点技术总监郭志金 摘自:百分点(ID: baifendian_com) 从1991年Tim Berners-Lee发明了万维网(World Wide Web)开始,到20年后2011年,互联网真正走向了一个新的里程碑,进入了“大数据时代”.经历了12.13两年热炒之后,人们逐渐冷静下来,更加聚焦于如何利用大数据挖掘潜在的商业价值,如何在企业中实实在在的应用大数据技术.伴随着大数据应用的讨论.创新,个性化技术成为了一个重要落地点.相比传统的线下会员管理.问卷调查.购物篮分析,大数据第一次

《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型 (转)

第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以跳过本章. 本章将带你漫游使用实体框架建模的基本实例,建模是实体框架的核心特性,同时也是区别实体框架和微软早期的数据访问平台的特性.一旦建好模,你就可以面向模型编写代码,而不用面向关系数据库中的行和列. 本章以创建一个简单概念模型的实例开始,然后让实体框架创建底层的数据库,剩下的实例,将向你展示,如

《MySQL Workbench数据建模与开发》

<MySQL Workbench数据建模与开发> 基本信息 原书名:MySQL Workbench:Data Modeling & Development 原出版社: McGraw-Hill Osborne Media 作者: (美)麦克劳克林(McLaughlin, M.) 译者: 张骏温 出版社:清华大学出版社 ISBN:9787302363712 上架时间:2014-6-5 出版日期:2014 年6月 开本:16开 页码:368 版次:1-1 所属分类:计算机 > 数据库

使用Microsoft Azure Backup备份Hyper-V数据

在之前的文章中讲到了微软的云计算备份解决方案-Microsoft Azure Backup,通过技术可以备份Azure中的VM以及企业内部的Hyper-V.关于Microsoft Azure Backup可参见之前写的文章<Microsoft Azure Backup介绍>: http://ericxuting.blog.51cto.com/8995534/1641104 今天主要为大家带来如何通过Azure Backup备份企业内部Hyper-V数据.(PS:本文档基于国际版Azure,由世

保险业个险计价模块开发的数据建模经验分享

前段时间在开发某大型保险公司的项目,其中,涉及到个险计价的模块,之前没接触过保险业,一看他们某险种的费率表,顿时惊呆了,是一个四维的表,也就是,四种因素(性别.保险期间.交费期间.年龄)决定一个价格,还有一个影响最终保费的因素,就是保额(也就是保险金额---最大赔付金额),但这个跟价格的关系是线性的,得出标记所以这里就忽略它不谈了. 某险种的费率表如下图所示: 这是某险种的费率表,要通过四个条件得出一个价格,如某男,保险期间设为30年,交费期间为10年,假如他现在的年龄是14岁,那他的保费价格是

NoSQL数据建模技术

原文来自“NoSQL Data Modeling Techniques”,由酷壳网陈皓编译<NoSQL数据建模技术>.这篇文章看完之后,你可能会对NoSQL的数据结构会有些感觉.我的感觉是,关系型数据库想把一致性,完整性,索引,CRUD都干好,NoSQL只干某一种事,但是牺牲了很多别的东西.总体来说,我觉得NoSQL更适合做Cache. 下面是正文: NoSQL数据库经常被用作很多非功能性的地方,如,扩展性,性能和一致性的地方.这些NoSQL的特性在理论和实践中都正在被大众广泛地研究着,研究的

使用Microsoft Azure Backup恢复Hyper-V数据

在实际环境中经常会出现这样一种场景,由于误操作或者计划外的任务导致相关数据丢失从而造成企业损失,在上一篇文章中谈到了适应Microsoft Azure Backup备份Hyper-V数据,今天主要演示当造成企业数据丢失后,如何通过Microsoft Azure Backup恢复之前备份的数据,从而降低企业损失. 1.登陆Azure门户网站查看之前备份的数据,确认备份的数据依然存在 2.登陆服务器"SH-VMHost-01",打开Azure Backup或者Windows Server

NoSQL 数据建模技术(转)

本文转载自:http://coolshell.cn/articles/7270.html ================================================ 全文译自墙外文章"NoSQL Data Modeling Techniques",译得不好,还请见谅.这篇文章看完之后,你可能会对NoSQL的数据结构会有些感觉.我的感觉是,关系型数据库想把一致性,完整性,索引,CRUD都干好,NoSQL只干某一种事,但是牺牲了很多别的东西.总体来说,我觉得NoSQL

[转] [Elasticsearch] 数据建模 - 处理关联关系(1)

[Elasticsearch] 数据建模 - 处理关联关系(1) 标签: 建模elasticsearch搜索搜索引擎 2015-08-16 23:55 6958人阅读 评论(0) 收藏 举报 分类: Search(43) Elasticsearch(43) 目录(?)[+] 数据建模(Modeling Your Data) ES是一头不同寻常的野兽,尤其是当你来自SQL的世界时.它拥有很多优势:性能,可扩展性,准实时的搜索,以及对大数据的分析能力.并且,它很容易上手!只需要下载就能够开始使用它了