MongoDB的问题
发布日期:2016-4-20 18:4:35
比起mysql,MongoDB 正是目前炙手可热的 NoSQL 文档型数据库,它提供了一些非常棒的特性:
当然,大部分情况下性能也很棒。但是有可能在深入使用 MongoDB 过程中,遇到了不少问题,下面总结几个我们遇到的缺点。这里,我们要特别申明:我们目前用的 MongoDB 版本是 2.4.10,曾经虽然我们升级到 MongoDB 2.6.0 版本,但是由于问题依然存在,所以我们又回退到 2.4.10 版本。 一、MongoDB 数据库级锁 问题:MongoDB的锁机制与一般关系数据库如 MySQL(InnoDB), Oracle 还是有很大的差异,MySQL与 Oracle 能提供行级粒度锁,可是 MongoDB 只能提供 库级粒度锁,这就意味着当 MongoDB 一个写锁处于占用状态时,其它的读写操作都得干等。 初看起来库级锁在大并发环境下有严重的问题,但 MongoDB 依然能够保持大并发量与高性能,这是因为 MongoDB 的锁粒度虽然很粗放,但是在锁处理机制和关系数据库锁有很大差异,主要表现在以下几点:
虽说通常不出问题,但是这不等于没有问题,若数据操作不当,依然会导致长时间占用写锁,例如下面提到的前台建索引操作,当出现这种情况的时候,整个数据库就可能处于完全阻塞状态,无法进行任何读写操作,情况十分严重。
二、建索引导致数据库阻塞
如下面例子,为超大表 posts 建立索引, 我们千万不用使用 db.posts.ensureIndex({user_id: 1}) 而应该使用 db.posts.ensureIndex({user_id: 1}, {background: 1}) 三、不合理使用嵌入 embed document 问题:embed document 是 MongoDB 相比关系数据库差异明显的一个地方,可以在某一个 document 中嵌入其它子 document,这样可以在document 保持在单一 collection 中,检索修改比较方便。 比如应用情景中有一个 Group document,用户申请加入 Group 建模为 GroupRequest document,我们最初的时候使用 embed 方式把 GroupRequest 放置到 Group 中。 Ruby 的代码如下所示(在这里使用了 Mongoid ORM): class Group include Mongoid::Document ... embeds_many :group_requests ... end class GroupRequest include Mongoid::Document ... embedded_in :group ... end 这个使用方式让我们遇到难题,它导致有接近两周的时间系统问题,高峰时段常有几分钟的系统卡顿,最严重一次,让我的 MongoDB 宕机。 仔细分析后,发现某些活跃的 Group 的 group_requests 增加(当有新申请时)与更改(当通过或拒绝用户申请时)异常频繁,而这些操作经常长时间占用写锁,导致整个数据库阻塞。因为当有增加 group_request 操作时,Group 预分配的空间不够,需要重新分配空间(内存与硬盘都需要),耗时较长,另外 Group 上建的索引很多,移动 Group 位置导致大量索引更新操作也很耗时,综合起来引起了长时间占用锁问题。 解决问题的方法:把 embed 关联更改成的普通外键关联,就是类似关系数据库的做法,这样 group_request 增加或修改都只发生在 GroupRequest 上,简单快速,避免长时间占用写锁问题。当关联对象的数据不固定或者经常发生变化时,一定要避免使用 embed 关联,不然会死的很惨。 四、不合理使用 Array 字段 MongoDB 的 Array 字段比较独特,它可以在单个 document 里存储一些简单的一对多关系。 我有一个应用情景使用遇到严重的性能问题,直接上代码,代码如下所示: class User include Mongoid::Document ... field :follower_user_ids, type: Array, default: [] ... end User 中通过一个 Array 类型字段 follower_user_ids 保存用户关注的人的 id,用户关注的人从 10个到 3000 个不等,变化是比较频繁的,和上面 embed 引发的问题类似,频繁的 follower_user_ids 增加修改操作导致大量长时间数据库写锁,从而引发 MongoDB 数据库性能急剧下降。 解决问题的方法:我们把 follower_user_ids 转移到了内存数据库 redis 中,这就避免了频繁更改 MongoDB 中的 User, 从而彻底解决问题。若不使用 redis,你也可以建立一个 UserFollower 集合,使用外键形式关联。 参考资料:
上一条: PostgreSQL 9.3正式发布
|