mongoDB优化浅谈1

1.理解填充因子

 何为填充因子?

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

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

  

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

  

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

  

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

 

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

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

 

  两种方案

 

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

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

 

  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字段随便你怎么起名,包括里边的填充字符当然也是可以随意添加的。

  

2.准确利用索引

   索引对于一个数据库的影响相信大家一定了解,如果一个查询命令进入到数据库中后,查询优化器没有找到合适的索引,那么数据库会进行全集合扫描(在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%的数据中查询,索引的作用大大降低。所以,我们在建立索引时要尽量将数据颗粒小的列放在索引左侧,以保证索引发挥最大的作用。

 

 

 

 

3. 写操作频繁无法使用读写分离

由于系统写操作较多,造成各种w级别锁经常出现(这种锁一般是block read的)而且系统对于数据一致性要求不会太多(大多是后台写入,前台读取,因此允许有一定延迟)所以想用副本集来做读写分离。当真正测试后发现副本集上的读取也经常出现阻塞的情况。通过db.currentOp()发现经常出现一个op:none的操作在申请global write lock,这时所有操作的状态都是在waitingForLock:true,这个问题google了很久都没找到解决方法。后面在官方文档有关并发的FAQ中发现下面这个大坑:

How does concurrency affect secondaries?

In replication, MongoDB does not apply writes serially to secondaries. 
Secondaries collect oplog entries in batches and then apply those 
batches in parallel. Secondaries do not allow reads while applying the 
write operations, and apply write operations in the order that they 
appear in the oplog.

原来mongodb的副本在复制主节点数据执行oplog的时候,读取是被阻塞的,这基本宣告无法在副本上去读取数据了,白白耗费了几天精力。所以mongo官方不推荐做读写分离,原来坑是在这里。。。其实写多读少的情况做读写分离作用也不大,因为性能瓶颈主要是在写入,读取一般不消耗多少资源(另外wiredTiger引擎的锁做到了doc级别,所以锁的情况相对较少)。官方推荐的做法是shard,可以有效的将写入分配到多台服务器提高写入速度,使系统实现水平扩容。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值