开发高性能的MongoDB应用—浅谈MongoDB性能优化 - 吴纹羽

标签: 开发 性能 mongodb | 发表时间:2014-12-16 14:30 | 作者:吴纹羽
出处:

关联文章索引:

大数据时代的数据存储,非关系型数据库MongoDB(一)

 

 

性能与用户量

 

  “如何能让软件拥有更高的性能?”,我想这是一个大部分开发者都思考过的问题。性能往往决定了一个软件的质量,如果你开发的是一个互联网产品,那么你的产品性能将更加受到考验,因为你面对的是广大的 互联网用户,他们可不是那么有耐心的。严重点说,页面的加载速度每增加一秒也许都会使你失去一部分用户,也就是说, 加载速度和用户量是成反比的。那么用户能够接受的加载速度到底是多少呢?

  

  如图,如果页面加载时间超过10s那么用户就会离开,如果1s--10s的话就需要有提示,但如果我们的页面没有提示的话需要多快的加载速度呢?是的, 1s

  当然,这是站在一个产品经理的角度来说的,但如果站在一个技术人员的角度来说呢? 加载速度和用户量就是成正比的,你的用户数量越多需要处理的数据当然也就越多,加载速度当然也就越慢。这是一件很有趣的事,所以如果你的产品如果是一件激动人心的产品,那么作为技术人员你需要做的事就是 让软件的性能和用户的数量同时增长,甚至性能增长要快于用户量的增长。

 

数据库性能对软件整体性能的影响是不言而喻的,那么,当我们使用MongoDB时改如何提高数据库性能呢?

 

1.范式化与反范式化

 

    在项目设计阶段,明确集合的用途是对性能调优非常重要的一步。

  从性能优化的角度来看,集合的设计我们需要考虑的是 集合中数据的常用操作,例如我们需要设计一个日志(log)集合,日志的查看频率不高,但写入频率却很高,那么我们就可以得到这个集合中常用的操作是 更新(增删改)。如果我们要保存的是城市列表呢?显而易见,这个集合是一个查看频率很高,但写入频率很低的集合,那么常用的操作就是 查询。

  对于频繁更新和频繁查询的集合,我们最需要关注的重点是他们的范式化程度,在上篇 范式化与反范式化的介绍中我们了解到,范式化与反范式化的合理运用对于性能的提高至关重要。然而这种设计的使用非常灵活,假设现在我们需要存储一篇图书及其作者,在MongoDB中的关联就可以体现为以下几种形式:

 

1.完全分离(范式化设计)

示例1:

{
"_id" : ObjectId("5124b5d86041c7dca81917"),
"title" : "如何使用MongoDB",
"author" : [
ObjectId("144b5d83041c7dca84416"),
ObjectId("144b5d83041c7dca84418"),
ObjectId("144b5d83041c7dca84420"),
]
}
View Code

  我们将 作者(comment) 的id数组作为一个字段添加到了 图书中去。这样的设计方式是在非关系型数据库中常用的,也就是我们所说的范式化设计。在MongoDB中我们将与主键没有直接关系的图书单独提取到另一个集合,用存储主键的方式进行关联查询。当我们要查询文章和评论时需要先查询到所需的文章,再从文章中获取评论id,最后用获得的完整的文章及其评论。在这种情况下查询性能显然是不理想的。但当某位作者的信息需要修改时,范式化的 维护优势就凸显出来了,我们无需考虑此作者关联的图书,直接进行修改此作者的字段即可。

 

2.完全内嵌(反范式化设计)

示例2:

{
"_id" : ObjectId("5124b5d86041c7dca81917"),
"title" : "如何使用MongoDB",
"author" : [
{
     "name" : "丁磊"
     "age" : 40,
    "nationality" : "china",
},
{
     "name" : "马云"
     "age" : 49,
     "nationality" : "china",
},
{
     "name" : "张召忠"
     "age" : 59,
     "nationality" : "china",
},
]
}
View Code

   在这个示例中我们将作者的字段完全嵌入到了图书中去,在查询的时候直接查询图书即可获得所对应作者的全部信息,但因一个作者可能有多本著作,当修改某位作者的信息时时,我们需要遍历所有图书以找到该作者,将其修改。

 

3.部分内嵌(折中方案)

示例3:

{
"_id" : ObjectId("5124b5d86041c7dca81917"),
"title" : "如何使用MongoDB",
"author" : [
{
    "_id" : ObjectId("144b5d83041c7dca84416"),
     "name" : "丁磊"
},
{
     "_id" : ObjectId("144b5d83041c7dca84418"),
     "name" : "马云"
},
{
     "_id" : ObjectId("144b5d83041c7dca84420"),
     "name" : "张召忠"
},
]
}
View Code

  这次我们将作者字段中的最常用的一部分提取出来。当我们只需要获得图书和作者名时,无需再次进入作者集合进行查询,仅在图书集合查询即可获得。

  这种方式是一种相对折中的方式,既保证了查询效率,也保证的更新效率。但这样的方式显然要比前两种较难以掌握,难点在于需要与 实际业务进行结合来寻找合适的提取字段。如同示例3所述,名字显然不是一个经常修改的字段,这样的字段如果提取出来是没问题的,但如果提取出来的字段是一个经常修改的字段(比如age)的话,我们依旧在更新这个字段时需要大范围的寻找并依此进行更新。

 

  在上面三个示例中,第一个示例的更新效率是最高的,但查询效率是最低的,而第二个示例的查询效率最高,但更新效率最低。所以在实际的工作中我们需要根据自己实际的需要来设计表中的字段,以获得最高的效率。

 

2.理解填充因子

  何为填充因子?

   填充因子(padding factor)是MongoDB为文档的扩展而预留的增长空间,因为MongoDB的文档是以顺序表的方式存储的,每个文档之间会非常紧凑,如图所示。

  (注:图片出处:《MongoDB The Definitive Guide》)

  

  1.元素之间没有多余的可增长空间。

  

  2.当我们对顺序表中某个元素的大小进行增长的时候,就会导致原来分配的空间不足,只能要求其向后移动。

  

  3.当修改元素移动后,后续插入的文档都会提供一定的填充因子,以便于文档频繁的修改,如果没有不再有文档因增大而移动的话,后续插入的文档的填充因子会依此减小。

 

  填充因子的理解之所以重要,是因为文档的移动非常消耗性能,频繁的移动会大大增加系统的负担,在实际开发中最有可能会让文档体积变大的因素是数组,所以如果我们的文档会频繁修改并增大空间的话,则一定要充分考虑填充因子。

  那么如果我们的文档是个常常会扩展的话,应该如何提高性能?

 

  两种方案

 

   1.增加初始分配空间。在集合的属性中包含一个  usePowerOf2Sizes 属性,当这个选项为 true时,系统会将 后续插入的文档,初始空间都分配为2的幂数。

  这种分配机制适用于一个数据会频繁变更的集合使用,他会给每个文档留有更大的空间,但因此空间的分配不会像原来那样高效,如果你的集合在更新时不会频繁的出现移动现象,这种分配方式会导致写入速度相对变慢。

 

   2.我们可以利用数据强行将初始分配空间扩大。

1 db.book.insert({
2 "name" : "MongoDB",
3 "publishing" : "清华大学出版社",
4 "author" : "john"
5 "tags" : []
6 "stuff" : "ggggggggggggggggggggggggggggggggggggg
7 ggggggggggggggggggggggggggggggggggggg
8 ggggggggggggggggggggggggggggggggggggg"
9 })

  是的,这样看起来可能不太优雅...但有时却很有效!当我们对这个文档进行增长式修改时,只要将stuff字段删掉即可。当然,这个stuff字段随便你怎么起名,包括里边的填充字符当然也是可以随意添加的。

  

3.准确利用索引

   索引对于一个数据库的影响相信大家一定了解,如果一个查询命令进入到数据库中后,查询优化器没有找到合适的索引,那么数据库会进行 全集合扫描(在RDBMS中也叫全表扫描),全集合查询对于性能的影响是灾难性的。没有索引的查询就如同在词典那毫无规律的海量词汇中获得某个你想要的词汇,但这个词典是没有目录的,只能通过逐页来查找。这样的查找可能会让你耗费几个小时的时间,但如果要求你查询词汇的频率如同用户访问的频率一样的话。。。嘿嘿,我相信你一定会大喊“老子不干了!”。显然计算机不会这样喊,它一直是一个勤勤恳恳的员工,不论多么苛刻的请求他都会完成。所以请通过索引善待你的计算机:D。

 

  在MongoDB中索引的类型与RDBMS中大体一致,我们不做过多重复,我们来看一下在MongoDB中如何才能更高效的利用索引。

  

 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}",依旧不会影响性能。

 

2.索引列颗粒越小越好

  什么叫颗粒越小越好?在索引列中每个数据的重复数量称为颗粒,也叫作索引的 基数。如果数据的颗粒过大,索引就无法发挥该有的性能。例如,我们拥有一个"age"列索引,如果在"age"列中,20岁占了50%,如果现在要查询一个20岁,名叫"Tom"的人,我们则需要在表的50%的数据中查询,索引的作用大大降低。所以,我们在建立索引时要尽量将数据颗粒小的列放在索引左侧,以保证索引发挥最大的作用。

 

---------------------------------------------------------------------

ps:如果我的博文对您有帮助的话,请点击右下角的推荐,这是对我最大的鼓励,谢谢谢谢!

  

 


本文链接: 开发高性能的MongoDB应用—浅谈MongoDB性能优化,转载请注明。

相关 [开发 性能 mongodb] 推荐:

开发高性能的MongoDB应用—浅谈MongoDB性能优化 - 吴纹羽

- - 博客园_首页
大数据时代的数据存储,非关系型数据库MongoDB(一).   “如何能让软件拥有更高的性能. ”,我想这是一个大部分开发者都思考过的问题. 性能往往决定了一个软件的质量,如果你开发的是一个互联网产品,那么你的产品性能将更加受到考验,因为你面对的是广大的 互联网用户,他们可不是那么有耐心的. 严重点说,页面的加载速度每增加一秒也许都会使你失去一部分用户,也就是说, 加载速度和用户量是成反比的.

mongodb性能测试

- - 数据库 - ITeye博客
1) Mongodb的非安全插入方式,在一开始插入性能是非常高的,但是在达到了两千万条数据之后性能骤减,这个时候恰巧是服务器24G内存基本占满的时候(随着测试的进行mongodb不断占据内存,一直到操作系统的内存全部占满),也就是说Mongodb的内存映射方式,使得数据全部在内存中的时候速度飞快,当部分数据需要换出到磁盘上之后,性能下降很厉害.

记一次MongoDB性能问题

- Fstone - 火丁笔记
最近忙着把一个项目从MySQL迁移到MongoDB,在导入旧数据的过程中,遇到了些许波折,犯了不少错误,但同时也学到了不少知识,遂记录下来. 公司为这个项目专门配备了几台高性能务器,清一色的双路四核超线程CPU,外加32G内存,运维人员安装好MongoDB后,就交我手里了,我习惯于在使用新服务器前先看看相关日志,了解一下基本情况,当我浏览MongoDB日志时,发现一些警告信息:.

mongodb索引讲解与性能调优

- - haohtml's blog
mongodb索引规则基本上与传统的关系库一样,大部分优化MySQL/Oracle/SQLite索引的技巧也适用于mongodb. 当查询中用到某些条件时,可以对该键建立索引,以提高查询速度. 如果数据量很多且查询多于更新时,可以用索引提高查询的速度. a)         查询索引:. 查询索引很简单,比如说需要查询mailaccess数据库中的Mail collection上的索引时:.

Cassandra HBase和MongoDb性能比较

- - 数据库 - ITeye博客
这是一篇基于亚马逊云平台上对三个主流的. NoSQL数据库性能比较,在读写两个操作不同的组合情况下性能表现不同. 横坐标是吞吐量,纵坐标是延迟,这是一对矛盾,吞吐量越大,延迟越低,代表越好. 纯粹插入,Cassandra领先,见下图:. 2.WorkloadA: 读修改操作各占一半情况下的修改性能:MongoDB明显延迟增加,落败:.

[Cacti] mongodb性能监控实战

- - CSDN博客数据库推荐文章
          为了更好的使用mongodb,需要监控出mongodb的一些基础使用情况,比如Flush数、连接数、内存使用率、Index操作,Slave延迟等等,这些可以通过配置cacti监控mongodb的模板来完成. 1,在cacti界面导入模板 在计算机本地,下载此tgz包:http://mysql-cacti-templates.googlecode.com/files/better-cacti-templates-1.1.8.tar.gz.

记一次MongoDB性能问题,附原理解析

- zffl - NoSQLFan
下面文章转载自火丁笔记,原作者描述了一次MongoDB数据迁移过程中遇到的性能问题及其解决方案,中间追查问题的方法和工具值得我们学习. 另外NoSQLFan还对作者略讲的问题产生原理进行了分析,希望对您有用. 最近忙着把一个项目从MySQL迁移到MongoDB,在导入旧数据的过程中,遇到了些许波折,犯了不少错误,但同时也学到了不少知识,遂记录下来.

Mongodb亿级数据量的性能测试

- - haohtml's blog
进行了一下Mongodb亿级数据量的性能测试,分别测试如下几个项目:.  (所有插入都是单线程进行,所有读取都是多线程进行). 1) 普通插入性能 (插入的数据每条大约在1KB左右). 2) 批量插入性能 (使用的是官方C#客户端的InsertBatch),这个测的是批量插入性能能有多少提高. 3) 安全插入功能 (确保插入成功,使用的是SafeMode.True开关),这个测的是安全插入性能会差多少.

MongoDB MapReduce 性能提升20倍的优化宝典

- - 数据库 - ITeye博客
自从MongoDB被越来越多的大型关键项目采用后,数据分析也成为了越来越重要的话题. 人们似乎已经厌倦了使用不同的软件来进行分析(这都利用到了Hadoop),因为这些方法往往需要大规模的数据传输,而这些成本相当昂贵. MongoDB提供了2种方式来对数据进行分析: Map Reduce(以下简称MR)和聚合框架(Aggregation Framework).

大偏移量下Redis、MongoDB分页/排名性能比较

- - CSDN博客数据库推荐文章
题目其实并不太准确,因为数据库并不会提供 分页、排名等功能,提供的只是数据的存取,分页排名这些都是我们基于数据库的实用案例而已. 然而无论是 Redis还是 MongoDB,通常都有一些常规的做分页和排名的方法. 本文就通过一些测试数据来向大家介绍Redis和MongoDB(以及传统关系型数据库)在这方面的性能差别.