最近、2つのアプリケーションが変換され、起動プロセス中に一連の問題が発生しました(その一部はObjectIDの誤解によって引き起こされました)
最初にObjectIDを理解しましょう:
タイムスタンプ
最初の4桁は、UNIXタイムスタンプ、INTカテゴリです。上記の例「4DF2DCEC」でObjectIDの最初の4桁を抽出し、10進数から10進数にインストールします: "1307761900"。この番号はタイムスタンプです。効果をより明確にするために、このタイムスタンプを慣れている時間形式に変換します(正確に秒)
$ date -d '1970-01-01 UTC 1307761900秒' -U
2011年6月11日土曜日03:11:40 UTC
最初の4バイトは実際にドキュメント作成の時間を非表示にし、タイムスタンプはキャラクターの前にあります。つまり、ObjectIDは挿入によって大まかにソートされます。これは、インデックスとしての検索効率を改善するなど、いくつかの側面で大きな役割を果たします。これはまた、迅速かつ連続的に複数のオブジェクトIDを作成すると、最初の数桁が現在の時刻を使用しているため、変更を見つけることはめったにないことがわかります。多くのユーザーは、サーバーの時間を同期することを心配しています。実際、このタイムスタンプの真の価値は、絶えず増加している限り、重要ではありません。
機械
次の3バイトは2CDCD2です。これらの3つのバイトは、それらが配置されているホストの一意の識別子であり、通常、マシンホスト名のハッシュ値です。これにより、異なるホストが異なるマシンハッシュ値を生成し、分布に競合がないことを保証します。これが、同じマシンによって生成されたObjectIDの文字列がまったく同じである理由です。
pid
上記のマシンは、異なるマシンで生成されたオブジェクトが競合しないようにし、PIDは同じマシン上の異なるMongoDBプロセスで競合しないオブジェクトを生成することです。 0936の次の2ビットは、オブジェクトを生成するプロセス識別子です。
インクリメント
最初の9バイトでは、1秒以内に異なるマシンとプロセスによって生成されたオブジェクトが競合しないことを保証します。次の3バイトA8B817は、同じ秒以内に生成されたオブジェクトが競合を見つけられず、16777216のレコードの一意性に等しい256から3番目の電力を可能にするための自動的に増加したカウンターです。
Objectidユニークさ
クライアントでもサーバーでも、ある程度はユニークであることが保証される可能性があると思うかもしれません。
誤解1。ドキュメント注文は挿入順序と一致していますか?
単一のスレッド状況
ObjectIDのタイムスタンプ、マシン、PID、およびIncは、同じマシンと同じプロセスで一意であることが保証できます。
ここには問題があり、MongoDB操作はマルチスレッドです。 a、b、c ...いくつかのスレッドが店内操作を実行する場合、どちらが他のスレッドよりも前にいることが保証されないため、順不同になります。
マルチスレッド、マルチマシン、またはマルチプロセスの状況
ObjectIDのマッハとPIDを見てみましょう。その後、データはさらに順調になります。
解決:
コレクションのデータは順序付けられていないため(キャップコレクションを含む)、最も簡単な方法はObjectIDをソートすることです。
ソートするには2つの方法があります。
1.MongoDBクエリステートメント
jQuery query = new Query(); if(id!= null){jquery.addcriteria(criteria.where( "_ id")。gt(id)); } jQuery.with(new sot(sort.direction.asc、 "_id"));2.java.util.priorityqueue
Comparator <dbobject> Comparator = new Comparator <DboBject>(){@Override public int Compare(dbobject o1、dbobject o2){return((objectid)o1.get( "_ id"))。 }}; PriorityQueue <dbobject> queue = new priorityqueue <dbobject>(200、Comparator);誤解2:複数のクライアントが高い並行性を持っている場合、順序を(並べ替え後)保証できますか?
書き込みが読み取りよりもはるかに大きいことを常に確認する場合(1秒以上の間隔)、これは順番に発生することはありません。
次の例を見てみましょう
次に、図を参照して、データを2回取り出します
初め
4DF2DCEC AAAA FFFF 36A8B813
4DF2DCEC AAAA EEEE 36A8B813
4DF2DCEC BBBB 1111 36A8B814
二度目
4DF2DCEC BBBB 1111 36A8B813
4DF2DCEC AAAA FFFF 36A8B814
4DF2DCEC AAAA EEEE 36A8B814
次のクエリ結果を実行するために最初の最大値(4DF2DCEC BBBB 1111 36A8B814)を取得した場合、それは見逃されます
(4DF2DCEC BBBB 1111 36A8B814)は、2回目のすべてのレコードよりも大きいため、2回目の3つの項目。
これにより、データの損失が発生します。
解決:
ObjectIDのタイムスタンプは数秒に遮断されるため、カウンターオペレーターの最初の4桁はマシンとプロセス番号です。
1.特定の時間間隔の前に記録を処理する(1秒以上)。そのため、マシンとプロセス数が障害を引き起こしたとしても、間隔の前に障害はありません。
2。シングルポイントの挿入、もともといくつかのポイントに配布されていた挿入操作は、マシンとプロセス番号が同じであることを確認するために1つのポイントで照会され、カウンター演算子がレコードを整然と作成するために使用されます。
ここでは、最初の方法を使用しました。
誤解3。MongoDBを使用してdbobject_idを設定してObjectIDを設定しないでください。
MongoDB挿入操作中、新しいdbbasicObject()の場合、_ IDが手動で設定されていない限り、誰もが_ IDが入力されていないことを確認します。それで、それはサーバーにセットアップされていますか?
挿入操作のコードを見てみましょう。
実装クラス
public Writeresult insert(list <dbobject> list、com.mongodb.writeconcern Concern、dbencoder encoder){if(cancer == null){show new elegalargumentexception( "concern can no null"); } return insert(list、true、cernect、encoder); }追加する必要があることがわかります、デフォルトは追加することです
protected writeresult insert(list <dbobject> list、boolean dewseply、com.mongodb.writeconcern Concern、dbencoder encoder){if(encoder == null)encoder = defaultdbencoder.factory.create(); if(willtrace()){for(dbobject o:list){trace( "save:" + _fullnamespace + "" + json.serialize(o)); }} if(sextapply){for(dbobject o:list){apply(o); _CheckObject(o、false、false);オブジェクトID = O.get( "_ id"); if(id instanceof objectId){((objectId)id).notnew(); }}} writeresult last = null; int cur = 0; int maxsize = _mongo.getmaxbsonobjectsize(); while(cur <list.size()){outmessage om = outmessage.insert(this、encoder、concert); for(; cur <list.size(); cur ++){dbobject o = list.get(cur); om.putobject(o); //バッチインサートの制限はサーバー上の4 x maxbsonです。2xを使用して安全にしてください(om.size()> 2 * maxsize){cur ++;壊す; }} last = _connector.say(_db、om、cencert); }最後に返します。 }ObjectID操作を自動的に追加します
/** *呼び出し{@link dbcollection#apply(com.mongodb.dbobject、boolean)} with suresid = true * @param o <code> dbobject> dbobject> dbobject> dbobject> dbobject> dbobject> dbobject </code>修正されたパラメーターオブジェクト * @return */public object apply(dbobject o){return(o、true); }/** *呼び出し{@link dbcollection#doapply(com.mongodb.dbobject)}、オプションで自動_id field * @param joオブジェクトを追加して * @param joオブジェクト * @param suresidにフィールドを追加するかどうかを追加するかどうか * @return the modified objiles Jo.get( "_id"); if(surseid && id == null){id = objectid.get(); Jo.put( "_id"、id); } doapply(jo); IDを返します。 }ご覧のとおり、ObjectIDはMongoDBドライバーパッケージに自動的に追加されます。
保存の方法
public Writeresult save(dbobject jo、writeconcern Concern){if(checkreadonly(true))return null; _Checkobject(Jo、false、false); Object Id = jo.get( "_id"); if(id == null ||(id instanceof objectId &&((objectId)id).isnew()))){if(id!= null && id instanceof objectid)((objectId)id).notnew(); if(concern == null)return insert(jo);それ以外の場合は、挿入物を返します(jo、懸念); } dbobject q = new BasicDboBject(); Q.put( "_id"、id); if(concern == null)return update(q、jo、true、false);それ以外の場合は、更新を返します(Q、Jo、True、False、Concern); }要約すると、デフォルトでは、ObjectIDはクライアントによって生成され、設定なしではサーバーによってではありません。
誤解4。FindAndModifyは本当に自動インクリメント変数を取得できますか?
dbobject update = new BasicDboBject( "$ inc"、new BasicDboBject( "counter"、1)); dbobject query = new BasicDboBject( "_ id"、key); dbobject result = getMongoTemplate()。getCollection(collectionName).findandModify(query、update); if(result == null){dbobject doc = new BasicDboBject(); doc.put( "counter"、1L); doc.put( "_ id"、key); // insert(collectionname、doc); getMongotemplate()。save(doc、collectionname); 1Lを返します。 } return(long)result.get( "counter");自動インクリメント変数の取得は、この方法を使用して記述されますが、実行後にわかります。
操作を検索し、最初に検索を実行してから修正を実行するため、結果がnullの場合は追加されて返される必要があります0
上記は、編集者が紹介したMongodbのObjectIDによって引き起こされた誤解と一連の問題です。それがあなたに役立つことを願っています。ご質問がある場合は、メッセージを残してください。編集者は時間内に返信します。 wulin.comのウェブサイトへのご支援ありがとうございます!