Mongodb架构设计浅谈

Posted 猿学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mongodb架构设计浅谈相关的知识,希望对你有一定的参考价值。

Mongodb架构设计

概览

Mongodb是文档型数据库,由于其不属于关系型数据库,不必遵守三大范式,而且也没有Join关键字来支持表连接,所以Mongodb的表结构设计跟Oracle、mysql很不一样。下面针对几种不同的表设计结构分别举例:

1对1关系模型

Mongodb架构设计浅谈

Mongodb架构设计浅谈

但是,为了方便,其实我们在设计表的时候不会严格遵守三大范式,会做一定的冗余数据,实际情况下可能就是这样子的表

那么,我们回到Mongodb,在这张非关系型的NoSQL数据库里,没有标准的外键(虽然我们可以手工建立连接,但是这种表之间的字段关联关系只能存在程序级别,数据库本身并没有提出外键约束的概念),我们可以怎么来建立表并处理表之间的关系呢?

  1. 建立连接

    这种方式可以理解为建立外键,在其中一个表里建立对方的id字段

    用户信息的文档设计 { _id: 1, name: "Peter Wilkinson", age: 27 } 保留外键的地址信息的文档设计 { user_id: 1, street: "100 some road", city: "Nevermore" }
  1. 内嵌文档

    { name: "Peter Wilkinson", age: 27, address: { street: "100 some road", city: "Nevermore" } }
    官方文档推荐1对1的数据模型尽量使用内嵌的方式,这样子会提高读操作的效率,更快地获取文档信息

1对多关系模型

1对多的关系模型,我们可以简单地以博客和对应的评论信息来举例

Mongodb架构设计浅谈

对应的Mongodb的表模型如下

博客信息的文档设计 { title: "An awesome blog", url: "http://awesomeblog.com", text: "This is an awesome blog we have just started" } 评论信息的文档设计 { name: "Peter Critic", created_on: ISODate("2014-01-01T10:01:22Z"), comment: "Awesome blog post" } { name: "John Page", created_on: ISODate("2014-01-01T11:01:22Z"), comment: "Not so awesome blog" }

在关系型数据库里,我们通常是分别建立两张表:一个Blog表、一个Comments表(从表,带有blog_id外键),然后通过join操作把两个表关联起来

Mongodb架构设计浅谈

但是在Mongodb里由于没有Join关键字,但是我们可以根据Mongodb的特点,得出以下三个解决方式:

  1. 内嵌

    内嵌了评论信息的博客文档设计 { title: "An awesome blog", url: "http://awesomeblog.com", text: "This is an awesome blog we have just started", comments: [{ name: "Peter Critic", created_on: ISODate("2014-01-01T10:01:22Z"), comment: "Awesome blog post" }, { name: "John Page", created_on: ISODate("2014-01-01T11:01:22Z"), comment: "Not so awesome blog" }] }

    上面这种表设计的好处是,我们可以直接获取指定博客下的评论信息,用户新增评论的话,直接在blog文档下的comments数组字段插入一个新值即可。

    但是这种表设计至少有三个如下的潜在问题需要注意:

    1. 博客下的评论数组可能会逐渐扩增,甚至于超过了文档的最大限制长度:16MB

    2. 第二个问题是跟写性能相关,由于评论是不停地添加至博客文档里,当有新的博客文档插入集合的时候,MongoDB会变得比较困难定位到原来的博客文档位置,另外,数据库还需要额外开辟新的内存空间并复制原来的博客文档、更新所有索引,这需要更多的IO交互,可能会影响写性能

 必须注意的是,只有高写入流量的情况下才可能会影响写性能,而对于写入流量较小的程序反而没有那么大的影响。视具体情况而定。 3. 第三个问题是当你尝试去进行评论分页的时候,你会发觉通过常规的find查询操作,我们只能先读取整个文档信息(包括所有评论信息),然后在程序里进行评论信息的分页
  1. 连接

    第二个方式是通过建立类似外键的id来进行文档间的关联

    博客的文档设计 { _id: 1, title: "An awesome blog", url: "http://awesomeblog.com", text: "This is an awesome blog we have just started" } 评论的文档设计 { blog_entry_id: 1, name: "Peter Critic", created_on: ISODate("2014-01-01T10:01:22Z"), comment: "Awesome blog post" } { blog_entry_id: 1, name: "John Page", created_on: ISODate("2014-01-01T11:01:22Z"), comment: "Not so awesome blog" }
这样子设计模型有个好处是当评论信息逐渐增长的时候并不会影响原始的博客文档,从而避免了单个文档超过16MB的情况出现。而且这样子设计也比较容易返回分页评论。但是坏处的话,就是假设我们在一个博客文档下拥有非常多的评论时(比如1000条),那我们获取所有评论的时候会引起数据库很多的读操作
  1. 分块

    第三个方法就是前面两种方法的混合,理论上,尝试去平衡内嵌策略和连接模式,举个例子,我们可能会根据实际情况,把所有的评论切分成最多50条评论的分块

    博客的文档设计 { _id: 1, title: "An awesome blog", url: "http://awesomeblog.com", text: "This is an awesome blog we have just started" } 评论信息的文档设计 { blog_entry_id: 1, page: 1, count: 50, comments: [{ name: "Peter Critic", created_on: ISODate("2014-01-01T10:01:22Z"), comment: "Awesome blog post" }, ...] } { blog_entry_id: 1, page: 2, count: 1, comments: [{ name: "John Page", created_on: ISODate("2014-01-01T11:01:22Z"), comment: "Not so awesome blog" }] }

    这样子设计最大的好处是我们可以单次读操作里一次性抓出50条评论,方便我们进行评论分页

    什么时候使用分块策略? 当你可以将文档切割成不同的批次时,那么采用这种策略可以加速文档检索 典型的例子就是根据小时、天数或者数量进行评论分页(类似评论分页)

多对多关系模型

多对多关系模型,我们以作者跟创作的书籍来举例

在关系型数据库里,我们可以通过中间表的方式来处理

  1. 双向嵌套

    在MongoDB里我们可以通过双向嵌套,把两个文档的外键通过数组字段添加到彼此的文档里

    作者信息的文档设计 { _id: 1, name: "Peter Standford", books: [1, 2] } { _id: 2, name: "Georg Peterson", books: [2] } 书籍信息的文档设计 { _id: 1, title: "A tale of two people", categories: ["drama"], authors: [1, 2] } { _id: 2, title: "A tale of two space ships", categories: ["scifi"], authors: [1] }
当我们进行查询的时候,可以通过两个维度互相进行查询 通过指定的作者搜索对应的书籍 var db = db.getSisterDB("library"); var booksCollection = db.books; var authorsCollection = db.authors; var author = authorsCollection.findOne({name: "Peter Standford"}); var books = booksCollection.find({_id: {$in: author.books}}).toArray(); 通过指定的书籍搜索对应的作者 var db = db.getSisterDB("library"); var booksCollection = db.books; var authorsCollection = db.authors; var book = booksCollection.findOne({title: "A tale of two space ships"}); var authors = authorsCollection.find({_id: {$in: book.authors}}).toArray();
  1. 单向嵌套

    单向嵌套策略是用来优化多对多关系模型里的读性能,通过将双向引用转移为类似一对多的单向引用。这种策略是有特定场景的,比如在我们这个案例中,我们设计的作者信息文档里,将书籍信息作为数组字段嵌入作者文档,但是实际情况下,书籍的数量是会快速地增长,很可能会突破单个文档16MB的限制。

    在这个案例中,我们可以看到书籍数量是快速增长的,但是书籍分类确实比较固定,通常不会有太大改动,所以我们把书籍分类信息单独设计成文档,然后作者信息作为书籍信息的嵌入数组引用,书籍分类也作为嵌入数组引用。以相对变化不大的书籍分类作为主表,把相对变化较大的书籍信息作为从表,储存主表id作为外键。

    书籍分类的文档设计 { _id: 1, name: "drama" } 通过外键关联对应分类的书籍信息文档设计 { _id: 1, title: "A tale of two people", categories: [1], authors: [1, 2] }

    相对应的查询语句如下

    通过指定书籍来查找对应的书籍分类 var db = db.getSisterDB("library"); var booksCol = db.books; var categoriesCol = db.categories; var book = booksCol.findOne({title: "A tale of two space ships"}); var categories = categoriesCol.find({_id: {$in: book.categories}}).toArray();
 根据指定书籍分类来查找对应书籍 var db = db.getSisterDB("library"); var booksCollection = db.books; var categoriesCollection = db.categories; var category = categoriesCollection.findOne({name: "drama"}); var books = booksCollection.find({categories: category.id}).toArray(); 需要注意的地方: 保持关联关系的平衡 当多对多关系模型里,有一个模型数量级别特别大(比如最多可达500000个),另一个数量级别特别小(

1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的。

2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的。

3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的。

4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的。

5.阿里Java高级大牛直播讲解知识点,分享知识,

多年工作经验的梳理和总结,带着大家全面、

科学地建立自己的技术体系和技术认知!


以上是关于Mongodb架构设计浅谈的主要内容,如果未能解决你的问题,请参考以下文章

浅谈一下可扩展性网站架构设计

浅谈mongodb的使用场景及优缺点

浅谈HDFS写数据流程的核心架构设计(上)

架构浅谈之 MVC

浅谈APP架构设计

阿里架构师:浅谈大型项目前端架构设计