MongoDB数据建模 - 索引和PK
本文关键字:PK 索引 数据 建模 MongoDB | 更新日期: 2024-11-07 08:56:24
我目前正在从RDBMS过渡到NoSQL解决方案,更具体地说是MongoDB。考虑我的数据库中的下表(原始解决方案要复杂得多,但我包括它,以便您有一个想法):
- 用户(PK_ID_User、名字、姓氏等);
- UserProfile: (PK_ID_UserProfile, ProfileName, FK_ID_User, ...);
此表中的键是 GUID,但它们是自定义生成的。例如:
- 用户 GUID 将具有以下结构:US022d717e507f40a6b9551f11ebf2fcb4(因此,US 前缀和随机数),而用户配置文件 GUIDS 将采用以下格式:UP0025f5804a30483b9b769c5707b02af6(因此 UPS 前缀和随机数)
现在,假设我想将这个RDBMS数据模型转换为NoSQL MongoDB。对于我的应用程序(使用 C# 驱动程序),MongoDB 中的所有文档属性都具有相同的名称非常重要。这也计入 ID 字段:PK_ID_User和PK_ID_UserProfile的名称(包括 GUID)必须相同。
现在,MongoDB使用标准的唯一索引属性_id来存储id。这_id字段的名称当然不能更改,即使我确实需要我的应用程序保留列/属性名称。
因此,我为我的用户和用户配置文件提出了以下文档结构。请记住,对于这种情况,出于各种原因,我选择使用引用数据建模而不是嵌入,我不会在这里解释:用户文档
{
_id: ObjectId, - indexed
PK_ID_User: custom GUID, - indexed, as it needs to be unique
FirstName: string,
...
}
UserProfile-document
{
_id: ObjectId - indexed
PK_ID_UserProfile: custom GUID, as explained above - indexed, as it needs to be unique,
...
}
下面是 C# 类:
public class User
{
[BsonConstructor]
public User() { }
[BsonId] // the _id field
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("PK_ID_User")]
public string PK_ID_User { get; set; }
//Other Mapper properties
}
我选择这种建模策略的原因如下:当前项目由使用ORM和RDBMS的整个Web服务组成,以及一个或多或少将数据库对象映射到客户端视图对象的客户端。因此,确实有必要尽可能保留 Id/PK 的名称。我决定最好让MongoDB在内部使用ObjectId(用于CRUD操作),因为它们不会导致性能开销,并使用自定义GUID,以便它们与我的其余代码兼容。这样,必须进行最少的更改,MongoDB很高兴,我也很高兴,因为在外部,我可以根据我的GUID PK继续查询结果,这些PK将始终是唯一的。与在MongoDB中一样,我的PK GUID存储为唯一的字符串,我想我不必担心服务器端的GUID开销:GUID是由我的C#应用程序创建的。
但是,我对性能有疑问,我现在每个文档/集合总是至少有 2 个索引,并且不知道它在性能方面有多昂贵。
对于我的问题,是否有更好的方法,或者我应该坚持我当前的解决方案?
亲切问候。
我现在总是每个文档/集合至少有 2 个索引,并且不知道它在性能方面有多昂贵。
索引插入和更新的成本性能,并且您未发布有关写入操作频率或设置的信息。没有测量就不可能给出明确的答案。
再说一次,如果你使用的是 Web 应用程序,我会说你的客户端的纯粹网络延迟将比 1、2 或 3 个索引之间的差异高几个数量级,因为所有这些操作大多会命中 RAM。
代价高昂的是写入磁盘,而不是在内存中重组 BTree。当然,拥有越来越多的索引会增加插入的可能性,从而导致必须命中磁盘的索引树的昂贵重组,但这也取决于键本身的结构。
如果有的话,我会担心 GUID 的缓存一致性和时间位置不好:如果你的数据是非常本地的(如日志),那么 GUID 可能会受到伤害(字符串开头的高抖动),因为更新更有可能重新排列整个子树,并且典型的时间范围查询会抓取整个树中杂乱无章的项目。但由于这似乎是关于用户和用户配置文件的,因此这样的查询可能没有多大意义。