在MongoDB的数据有灵活的模式。不像SQL数据库,(SQL数据库)要求你必须在插入数据之前决定和声明一个表的模式,MongoDB的集合不强制文档的结构。这个灵活性有利于文档到实体或对象的映射。每个文档可以匹配所要表示实体的数据字段,即使数据的变化很显著。但在实际操作中,一个集合的文档共享一个相似的结构。
数据模型的关键挑战在于平衡应用的需要,数据库引擎的性能和数据存取模式。当设计数据模型时,要考虑数据在应用里的使用情况(如,查询、更新和处理数据),以及数据本身的内在结构。
文档结构
在为MongoDB应用设计数据模型时的关键是围绕文档的结构和应用时如何表示数据间的联系。有两个工具来允许应用来表示这些关系:引用和嵌入文档( references and embedded documents)。
- 引用
引用通过包括连接或一个文档到另一个文档间的引用存储着数据间的关系。应用能够解析这些引用来访问到相关数据。广义上说,这些都是归一化的数据模型(normalized data models).
上图的数据模型使用引用来联系文档。contract文档和access文档都保护着user文档的引用。
下面介绍归一化数据模型在使用引用的优缺点:
归一化模型使用引用描述文档间的关系。一般地,使用归一化模型的情况有,
- 当嵌入会导致数据重复且不会提供有效的读性能。
- 表示更复杂的多对多的关系
- 对大型分级数据建模
引用比嵌入式文档的灵活性更大。但客户端应用必须处理引用带来的查询问题。总之,归一化数据模型需要更多的往返服务器。
- 嵌入数据
嵌入式文档通过在一个单一文档结构里存储相关数据来捕获数据间的关系。MongoDB的文档使在一个文档里的一个字段或字段数据嵌入一个文档作为子文档具体可能性。这些非规范化数据使得应用可以在一个单一数据库操作力获取和操纵数据。
上图的数据模型就是嵌入式字段保护所有的相关信息。
下面讨论嵌入子文档的数据模型的优缺点:
使用MongoDB,你可以在一个单一结构或文档嵌入相关数据。这个模型是著名的“非规范化”模型,利用了MongoDB丰富文档的优势。
嵌入数据模型允许应用在相同的数据库记录里存储相关片段信息。因此,应用在完成一个常规操作时,只需处理很少的查询或更新。
一般,当下面情形时可使用嵌入数据模型:
- 实体间有“包含关系”
- 实体间有一对多的关系。在这些关系里,“多“或子文档经常被看做"一"或父文档的上下文里
一般来说,嵌入提供了更好的读性能,以及在单一数据库操作里请求和获取相关数据的能力。嵌入数据模型使得在哪一个原子操作里更新相关数据成为可能。
然而,在一个文档的嵌入数据模型可能导致文档创建后的增长。文档的增长会影响写性能并导致数据碎片问题。并且,在MongoDB里的文档大小必须小于最大的BSON文档大小。对大型二进制数据,考虑GridFS。
写操作的原子性
在MongoDB,写操作在文档这一级是原子的,并且没有单一的写操作能原子性的影响多个文档或集合。一个有嵌入数据的非规范化数据模型在一个单一文档里包含了能表示一个实体的相关数据。这有利于写操作的原子性,因为单一的写操作能直接对一个实体插入或更新数据。规范化数据会在多个集合里分散了数据,这会要求多次写操作,因此不是原子性的。
然而,有利于原子性写的模式会限制一个应用使用数据的方法或修改数据的方法。因此需要平衡原子性和平衡性。
文档增长
有的更新,比如向数组添加元素或添加新的字段,会增大文档的大小。如果文档的大小超过了给该文档分配的空间,MongoDB会重新定位这个文档。文档的增长会影响规范化和非规范化数据的选择。
数据使用和性能
当设计一个文档模型,要考虑应用将如何使用你的数据库。比如,如果你的应用仅使用最近插入的数据,考虑使用 Capped Collections.或者,你的应用需要总是读操作,添加索引是常见的提升性能的办法。