注意事项

模式设计,即在文档中表示数据的方式,对于数据表示来说时非常关键的。

为 MongoDB 做模式设计时,在性能、可伸缩性和简单性方面是重中之重,也需要考虑一些特别的注意事项。

限制条件

与常见的 SQL 相比而言,MongoDB 有自己的限制条件:

  • 文档最大限制为 16M 大小
  • 从磁盘读写完整文档
  • 更新会重写整个文档
  • 在文档级别进行原子更新

访问模式

设计模式时最需要关注的就是数据库的读操作,在数据库运行过程中,应尽量减少查询的数量,这就需要在设计时确保一起查询的数据存储在同一个文档中。

其实,就是考虑是否是否可以将动态(读/写)数据和静态(主要是读)数据分离开,如未经常使用的数据应该移到不同的集合中。

在进行模式设计时,提高最常见查询的优先级会获得最佳的性能。

关系类型

数据之间的关系影响着文档之间应该是内嵌还是引用。

比如说,需要弄清楚如何在不执行其他查询的情况下引用文档,以及当关系发生变化时需要更新多少文档。

关系基数对于文档之间的关系非常重要,如一对一、一对多、多对多、一对百万、多对百万等等关系基础,影响的程度差距非常大,应选取最佳格式去做建模。

在关系基数的基础上,还需关注访问的情况、重要数据更新与读取的比例,这些充分考虑之后,将有助于确定应采用内嵌文档还是引用文档。

范式化和反范式化

基本概念

通常来说,多文档之间的关系可以使用反范式化(内嵌)或范式化(引用)。

范式化是指在文档中引用外部数据的标识,同一份数据只存在一个地方。

在查询时,查询完整的数据需要做 JOIN 的操作,需要查询多次才可能获取到所需内容;但是在更改时仅需修改一处地方,不需要担心破坏数据的完整性。

反范式化是指将外部数据复制一份存储在文档中,也就是说同一份数据存在多处地方。

在查询时,只需查询一次即可得到所需内容,查询效率比较可观;而在更改时,需要更新多处地方,可能会出现数据不一致的情况,不能保证完整性。

范式化选择

决定何时采用范式化以及何时采用反范式化是比较困难的:通常,范式化的写入速度更快,而反范式化的读取速度更快。

通过判断以下因素可决策选择使用范式化还是反范式化:

更适合范式化 更适合反范式化
较大子文档 较小子文档
数据经常变更 数据不经常变更
数据要强一致 数据最终一致即可
文档数据大幅增加 文档数据小幅增加
数据通常不包含在结果中 数据通常需要执行二次查询才能获得
快速写入 快速读取

模型设计小技巧

指导原则

通常来说,具有类似模式的文档应该保存在同一个集合中。

对于集合来说,需要考虑的一个大问题是锁机制(每个文档都有一个读/写锁)和存储。

当使用 --directoryperdb 选项时,每个数据库都可以保留在自己的目录中,这允许你将不同的数据库挂载到不同的卷中。

同一个应用程序连接的数据库可以根据业务进行划分,也许可以将高价值的业务数据存储在 SSD 上,或者是使用 RAID10 进行存储,而低价值的数据可以存储在 RAID0 上。

删除旧数据

有些数据只在短时间内比较重要,过了这段时间,保存这些数据只是再浪费存储空间。

删除旧数据有 3 种常见的方式:使用固定集合、使用 TTL 索引、使用多个集合。

最简单的方式是使用固定集合:将集合大小设置成一个较大的值,并让旧数据从固定集合的末尾被“删除”。

第二种方式是使用 TTL 集合:TTL 集合可以更精确地控制删除文档的时间,但其在写入量过大的集合中操作速度不够快。

最后一种方式是使用多个集合:例如每个月的文档都单独使用一个集合。

一致性管理

MongoDB 支持多种一致性级别,从总是能够读取自己所写的数据到读取不确定的旧数据。

其内部实现是服务器端为每一个数据库连接都维护了请求队列,同一个连接发来的请求都会被添加到队列的末尾,连接中的任何后续请求都将依次得到处理。

这个管理方式涉及到多个客户端连接会出现并发问题,在一个连接中插入文档后,在另一个连接的后续查询却不一定会返回这个文档(实际上已经插入成功)。

同样的一致性问题在 MongoDB 拥有副本集时也会出现,副本节点的数据与主节点的数据总是会有时间差,高并发的请求同样存在读取到旧数据的风险。

MongoDB 提供了 readConcern 选项来控制被读取数据的一致性和隔离性。它通常与 writerConcern 组合使用,以控制为应用程序提供的一致性和可用性保证:

如果 readConcern=local,从当前实例查询并返回结果,不能保证数据已经写入大多数副本集成员。默认在主库读,如果本次读取使用了 causally consistent 则在从库读。

如果 readConcern=available,从当前实例查询并返回结果,不能保证数据已经写入大多数副本集成员。默认在从库读,并且此选项与 causally consistent 不能同时使用。

如果 readConcern=majority,查询结果返回被副本集的大多数成员确认的数据,读操作返回的文档是持久化的。前提是 MongoDB 必须是 WiredTiger 存储引擎。

如果 readConcern=linearizable,查询可能会等待并发执行的写操作传播到大多数副本集成员,然后再返回结果。

如果 readConcern=snapshot,这是适用于多文档事务中的操作,通常情况下使用较少。

模式迁移

随着应用程序的增长和需求的变化,数据库模式也可能需要随之增长和改变。理想情况下,如果可以的话,应该考虑使用文档版本控制模式。

最简单的方式是根据应用程序的需要改进数据库模式,以确保应用程序支持所有的旧版模式。但是这种方式可能会导致混乱,特别是当不同版本的模式存在冲突时。

为了以一种更结构化的方式处理不断变化的需求,可以在每个文档中包含一个 version 字段,并使用它来确定应用程序将接受的文档结构。

最后一种方式是在模式变更时迁移所有数据。但这通常不是一个好主意:会给系统带来压力,还必须确保所有文档都被更新成功。

模式管理

MongoDB 3.2 引入了模式验证,其可以在更新和插入操作期间对数据进行验证。

MongoDB 3.6 又通过 $jsonSchema 运算符添加了 JSON 模式验证,现在这是 MongoDB 中所有模式验证的推荐方法。

只有当文档被更改时,验证功能才会检查这些文档,并且此功能是每个集合都需要单独配置的。

要向现有集合添加验证功能,可以在 collMod 命令中使用 validator 选项。在使用 db.createCollection() 时,可以通过指定 validator 选项将验证添加到新集合中。

MongoDB 还提供了两个额外的选项:

  • validationLevel: 决定了在更新过程中验证规则对现有文档检查的严格程度
  • validationAction: 决定了是应该在发生错误时拒绝请求,还是允许请求并发出警告

当然,更详细的相关内容可以查看 官方文档

编写代码来处理数据完整性问题

为保证 MongoDB 数据的完整性,有可能需要在应用程序中增加必要的逻辑代码进行处理,也需要增加定时任务来保持数据的一致性。

有可能需要有以下的任务:

  • 一致性修复程序:检查计算和重复数据以确保每个人都具有一致的值
  • 预填充器:创建将来需要的文档
  • 聚合:保持内联聚合为最新
  • 架构检查器:确保当前使用的文档集都具有一组字段,可以自动更正它们
  • 定时备份:定期锁定和转储数据库

MongoDB - 模式设计的更多相关文章

  1. 关系型数据库与Key-value型数据库Mongodb模式设计对比

    MongoDb 相比于传统的 SQL 关系型数据库,最大的不同在于它们的模式设计( Schema Design )上的差别,正是由于这一层次的差别衍生出其它各方面的不同. 我们可以简单的认为关系型数据 ...

  2. MongoDB更需要好的模式设计 及 案例赏析

    一  挑战 设计从来就是个挑战. 当我们第一次接触数据库,学习数据库基础理论时,都需要学习范式,老师也一再强调范式是设计的基础.范式是这门课程中的重要部分,在期末考试中也一定是个重要考点.如果我们当年 ...

  3. MongoDB 进阶模式设计

    原文链接:http://www.mongoing.com/mongodb-advanced-pattern-design 12月12日上午,TJ在开源中国的年终盛典会上分享了文档模型设计的进阶技巧,就 ...

  4. MongoDB十二种最有效的模式设计【转】

    持续关注MongoDB博客(https://www.mongodb.com/blog)的同学一定会留意到,技术大牛Daniel Coupal 和 Ken W. Alger ,从 今年 2月17 号开始 ...

  5. 分享基于Entity Framework的Repository模式设计(附源码)

    关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...

  6. php模式设计之 观察者模式

    这是我写的<php模式设计>的第五篇.前面的四篇在不断学习不断加深认识,到了今天再看观察者模式,觉得非常容易理解.这也许就是我们积少成多的结果吧.希望还是能够不断进步. 开篇还是从名字说起 ...

  7. php模式设计之 适配器模式

    在这个有没有对象都要高呼“面向对象”的年代,掌握面向对象会给我们带来意想不到的方便.学编程的小伙伴从开始能写几行代码实现简单功能到后来懂得将一些重复的操作组合起来形成一个“函数”,再到后来将“函数”和 ...

  8. php模式设计之 注册树模式

    在前两篇单例模式和工厂模式后,终于迎来了最后一个基础的设计模式--注册树模式. 什么是注册树模式? 注册树模式当然也叫注册模式,注册器模式.之所以我在这里矫情一下它的名称,是因为我感觉注册树这个名称更 ...

  9. php模式设计之 工厂模式

    承接上篇php模式设计之 单例模式,(虽然好像关系不大).今天讲述第二种基础的模式设计——工厂模式. 那么何为工厂模式? 从名字来看,似乎看不出什么端倪.工厂模式,和生产有关?还是和生产流程有关?难道 ...

  10. php模式设计之 单例模式

    模式设计是什么?初学者一开始会被这高大上的名称给唬住.而对于有丰富编程经验的老鸟来说,模式设计又是无处不在.很多接触的框架就是基于各种模式设计形成的. 简单说,在写代码的过程中一开始往往接触的是面向过 ...

随机推荐

  1. 原生js的懒人轮播图

    <style> body{ margin: 0; padding: 0px;}#carousel{ margin: auto; /* 居中 */ width: 600px; /* 设置宽度 ...

  2. 洛谷P6060 [加油武汉]传染病研究

    一道不错的数学题 Solution 看到约数个数就想到枚举约数,但对于每个询问都枚举显然不现实,但是我们可以将大致的方向锁定在这方面,是否可以预处理出一定的东西,然后低复杂度询问呢? 我们想到预处理出 ...

  3. 前端框架Vue------>第一天学习(2) v-if

    API:https://cn.vuejs.org/v2/api/#key 文章目录 5.条件渲染 5.1 . v-if 5.2 . v-else-if 6 .列表渲染 7 .事件监听 5.条件渲染 5 ...

  4. reportportal 集成 robotframework 自动化执行及结果可视化

    前言: 最近领导想了个需求,想把目前组内在linux平台上执行的自动化脚本搞成可视化,如果是web站点相关日志可视化倒是简单了,ELK就是不错的选择,大部分可视化项目这种的,可以做的开起来很炫. 我们 ...

  5. Codeforces1695 D1.+D2 Tree Queries

    题意 给一个n个点的无向图,其中有一个隐藏点X,可以进行一组询问S来确定S是n个节点中的哪个点.S包括k个询问节点.询问返回的值也为k个值,每个值为X点到每个询问节点的最短路距离,求k最小为多少. 提 ...

  6. 『现学现忘』Git分支 — 41、分支基本操作(二)

    目录 6.新建一个分支并且使分支指向指定的提交对象 7.思考: 8.项目分叉历史的形成 9.分支的总结 提示:接上篇 6.新建一个分支并且使分支指向指定的提交对象 使用命令:git branch br ...

  7. CVE-2021-44832 log4j_2.17.0 RCE复现与吐槽

    先说一句,这傻x洞能给cve就离谱,大半夜给人喊起来浪费时间看了一个小时. 先说利用条件: 需要加载"特定"的配置文件信息,或者说实际利用中需要能够修改配置文件(你都能替换配置文件 ...

  8. 【单元测试】Junit 4(三)--Junit4断言

    1.0 前言 ​ 断言(assertion)是一种在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果--当程序执行到断言的位置时,对应的断言应该为真.若断言 ...

  9. Java 19 新功能介绍

    点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Java 19 在2022 年 9 ...

  10. 第2-3-7章 个人网盘服务接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss

    目录 5.8 导入其他接口代码 5.8.1 接口导入-分页查询附件 5.8.2 接口导入-根据业务类型/业务id查询附件 5.9 导入网盘服务接口 5.9.1 导入FileController 5.9 ...