モンゴDBの穴は

5486 ワード

MongoDBは現在人気のあるNoSQLドキュメント型データベースで、自動failoverメカニズム、自動sharding、モードレスschemalessなど、多くの場合、パフォーマンスも優れています.しかし、ミントはMongoDBを深く使う過程で、多くの問題に直面しました.以下、私たちが出会った穴をいくつかまとめます.特に、現在使用しているMongoDBバージョンは2.4.10で、MongoDB 2.6.0バージョンにアップグレードされたことがありますが、問題は依然として存在し、2.4.10バージョンに戻ります.
MongoDBデータベースレベルロック
坑父指数:5星(最高5星)
MongoDBのロックメカニズムと一般関係データベース、例えばMySQL(InnoDB)では、Oracleに大きな違いがあり、InnoDBとOracleは行レベルのパーティクルロックを提供することができ、MongoDBはライブラリレベルのパーティクルロックしか提供できない.これは、MongoDBの1つの書き込みロックが占有されている場合、他の読み書き操作が行われることを意味する.
最初はライブラリレベルのロックが大きな同時環境で深刻な問題があったように見えますが、MongoDBは依然として大きな同時量と高性能を維持することができます.これは、MongoDBのロック粒度が粗放ですが、ロック処理メカニズムと関係データベースのロックに大きな違いがあり、主に以下のように表現されています.
  • MongoDBには完全なトランザクションサポートがなく、操作原子性は単一documentレベルにすぎないため、通常は操作粒度が小さい.
  • MongoDBロックの実際の占有時間はメモリデータの計算と変更時間であり、通常は速い.
  • MongoDBロックには、低速IOの読み書きデータを待つ必要がある場合に一時的に放棄し、IOが完了してからロックを再取得する一時的な放棄メカニズムがあります.

  • 通常、問題が発生しないのは問題がないということではありません.データの操作が適切でない場合、次のフロントインデックス作成操作など、書き込みロックが長時間かかります.このような場合、データベース全体が完全にブロックされ、読み書き操作ができなくなります.
    問題を解決する方法は、できるだけ長時間の書き込みロック操作を避けることであり、いくつかの集合操作が本当に避けられない場合は、この集合を個別のMongoDBライブラリに入れることを考慮することができる.MongoDBの異なるライブラリロックは互いに隔離されているため、集合を分離することで、ある集合操作がグローバルなブロック問題を引き起こすことを避けることができる.
    インデックス作成によるデータベースのブロック
    坑父指数:3星
    上記のMongoDBライブラリレベルのロックの問題について説明したように、インデックスの作成は長時間の書き込みロックを引き起こしやすい問題であり、MongoDBはフロントでインデックスを作成する際に書き込みロックを1つ占有する必要がある(しかも一時的に放棄しない)、集合のデータ量が大きい場合、インデックスの作成には通常比較的長い時間がかかり、特に問題を引き起こしやすい.
    解決策は簡単で、MongoDBは2つのインデックス作成アクセスを提供しています.1つはbackground方式で、長時間書き込みロックを必要とせず、もう1つは非background方式で、長時間ロックを必要とします.background方式で問題を解決できます.たとえば、超大テーブルpostsのインデックスを作成するには、決して使用しないでください.
    db.posts.ensureIndex({user_id: 1}) 

    使用するべきです
    db.posts.ensureIndex({user_id: 1}, {background: 1}) 

    埋め込みembed documentの不合理な使用
    坑父指数:5星
    Embed documentはMongoDBと比較してリレーショナル・データベースの違いが明らかな場所であり、あるdocumentに他のサブdocumentを埋め込むことができ、親子documentを単一collectionに保つことができ、検索修正が便利である.
    例えばミントの応用シナリオにはGroupdocumentがあり、ユーザーはGroupに参加してGroupRequest documentとしてモデリングすることを申請し、私たちは最初にembed方式でGroupRequestをGroupに配置した.Rubyコードは以下の通りです(Mongoid ORMを使用しています):
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13class Group    include Mongoid::Document    ...    embeds_many :group_requests    ... end class GroupRequest    include Mongoid::Document    ...    embedded_in :group    ... endこの使い方は私たちを穴に落として、もう少しで這い出せないところでした.それは2週間近くシステムの問題を招き、ピーク時には数分のシステムカートンがあり、最も深刻なのはMongoDBのダウンタイムを引き起こすこともあります.
    よく分析すると、いくつかの活発なグループのグループが見つかりました.requestsは、新規購買依頼がある場合に増加し、ユーザー購買依頼が通過または拒否された場合に変更が頻繁に行われます.これらの操作は、書き込みロックを長時間使用し、データベース全体がブロックされることがよくあります.なぜならgroupが増加するとrequest操作の場合、Groupの事前割り当てのスペースが不足し、再割り当てのスペース(メモリもハードディスクも必要)が必要で、時間がかかります.また、Groupに構築されたインデックスが多く、Groupの位置を移動するとインデックスの更新操作にも時間がかかり、総合的に長時間のロック問題を引き起こします.
    問題を解決する方法は、簡単に言えば、embed関連を通常の外部キー関連に変更することです.関係データベースのような方法です.group_requestの増加または変更はGroupRequestでのみ発生し、書き込みロックの問題を長時間使用しないように簡単で迅速です.関連オブジェクトのデータが固定されていない場合や頻繁に変化する場合は、embed関連を使用することを避けなければなりません.そうしないと、惨めに死んでしまいます.
    Arrayフィールドの不合理な使用
    坑父指数:4星
    MongoDBのArrayフィールドは比較的ユニークな特性であり,単一documentに簡単な一対の多関係を格納することができる.
    ミントには、次のような深刻なパフォーマンスの問題に直面するアプリケーションのシナリオがあります.
    ?
    1
    2
    3
    4
    5
    6class User    include Mongoid::Document    ...    field :follower_user_ids, type: Array,  default : []    ... endUserのArrayタイプフィールドfollower_user_idsはユーザーが注目している人のidを保存し、ユーザーが注目している人は10人から3000人まで様々で、変化は比較的頻繁で、上のembedが引き起こした問題と似ていて、頻繁なfollower_user_idsの変更操作が増加すると、長時間のデータベース書き込みロックが大量に発生し、MongoDBデータベースのパフォーマンスが急激に低下します.
    問題を解決する方法:followerをuser_idsはメモリデータベースredisに移行し、MongoDBのUserを頻繁に変更することを回避し、問題を徹底的に解決します.redisを使用しない場合は、外部キー形式で関連付けるUserFollowerコレクションを作成することもできます.
    まず上のいくつかの穴を挙げましょう.すべて人を害する罠です.MongoDBを使う過程では、穴に落ちないように注意しなければなりません.
    参考資料:*1.MongoDBロックメカニズムの詳細 * 2. MongoDBインデックス作成操作ドキュメント
    由来:http://xiewenwei.github.io/