关于MongoDB的全局锁

MongoDB有所了解的人都知道,MongoDB有一个让人头疼的全局锁读写锁,允许并发读,而写会阻塞所有的读写),要命的是这个锁不是表级的,不是库级的,而是整个Server级别的,这让人听起来是不是非常的蛋疼。

在2.0版本以前,这一问题一直没有得到解决,于是有人提出,在可预见某个update操作的记录可能在磁盘上时,为了减少写锁占用的时间,可以采用先读后写的方式,通过先读一次,将要操作的记录加载到内存中,再进行内存中的update,这样写锁就不包括将数据从磁盘加载到内存的时间了。

在可预见数据冷热的情况下,这种操作能够有一定的效果,但是很明显,这种变态的方法不应该是一个终极解决方案。

值得庆幸的是,在2.0版本中,MongoDB宣称有很大程度的并发性能提升,而这一提升的基础正是解决了这个全局锁的问题。

解决的方法并不是通过减少锁粒度来解决,虽然collection级别的锁机制也正在开发中。(SERVER-1240

解决方法是通过对一些可能造成长时间锁占用的操作进行锁抑制。比如和我们上面的方法类似,在进行update操作时,如果发现需要更新的记录在磁盘上,那么这个锁就不会一直占用,而是等到将数据从磁盘加载到内存后再添加写锁进行update。

而同理,对于其它一些可能耗时比较长的操作也可以采用类似的方法,通过将长时间占用的全局锁拆分成多个细粒度的小锁来使需要获取锁来进行的操作能够交错的执行,从而避免一夫当关万夫莫开的情况,主要包括下面一些操作:

  • 查询操作
  • 批量更新操作
  • 批量删除操作
  • 批量insert写入操作

如果你还在使用2.0以前的版本,并且在并发性能上出现问题,可以考虑在2.0.x版本上进行一些性能测试并对你的MongoDB进行升级。

2.0版本中的改进听起来是可行的,然而他的实际效果如何呢,这里有两张性能测试图片,来自blog.pythonisito.com的测试。

第一张是在不触发page faulting的情况下1.8版本和2.0版本在加了写锁后的性能对比,可以看出,在写操作能够直接在内存中完成的情况下,二者的性能几乎没有差别。

第二张图是在写操作会触发page faulting的情况,也就是在1.8中,加写锁的时间会包括将数据从磁盘讲到内存的时间,而在2.0中不包括这段时间。可以看出2.0版本中情况好了很多,因为采用了锁抑制策略,使得产生page faulting后还能有较好并发性能。

参考:www.mongodb.org

anyShare分享此文章的同学,将有机会送我iphone5!
          

无觅相关文章插件,快速提升流量

    • 简单的说,就是原来是锁住后去磁盘读数据然后修改,2.0的改进是监测到数据在磁盘,就先从磁盘读出来后再加锁进行修改,所以锁的时间不包括从磁盘读数据的时间,锁时间少了,并发就高了。

  1. 我这有个update服务,一跑就会50%以上的锁,开始觉不到什么,后来就查询很慢了。是什么原因呢

    • 50%以上的锁那是很严重的问题了。你应该优化你的update操作。可能是你一次update的数据太多,如果你用的2.0版本以下,也可能是你的数据在磁盘上太多,导致update的时间包括了从磁盘读数据的时间。

  2. 从硬盘读数据时不加锁,那么并发的两个update有没有可能触发两次读数据,从而出现问题,比如用户A将 count = 2 update为count =3,用户B将count = 2 update为count =4.
    两个udpate同时到达时,有没有可能触发两个并发的读,从而导致磁盘中保留的是用户B更新后的数据(将用户A的数据覆盖了)