文档DB中每个集合应该有一个实体吗?
考虑我在下图中有外键关系:
我应该为员工创建两个集合吗?其他的公司。或者我应该将它们存储在一个集合中吗?
我读了here在documentdb范围内的存储过程触发器等在集合中。因此,通过将不同的实体拆分为单独的集合,我开箱即用。
所以将这两个类转储为单个实体并不是更好,如下所示:
{
"Id": 1001,
"Industry": "Software",
"Employees": [
{
"Id": 10011,
"Name": "John Doe",
"CompanyId": 1001
},
{
"Id": 10012,
"Name": "Jane Doe",
"CompanyId": 1001
}
]
}
在DocumentDB中实现相关实体的标准做法是什么?
答案 0 :(得分:56)
每个集合存储多个实体类型通常很好。是否将实体类型存储到单个文档中需要更多考虑。
正如大卫所说 - 如何建模数据有点主观。
在集合中存储多个实体类型
首先......让我们谈谈在集合中存储多个实体。 DocumentDB集合是不表。集合不强制架构;换句话说,您可以在同一个集合中存储具有不同模式的不同类型的文档。您只需在文档中添加类型属性即可跟踪不同类型的实体。
您应该将集合视为查询和事务执行的分区和边界的单位。因此,在同一个集合中存储不同实体类型的巨大优势是您可以通过sprocs获得开箱即用的事务支持。
在文档中存储多个实体类型
是否在单个文档中存储多个实体类型需要更多考虑。这通常被称为去规范化(通过在单个文档中嵌入数据来捕获数据之间的关系)和规范化(通过创建弱链接来捕获数据之间的关系< / em>到其他文件)你的数据。
通常反规范化可提供更好的读取效果。
应用程序可能需要发出更少的查询和更新以完成常见操作。
通常,在以下情况下使用非规范化数据模型:
非规范化数据模型的示例:
{
"Id": 1001,
"Type": "Company",
"Industry": "Software",
"Employees": [
{
"Id": 10011,
"Type": "Employee",
"Name": "John Doe"
},
{
"Id": 10012,
"Type": "Employee",
"Name": "Jane Doe"
}
]
}
通常规范化可提供更好的写效果。
提供比去标准化更多的灵活性
客户端应用程序必须发出后续查询以解析引用。换句话说,规范化数据模型可能需要更多往返服务器。
通常,使用标准化数据模型:
标准化数据模型的示例:
{
"Id": 1001,
"Type": "Company",
"Industry": "Software"
}
{
"Id": 10011,
"Type": "Employee",
"Name": "John Doe",
"CompanyId": 1001
}
{
"Id": 10012,
"Type": "Employee",
"Name": "Jane Doe",
"CompanyId": 1001
}
混合方法
在标准化和去标准化之间进行选择不一定是黑白选择。我经常发现获胜的设计模式是一种混合方法,您可以选择对对象场的部分集合进行标准化,并对其他字段进行去标准化。
换句话说,您可以选择对频繁读取的稳定(或不可变)属性进行反规范化,以减少后续查询的需要,同时规范化频繁写入/变异字段,以减少扇出写入的需要。
混合方法的例子:
// Author documents:
[{
"id": 1,
"firstName": "Thomas",
"lastName": "Andersen",
"countOfBooks": 3,
"books": [1, 2, 3],
"images": [{
"thumbnail": "http://....png"
}, {
"profile": "http://....png"
}, {
"large": "http://....png"
}]
}, {
"id": 2,
"firstName": "William",
"lastName": "Wakefield",
"countOfBooks": 1,
"books": [1, 4, 5],
"images": [{
"thumbnail": "http://....png"
}]
}]
// Book documents:
[{
"id": 1,
"name": "DocumentDB 101",
"authors": [{
"id": 1,
"name": "Thomas Andersen",
"thumbnailUrl": "http://....png"
}, {
"id": 2,
"name": "William Wakefield",
"thumbnailUrl": "http://....png"
}]
}, {
"id": 2,
"name": "DocumentDB for RDBMS Users",
"authors": [{
"id": 1,
"name": "Thomas Andersen",
"thumbnailUrl": "http://....png"
}, ]
}]
答案 1 :(得分:6)
您的问题有点主观,因为您要求实体设计,为此,没有单一的正确答案。
但是:从更目标的角度来看:没有什么可以阻止您在集合中拥有多个实体类型(例如Company
文档类型和Employee
文档类型,在您的情况下)。
您需要为自己包含某种类型的提示(可能是type
属性),以便在运行查询时帮助区分这两种提示。但是,通过在同一个集合中包含这两种类型,您现在可以使用集合范围。关于type
属性:由于DocumentDB默认为所有属性编制索引,因此type
属性很容易集成到您的查询中。
编辑删除了有关每个容量单元3个集合的部分,因为当DocumentDB从预览转换为生产时,该安排已被删除。
答案 2 :(得分:1)
在过去的5年中,Cosmos DB发生了许多变化,影响数据结构设计的最重要变化之一就是可以创建许多容器并在所有容器之间共享RU。
在同一容器中组合多个实体类型(用于收集的新名称)仍然可以。但是,在2020年,也可以将每种实体类型放在单独的容器中。
当然,这取决于应用程序的需求,并且非常重要的考虑因素是您打算如何读取此信息。但是,这里是您可以考虑的常规数据结构和方法:
对于要优化的读取,将数据复制到专用于此目的的新容器中,并确保分区键与查询所依据的主要参数匹配。您可以将许多不同的实体放在同一容器中。
您会发现这种方式的读取操作效率要高很多倍。
Cosmos DB的性能在很大程度上取决于数据量,并且如果您确保可以轻松地通过分区键访问文档,那么将大量数据放入单个文档中就不会带来任何明显的性能提升。将它们保存在单独的文档中。
示例
您有两个容器:
订单按 productId 进行分区,订单详细信息按 orderId 进行分区。但是,对于一项显示单个用户订单历史记录的新功能而言,不仅要花费 userId 属性获取订单,而且尤其要对每个订单进行后续调用,它会花费太多的RU。获取放置在单独分区中的订单详细信息。
幸运的是,两个文档都包含一个 userId 属性。您要做的是创建一个新容器,该容器可能称为用户订购,并将 userId 属性配置为分区键。然后将所有文档从 orders 和 order-details 复制到此容器。
您现在可以通过 userId 对这个容器进行高效的读取。
您可以基于变更源使用Data Factory,Azure Functions复制,不久将为此目的提供内置功能(在注释中查看): https://stackoverflow.com/a/64355508/392362