MongoDB的数据建模

MongoDB是一种面向Document的NoSQL数据库,如果我们还是按照RDB的方式来思考MongoDB的数据建模,则不能有效地利用MongoDB的优势;然而,我们也不能因为Document的灵活性,就可以在设计之初放任自流。

适度的建模是非常有必要的,尤其对于相对复杂的关联关系。因为在MongoDB中,处理这种关联关系既可以使用Link,也可以使用Embedded。

我们要评价一种决策,不能将其与具体的上下文割裂开来做判断,那种单纯说A技术要比B技术好的做法,就像小孩子看卡通片里的人物只知道说谁是好人谁是坏人一般的幼稚。世界上没有一种完美至善的技术,关键还是要结合场景来看使用是否得法。

例如使用Embedded方式,就各有优缺点。举例来说,倘若我们采用Embedded方式将Addresses作为Person对象内部的数组:

{
  name: 'Kate Monster',
  ssn: '123-456-7890',
  addresses : [
     { street: '123 Sesame St', city: 'Anytown', cc: 'USA' },
     { street: '123 Avenue Q', city: 'New York', cc: 'USA' }
  ]
}

当我们在查询Person的信息时,要获取其内嵌的属性细节,我们无需再执行多次查询。倘若我们改变一下领域场景,需要开发一个任务跟踪系统。如果我们将Tasks的信息嵌入到Person对象中,当我们面对以下需求:

  • 显示所有明天到期的任务
  • 显示所有未完成的任务

采用这种Embedded就不那么令人愉快了。

如果采用Link方式,情况就完全不同了:

//Tasks
[
    {
        _id: ObjectID('AAAA'),
        task_number: 1234,
        taks_name: 'Prepare MongoDB environment',
        due_date: '2017-01-15'
    },
    {
        _id: ObjectID('BBBB'),
        task_number: 1235,
        taks_name: 'Import Test Data',
        due_date: '2017-02-15'
    },
]

//Persons
{
  name: 'Kate Monster',
  role: 'Manager',
  tasks : [
    ObjectID('AAAA'),
    ObjectID('BBBB')
  ]
}

有得必有失,当我们需要查询Person承担的Tasks时,采用这种方式,就需要采用application-level join方式执行两次查询。

这种建模方式还带来另一种可能,就是原本Person->Tasks的one-to-N关系就可以变为N-to-N关系,因为一个Task可以被多个Person所拥有。如果采用Embedded方式,则会导致Task数据的冗余。

在文章 6 Rules of Thumb for MongoDB Schema Design中,作者将这种1对N关联实现的判断依据划分为三种形式:

  • one-to-few
  • one-to-many
  • one-to-squillions

但我认为该怎么实现关联,应该从Entity之间的领域关系来判断,我们可以引入DDD的Aggregation设计概念作为建模的依据。简单来说,如果使用Embedded,可以认为该Entity处于Aggregation边界之内,对外应该通过Aggregation Root来访问。文章 6 Rules of Thumb for MongoDB Schema Design的说法就是:

Will the entities on the “N” side of the One-to-N ever need to stand alone?

如果是Stand Alone,就意味着该Entity可以成为一个独立的Aggregation,然后再通过ID与另外一个Aggregate关联。

在SegmentFault上则有人做了如此总结

  • FirstClass (比如“User”这种) 应该用独立的Collection
  • "条目类型"的,应该 embedded
  • 两个模型之间如果是包含关系,用 embedded
  • 多对多关系,用 link(类似sql里面的foregin key)
  • 如果一个模型,其可能存的对象很少,那么就用独立的collection,这样有助于mongodb server做缓存
  • embedded方式不利于做复杂的关联,复杂的查询
  • embedded方式性能很有优势,如果你有“性能”方面的要求,可以考虑用embbed

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2017-01-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏伦少的博客

spark 统计每天新增用户数

转载请务必注明原创地址为:http://dongkelun.com/2018/04/11/sparkNewUV/

3223
来自专栏xingoo, 一个梦想做发明家的程序员

在Elasticsearch中查询Term Vectors词条向量信息

这篇文章有点深度,可能需要一些Lucene或者全文检索的背景。由于我也很久没有看过Lucene了,有些地方理解的不对还请多多指正。 更多内容还请参考整理的E...

22110
来自专栏上善若水

L016使用/dev/random生成随机数

很多库例程产生的“随机”数是准备用于仿真、游戏等等;它们在被用于密钥生成一类的安全函数时是不够随机的。其问题在于这些库例程使用的算法的未来值可以被攻击者轻易地推...

824
来自专栏张高兴的博客

为什么树莓派不会受到 Spectre 和 Meltdown 攻击

3197
来自专栏数据库

时序数据库技术体系(二)–初识InfluxDB

在上篇文章《时序数据库体系技术 – 时序数据存储模型设计》中笔者分别介绍了多种时序数据库在存储模型设计上的一些考虑,其中OpenTSDB基于HBase对维度值进...

2906
来自专栏ImportSource

ZGC回收器到底有多变态?

多少年来,JVM中的各种垃圾回收器都在努力追求着两个目标,暂停时间足够短,同时吞吐量也要不错。为了追求二者兼具,各种垃圾回收器可谓绞尽脑汁,但还是无法同时让两个...

2421
来自专栏编程札记

Spark的分区机制的应用及PageRank算法的实现

Spark中有一个很重要的特性是对数据集在节点间的分区进行控制,因为在分布式系统中,通信的代价是很大的,因此控制数据分布以获得最少的网络传输可以极大地提升整体性...

391
来自专栏杨建荣的学习笔记

Oracle表空间检测异常的问题诊断

不知道大家在工作中的表空间管理情况如何,大体会分为两派。以前的公司我们更喜欢直接把空间都分配好,比如500G的容量规划,那就提前准备500G,另外一类是...

3609
来自专栏技术分享

.NET并行编程实践(一:.NET并行计算基本介绍、并行循环使用模式)

阅读目录: 1.开篇介绍 2.NET并行计算基本介绍 3.并行循环使用模式 3.1并行For循环 3.2并行ForEach循环 3.3并行LINQ(PLI...

18210
来自专栏Java架构解析

如果再有人问你分布式 ID,这篇文章丢给他

通常我们会调研各种各样的生成策略,根据不同的业务,采取最合适的策略,下面我会讨论一下各种策略/算法,以及他们的一些优劣点。

523

扫码关注云+社区