设为首页 收藏本站
查看: 882|回复: 0

[经验分享] MongoDB循序渐进之[特性]——面向文档存储

[复制链接]

尚未签到

发表于 2015-7-7 09:03:58 | 显示全部楼层 |阅读模式
  
  
Document-Oriented Storage
JSON-style documents with dynamic schemas offer simplicity and power.

  
面向文档存储
  JSON风格的文件与动态模式使MongoDB简单而强大。

  
  Schema Design:模式设计

  
  
  
  
     在MongoDB中Schema的设计是非常不同于传统的DBMS。然而Schema是非常重要的,并且是建立应用程序的第一步。
  在传统的数据模型中,给一个实体关系模型一个独立的用例在概念上是正确的,这是一个很正常的第三范式,但这通常会偏离你处于性能方面的考虑。在MongoDB中,Schema的设计不仅仅是对数据进行建模的用例。根据最常见的用例,我们对Schema的设计进行了优化,这有利有弊——用例通常是高性能的。然而有一个偏见是说Schema可能使某些动态查询相比于关系模型缺少一点优雅。
  当我们要设计Schema时,需要考虑以下问题:
  1.什么时候我们嵌入数据和链接(见下文)?我们在这里的决定讲影响第二个问题的答案
  2.我们有多少集合,它们是什么?
  3.什么时候我们需要原子操作?这些操作可以执行范围内的BSON文档,但并不是所有文档。   
  4.我们将创建什么索引使查询和更新快?
  5.我们如何切分?什么是分片键?
  
  

  Embedding and Linking:嵌入和链接
     在设计一个MongoDB Schema时一个关键问题是什么时候嵌入,什么时候链接。嵌入是嵌套对象和数组到BSON文档中,链接是文档之间的引用。   
  在MongoDB中没有join——在1000服务器集群中做分布式join是很困难的。嵌入有点像“prejoined”(预连接)数据。
  服务器处理在一个文档里面的操作是很容易的,这些操作可以相当丰富。链接相比之下必须处理客户端应用程序,应用程序是通过发行一个后续查询来处理文档。
  一般来说,实体之间有“包含”关系,则应该选择嵌入。当不使用连接会导致重复的数据,那么就选择使用链接。
  
  

  Collections:集合
     在MongoDB中集合类似于关系数据库中的表,每一个集合包含文档,正如上面提到的这些文件可以相当丰富。在一个集合文档内字段是没有显式声明。然而来自于Schema设计师的一个关于那些字段将会是什么的概念,并且文档在集合内是怎样被结构化的。MongoDB不需要集合内的文档有相同的结构,然而在实践中大多数集合都是高度同质的。只要我们愿意我们就可以避免这些,例如当添加一个新字段,在这种情况一个“alter table”风格操作不是必要的。
  
  

  Atomic Operations:原子操作
     有些问题需要能够执行原子操作。例如,简单地增加计数器一个需要的原子性操作的案例。MongoDB还可以执行更复杂的操作,如下面所示的伪代码:  



atomically {
if( doc.credits > 5 ) {
doc.credits -= 5;
doc.debits += 5;
}
}
  另一个例子是一个用户注册的场景,我们永远不会想要两个用户注册相同的用户名同时:



atomically {
if( exists a document with username='jane' ) {
print "username already in use please choose another";
} else {
insert a document with username='jane' in the users collection;
print("thanks you have registered as user jane.");
}
}
  Schema设计的关键点是我们的范围的原子性/ ACID属性就是文档。
  
  

  Indexes:索引
     MongoDB支持声明的索引。在MongoDB中索引非常类似于关系数据库中的索引:它们都需要有效的查询处理,必须显式声明。因此我们需要考虑我们将定义哪些索引作为Schema的一部分设计过程。就像在关系数据库,索引可以在以后添加——如果我们决定以后有一个新的索引,我们可以这样做。
  

  Sharding:分片
     另一个考虑事项是分片模式设计。一个BSON文档(可能有大量的嵌入)驻留在一个且只有一个分片中。
  一个集合可能包含分片。当分片时,集合有一个分片键,这决定了集合是如何在分片中分割的。通常(但不总是)查询一个分片集合包含作为查询表达式的一部分的分片键。
  这里的关键是,改变分片键是困难的,从一开始你就会想要选择正确的键
  
  

  Example:示例
     让我们考虑一个例子,这是一个内容管理系统。下面的例子使用mongo shell语法,当然也可以使用其他任何编程语言来做——只要选择语言的相关驱动就可以。
  我们的内容管理系统会有新帖子,文章的作者。我们想支持评论和投票帖子,我们也将提供对帖子的索引查询。   
  对于这个场景一个比较好的schema设计是建立两个MongoDB集合,一个是帖子集合另一个是用户集合,这就是我们将使用的示例。
  我们的用户有几个属性——一个用户id注册的、他们的真实姓名、和他们的报酬,例如我们可以执行以下命令:



>db.users.insert( { _id : "alex", name: { first:"Alex", last:"Benisson" }, karma : 1.0 } )
     这个_id字段总是存在MongoDB中,是用一种独特的automically索引键约束。这是适合我们的用户名,所以我们将它们存储在_id字段。我们不需要,但我们可以设置一个独立的用户名字段,并让系统automically生成一个惟一的id。
  现在让我们考虑帖子,我们将假设一些帖子已经填充,让我们查询一个:



> db.posts.findOne()
{
_id : ObjectId("4e77bb3b8a3e000000004f7a"),
when : Date("2011-09-19T02:10:11.3Z",
author : "alex",
title : "No Free Lunch",
text : "This is the text of the post.  It could be very long.",
tags : [ "business", "ramblings" ],
votes : 5,
voters : [ "jane", "joe", "spencer", "phyllis", "li" ],
comments : [
{ who : "jane", when : Date("2011-09-19T04:00:10.112Z"),
comment : "I agree." },
{ who : "meghan", when : Date("2011-09-20T14:36:06.958Z"),
comment : "You must be joking.  etc etc ..." }
]
}
     有趣的是,与之形成对比的是,我们如何在一个关系数据库设计相同的schema。我们可能会有一个用户集合和一个帖子集合,但是另外通常会有一个标签集合,一个投票集合和评论集合。
     我们可以用一个简单的语句在一次请求中查询所有的信息,这里查询整个帖子我们可以执行下面的语句:   



> db.posts.findOne( { _id : ObjectId("4e77bb3b8a3e000000004f7a") } );
  如果要查询所有的帖子,我们可以按照下面的写法:



> db.posts.find( { author : "alex" } )
  如果上面的是一种常见的查询,我们将创建一个索引在author字段上:   



> db.posts.ensureIndex( { author : 1 } )
  文档可以是非常大的,根据标题来查询帖子,可以参照下面的语句:



> db.posts.find( { author : "alex" }, { title : 1 } )
  或者,我们可能会按照帖子的标签来查询相关贴子:



> // make and index of all tags so that the query is fast:
   > db.posts.ensureIndex( { tags : 1 } )
   > db.posts.find( { tags : "business" } )
  假如我们想找到所有 meghan 写的帖子呢?



> db.posts.find( { comments.who : "meghan" } )
  加上索引使查询速度更快:



> db.posts.ensureIndex( { "comments.who" : 1 } )
     我们跟踪上面的投票人,不允许一个人对一个帖子投两次票。假设 calvin 想给上面示例中的帖子投票,以下更新操作将记录加尔文的投票。因为$ nin子表达式来检测作用,所以如果卡尔文已投票,更新将不会有效。



> db.posts.update( { _id : ObjectId("4e77bb3b8a3e000000004f7a"),
voters : { $nin : "calvin" } },
{ votes : { $inc : 1 }, voters : { $push : "calvin" } );
  注意:上面的操作是原子的,如果多个用户同时投票,投票依然不会丢失。
     假设我们想在系统中显示最新的文章的标题以及作者完整的用户名,在这种情况下,我们必须使用客户端连接:



> var post = db.posts.find().sort( { when : -1 } ).limit(1);
   > var user = db.users.find( { _id : post.author } );
   > print( post.title + " " + user.name.first + " " + user.name.last );
  
     最后一个问题,我们可能会问关于我们的例子我们是如何分片的。如果用户的集合是比较小的,那我们就不需要切分它,如果文章是巨大的,我们切分它。我们需要选择一个分片键,分片键的选择应该以常见的查询为基础。
  1.是否按照 _id 切分是可选的。
  2.如果发现[最近的帖子]是一个非常频繁的查询,然后我们会在 when 这个字段上做切分。(还有一个优化技巧可能在这里)
  Summary of Best Practices:最佳实践的摘要
  1.顶级对象的特点是它们有自己的集合(比如帖子对象就是顶级对象,标签就是次级对象)。
  2.排列项的详情对象的特点是嵌入的(比如帖子的标签属性对象是嵌入在帖子对象里面的)。
  3.如果对象之间符合包含关系的模型,那么通常应该被嵌入。
  4.多对多关系通常是做链接(例如帖子和投票者)。
  5.只有几个对象集合可能安全地作为单独的集合存在,因为整个集合可以快速被缓存在应用程序服务器中。
  6.相比于顶级对象,嵌入的对象比较难以获取。
  7.当需要一个用到MongoDB的map/reduce工具的操作的时候,得到嵌入对象的系统基本的视图是非常困难的(不解)。
  8.如果嵌入的对象的数据量是巨大的(N兆字节),你可能会达到一个对象大小的极限。参见网格文件系统
  9.如果性能是一个问题,那么选择嵌入。
  

  More Details:更多细节
  选择索引
     Schema设计的第二个方面是索引的选择。作为一般规则,像在关系数据库中需要索引一样,MongoDB也同样需要索引。
  1._id字段是自动索引。
  2.查询频率比较高的字段上也需要索引。
  3.排序字段通常应该被索引。
     MongoDB分析工具提供了比较有用的信息,关于哪里缺少索引需要添加。
  注意,添加一个索引放缓写到一个集合,但不读。为集合使用大量高读写比率的索引(假设你不介意存储超额)。对于写多于读的集合,索引是非常重要的键必须对每一次插入添加索引(不解)。
  
  有多少集合?
     因为Mongo集合是多态的,所以你只要有一个集合对象,那么你可以把所有东西都放进去!这种方法被一些对象数据库所吸引。在MongoDB中这是不推荐,有几个原因,主要在于性能。 在单个集合里的数据数据大部分是连续的磁盘上,因此,一个集合的“表扫描”是可能的,有效的,就像在关系数据库中,独立的集合是非常重要对于高吞吐量的批处理。
  

  See Also:参见
  书籍
1.
MongoDB文档设计 - O'Reilly Ebook
2.
更多书籍
  
  博客文章
http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling
Blog post on dynamic schemas

  相关文档页面
MongoDB中的树
MongoDB数据建模和Rails
高级的查询
  
  视频
模式设计基础——MongoSF演示(2011年5月)
模式设计规模——MongoSF演示(2011年5月)
模式设计:文档型数据——MongoSV演示(2010年12月)
  
  
  Tip:本人的英语不好,大部分地方算是直译吧,所以比较差,只是想抛砖引玉,感兴趣的童鞋可以看看原文。本人读完了解的也只是皮毛,仅供参考,更详细内容请参考官网页面:http://www.mongodb.org/display/DOCS/Schema+Design
                                   本页翻译完毕——于:2012年11月8日

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-83985-1-1.html 上篇帖子: PHP Mongodb API 及使用 下篇帖子: mongodb 学习
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表