1.为什么选择MongoDB?1.性能在大数据时代,处理大量数据成为考虑数据库的最重要原因之一。MongoDB的主要目标之一就是尽可能保持数据库的优良性能,这在很大程度上决定了MongoDB的设计。在传统机械硬盘占主导地位的时代,硬盘很可能成为性能的短板,但MongoDB选择最大限度地利用内存资源作为缓存来换取卓越的性能,并自动选择最快的索引进行查询。MongoDB尽可能简化数据库,将尽可能多的操作交给客户端。这种做法是MongoDB能够保持出色性能的原因之一。2.膨胀现在互联网上的数据量已经从过去的MB、GB发展到现在的TB级别。单一的数据库显然是难以承受的,可扩展性成为了一个重要的话题。然而,现在开发者在选择扩展方式时经常会犯错误。难点,到底是选择横向扩展还是纵向扩展?横向扩展(scaleout)就是把数据库分成不同的块,通过增加分区的方式分布到不同的机器上。这样做的好处是扩建成本低,但管理难度大。垂直扩展(scaleup)垂直扩展和水平扩展的区别在于,他会将原有的服务器进行升级,使其拥有更强大的计算能力。这样做的好处是无需考虑扩容带来的诸多问题,便于管理,但缺点也很明显,那就是成本高。大型机的价格往往非常昂贵,这样的升级可能在数据达到极限时找不到更强大的机器。MongoDB选择了更经济的横向扩展,可以方便的将数据拆分到不同的服务器上。而且,开发者在获取数据时不需要考虑多台服务器带来的问题。MongoDB可以自动将开发者的请求路由到正确的服务器上,让开发者摆脱水平扩展带来的劣势,更加专注于程序开发。.3、使用MongoDB采用NoSQL的设计方式,可以更加灵活的操作数据。在传统的RDBMS中,你一定遇到过几十行甚至上百行的复杂SQL语句。传统RDBMS的SQL语句包含大量的关联、子查询等语句,增加了复杂度,并允许进行性能调整。优化变得更加困难。MongoDB的面向文档的设计使用更灵活的文档作为数据模型来替换RDBMS中的行。面向文档的设计让开发者更灵活的获取数据,即使只有一条语句也可以查询复杂的嵌套关系,让开发者不用为了获取数据而绞尽脑汁。二、NoSQL对传统数据库设计思维的影响1、预设计模式和动态模式在传统的数据库设计思维中,项目的设计阶段需要指定数据库表中的字段名称和字段类型。如果你试图插入一些不符合设计的数据,数据库将不会接受这个数据以保证数据的完整性。--数据库字段:NAME,SONGINSERTINTOT_INFOVALUES('John','ComeTogether');--成功INSERTINTOT_INFOVALUES('Xiaoming',20,'xiaoming@111.com');--失败NoSQL使用集合(类似于"文档(类似于表中的“行”))是动态追加的,数据类型在集合创建之初就没有限制。任何文档都可以附加到任何集合。比如我们可以像这样把两个文档加入到一个集合中:{"name":"John","song":"ComeTogether"}{"name":"Xiaoming","age":"20","email":"xiaoming@111.com"}MongoDB中文档的格式类似于我们常见的JSON,所以我们可以看到第一个有name和song两个字段,第二个有name","age"和"email"三个字段,在预先设计好的模式下是不可能在数据库中插入成功的,但是在MongoDB的动态模式下是可以的。一个表,他们可以单独存储在一个表中,但是这样做的缺点是很明显的。我们在获取数据的时候,需要区分同一张表的不同文档,增加了开发的代码量。因此,在设计之初,需要权衡动态模式的优劣来选择表中的数据类型。2.规范化和非规范化规范化是关系模型的发明者EdgarCodd在1970年提出的一个概念。规范化会将数据分散到不同的表中,并使用关系模型进行关联,由此带来的好处是,当修改在后期,与其关联的数据不受影响,只需要修改自身即可完成。非规范化与规范化相反。反规范化会将当前文档的数据存储在这张表中,而不是拆分。规范化和反规范化之间不存在优劣的问题。规范化的好处是在我们写、修改、删除的时候可以提供更高的性能,反规范化可以提高我们的查询性能。当然NoSQL中没有关联查询来提高查询性能,但是我们还是可以通过在表中存储关联表的ID来进行归一化。但由此可见,反范式在NoSQL概念中的地位要大于规范化。3.性能和用户数“如何让软件有更高的性能?”我想这是大多数开发者都思考过的问题。性能往往决定了软件的质量。如果你开发的是互联网产品,那么你的产品的性能会受到更多的考验,因为你面对的是大量的互联网用户,他们没有那么耐心。认真的说,页面加载速度每提高一秒,都可能让你失去一些用户,也就是说,加载速度与用户数量成反比。那么用户可以接受的加载速度是多少呢? 如图所示,如果页面加载时间超过10s,用户将离开。如果需要1s--10s,需要提示,但是如果我们的页面没有提示,加载速度应该多快呢?是的,1s。当然,这是站在产品经理的角度,那么站在技术人员的角度呢?加载速度与用户数量成正比。您拥有的用户越多,需要处理的数据就越多,加载速度就越慢。这是一件很有趣的事情,所以如果你的产品是一个令人兴奋的产品,那么作为技术人员需要做的就是让软件的性能随着用户数量的增加而同步增长,甚至性能增长是快于用户增长。数据库性能对软件整体性能的影响是不言而喻的,那么我们在使用MongoDB时如何提高数据库性能呢?4.规范化和反规范化在项目设计阶段,明确集合的目的是性能调优非常重要的一步。从性能优化的角度,我们需要考虑集合中数据的通用操作。比如我们需要设计一个日志(log)集合。查看日志的频率不高,但是写的频率很高。那么我们可以得到这个集合中常用的操作是更新(增删改查)。如果我们想保存一个城市列表怎么办?很明显,这个合集是一个浏览频率高写频率低的合集,所以常用的操作就是查询。对于频繁更新和频繁查询的集合,我们最需要关注的是它们的规范化程度。在上一篇关于规范化和反规范化的介绍中,我们了解到规范化和反规范化的合理使用对性能有很大的影响。改进至关重要。但是,这种设计的使用非常灵活。假设现在我们需要存储一本书和它的作者,MongoDB中的关联可以通过以下形式体现:1.完全分离(规范化设计)示例1:{"_id":ObjectId("5124b5d86041c7dca81917"),"title":"如何使用MongoDB","author":[ObjectId("144b5d83041c7dca84416"),ObjectId("144b5d83041c7dca84418"),Object]Id("144b5d83041c20dca84418"),Object]("144b5d83041c20dca84418"),4)id数组作为字段添加到书中。这种设计方法常用于非关系型数据库,也就是我们所说的范式设计。在MongoDB中,我们将与主键不直接相关的书籍提取到另一个集合中,通过存储主键的方式进行关联查询。当我们要查询文章和评论时,首先需要查询需要的文章,然后从文章中获取评论id,最后使用获取到的完整文章及其评论。这种情况下的查询性能显然不理想。但是当作者信息需要修改时,常态化维护的优势就凸显出来了。我们不需要考虑这个作者关联的书籍,直接修改作者的字段即可。2、全嵌入(反规范化设计)例2:{"_id":ObjectId("5124b5d86041c7dca81917"),"title":"MongoDB使用方法","author":[{ "name":"丁磊" "年龄":40, "国籍":"中国",},{ "姓名":"马云" "年龄":49, "nationality":"china",},{ "name":"ZhangZhaozhong" "age":59, "nationality":"china",},]}在这个例子中我们将使用作者的领域完全嵌入书中。查询图书时,可以直接查询图书,得到对应作者的所有信息。但是由于一个作者可能有多本书,所以在修改一个作者的信息时,我们需要遍历所有的书。找到作者,修改。3.部分嵌入(折中方案)例3:{"_id":ObjectId("5124b5d86041c7dca81917"),"title":"MongoDB使用方法","author":[{ "_id":ObjectId("144b5d83041c7dca84416"), "name":"丁磊"},{ "_id":ObjectId("144b5d83041c7dca84418"), "name":"马云"},{ "_id":ObjectId("144b5d83041c7dca84420"), "name":"张肇中"},]}这次提取作者字段中最常用的部分。当我们只需要获取书名和作者时,不需要再次进入作者集合进行查询,只需要查询图书集合即可获取。这种方式是一种比较折中的方式,既保证了查询效率,又保证了较高的更新效率。但是,这种方法显然比前两种方法更难掌握。难点在于需要结合实际业务寻找合适的提取领域。如示例3中所述,名称显然不是经常修改的字段。如果抽取这样一个字段是没有问题的,但是如果抽取的字段是一个经常修改的字段(比如年龄),我们还在更新这个字段的时候需要大量的搜索和相应的更新。以上三个例子中,第一个例子的更新效率最高,但查询效率最低,而第二个例子的查询效率最高,但更新效率最低。因此,在实际工作中,我们需要根据自己的实际需要来设计表中的字段,以获得最高的效率。5.了解填充因子什么是填充因子?paddingfactor是MongoDB为文档扩展预留的增长空间,因为MongoDB的文档是按顺序表存储的,每个文档都会非常紧凑,如图。(注:图片来源:《MongoDB The Definitive Guide》) 1.元素之间没有额外的增长空间。2、当我们增加序列表中的一个元素的大小时,原来分配的空间会不够用,只能要求它向后移动。3、修改元素移动后,后续插入的文档会提供一定的填充因子,方便文档的频繁修改。如果没有文件因为增加而移动,那么后面插入的文件的填充因子会相应的变小。之所以对填充因子的理解很重要,是因为文档的移动非常消耗性能,频繁的移动会大大增加系统的负担。在实际开发中,最有可能会增加文档大小的因素就是数组,所以如果我们的文档会频繁修改和增加空间的话,那么一定要充分考虑填充因子。那么如果我们的文档经常被扩展,我们应该如何提高性能呢?两种选择1.增加初始分配的空间。usePowerOf2Sizes属性包含在集合的属性中。当该选项为true时,系统会将后续插入文档的初始空间分配为2的N次方。这种分配机制适用于数据变化频繁的集合。它将为每个文档留出更多空间,但因此空间分配不会像以前那样有效。如果你的收藏不是经常更新,这种分配方式会导致写入速度比较慢。2.我们可以用数据强行扩大初始分配的空间。db.book.insert({{“name”:“mongodb”,“publishing”:“清华清华出版社”,“作者”:“John”“tags”:[],这可能看起来不是很优雅......但它有时会起作用!当我们对这个文档进行增量修改时,只需要删除stuff字段即可。当然这个stuff字段可以随便命名,包括里面的padding字符,当然你也可以随意加。 6.索引的正确使用索引对数据库的影响相信大家都知道。如果查询命令进入数据库,查询优化器没有找到合适的索引,那么数据库就会进行全集扫描(在RDBMS中也称为全表扫描),全集查询对性能的影响是灾难性的。没有索引的查询就像是在字典的大量不规则词中获取你想要的某个词,但是这本字典没有目录,只能一页页地查找。这样的查找可能会花费您几个小时,但如果您需要像用户访问它们一样频繁地查找术语。..嘿,我相信你会大喊“我不干了!”。显然电脑不会这么喊,它一直是个勤奋的员工,不管多么苛刻的要求,他都会完成。因此,请通过索引善待您的计算机:D。MongoDB中的索引类型与RDBMS中的索引类型大致相同。让我们不要重复太多。让我们来看看如何在MongoDB中更高效地使用索引。6.1索引越少越好索引可以大大提高查询性能,那么索引是不是越多越好呢?答案是否定的,并不是索引越多越好,而是索引越少越好。每当您创建索引时,系统都会为您添加一个索引表来索引指定的列。但是,当插入或修改索引列时,数据库需要对原索引表进行重新排序,重新排序的过程会消耗大量的性能,但是处理少量索引压力不是太大,但是如果数量索引量很大,对性能的影响可想而知。因此,在创建索引时,一定要慎重构建索引,充分发挥各个索引的作用。也就是说,索引数量越少,越能满足索引要求。隐式索引//创建复合索引db.test.ensureIndex({"age":1,"no":1,"name":1})我们可以在查询的时候快速对age和no字段进行排序,隐式的公式索引意思是如果我们要排序的字段包含在建立的复合索引中,就不需要重复索引。db.test.find().sort("age":1,"no":1)db.test.find().sort("age":1)对于以上两个排序查询,可以使用上面的复合索引无需重新索引。翻转索引//创建复合索引db.test.ensureIndex({"age":1})翻转索引很容易理解,就是我们在查询排序的时候不需要考虑索引列的方向.例如,在这个例子中,我们可以将排序条件写成“{'age':0}”,这样仍然不会影响性能。6.2指标栏目颗粒越小越好。颗粒越小越好是什么意思?索引列中每条数据的重复次数称为一个粒度,也称为索引的基数。如果数据的粒度太大,索引就不能发挥应有的作用。例如,我们在“age”列上有一个索引。如果在“年龄”这一列中,20岁占50%,如果我们要查询一个叫“汤姆”的20岁的人,我们需要50%的表在数据的查询中,作用指数大大降低。因此,在建立索引时,我们应该尽量将数据颗粒小的列放在索引的左侧,以保证索引能够发挥最大的作用。
