聊聊 MongoDB 数据库的设计

自从正式使用了 MongoDB 之后,不止一次吐槽过 MongoDB 的各种垃圾设定,包括但不仅限于:

  • 没有事务
  • 没有表连接(新版支持了,但估摸着性能堪忧)

也就是说,同样的操作,在 SQL 下通过 JOIN 控制原子性的,通过 MongoDB 可能就不得不去查个两次,而且原子性不可保证——MongoDB 官方也是非常实诚,人家在选型的时候就说了,如果贵系统对并发性(Concurrency)有强要求,那么 MongoDB 可能就不是你的菜了。

MongoDB 初窥 中我们也简单的介绍了锁机制,如有需要可以阅读。

前两天无聊接着看《MongoDB 权威指南》的时候看到一些观点,觉得给 MongoDB 数据库的设计起到了一定指导和参考作用,故作此文(笔记):

首先我们了解两个概念:

  1. 范式化:将数据分散到不同的集合,多个集合之间可以相互引用,这样要修改这部分数据,只要修改这部分数据所在的文档,其他区域与数据内容无关。
  2. 反范式化:每个文档所需的数据都存储在文档内部,每个文档都拥有自己的数据副本。如果要修改这部分数据,需要修改这块数据对应的每一个文档。

对于我们前文所说的,由于没有「连接」,范式化意味着我们要取出完整所需的数据可能要进行多次查询,而反范式化则只要一次查询即可。从写入的角度,我们可以看到,范式化写入的消耗更少,配合锁机制,对于拥有 subdocument 概念的 MongoDB,我们需要根据自己的实际需求去权衡。

在 SQL 中,我们经常会提起:一对一,一对多,多对多,而在 MongoDB 这样的数据库中,我们可以分为新的类型:少和多,之后我们会根据少和多进行一些数据库设计的详细分析,先来简单根据之前的介绍引用一下《MongoDB 权威指南》中的表格:

更适合内嵌更适合引用
子文档较小子文档较大
数据不会定期改变数据经常改变
最终数据一致即可中间阶段的数据必须一致
文档数据小幅增加文档数据大幅增加
数据通常需要执行二次查询才能获得数据通常不包含在结果中
快速读取快速写入

通常来说,「少」的关系对于内嵌更为合适,「多」则对于引用更加合适:比如文章和标签的关系可能是多对少,文章和评论的关系可能是一(少)对多。

所以我们的 Tags 可以内嵌,而评论则使用引用更好。

由于 MongoDB 的文档会自动扩充大小,如果太过频繁的让 MongoDB 产生文档移动,将会造成性能问题,在设计阶段,可以预留足够的空间,提高写入速度。

根据这一设计原理,结合 MongoDB 的一些限制,可以在一定程度上解决以下问题,而不是看心情靠玄学去进行设计:

  • 我该不该用 MongoDB?
  • 我该用引用还是 SubDocument?

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: 知识, mongodb

已有 5 条评论

  1. NoSQL一时爽 复杂逻辑毁一生啊

    1. 我也感觉 NoSQL 实际上并不适合复杂的场景,毕竟虽然说「非关系」,但是实际上关系是不可避免的,在设计时即使可能是同一个实体内的东西,为了优化也会抽离出去,以引用的形式去使用。这种时候一旦对「并发」、「事务」存在要求,马上就会 GG 了,因此我认为 NoSQL 还是需要提供一些简单的事务处理手段,以备不时之需。

  2. nokire

    讲完了?

  3. andy

    这篇文章的内容很直白、很明确!是时候考虑是否将mongo换成mysql了...(哭)

添加新评论