web-dev-qa-db-ja.com

MongoDBで最近削除されたドキュメントを回復する方法はありますか?

誤って最後のクエリでいくつかのドキュメントを削除しました。最後のクエリmongoコレクションをロールバックする方法はありますか。

これが私の最後のクエリです:

 db.datas.remove({ "name" : "some_x_name"}) 

ロールバック/取り消しオプションはありますか?データを取り戻すことはできますか?

17
trex

ロールバックオプションはありません( MongoDBコンテキストではロールバックには異なる意味があります )、厳密に言えば、これらのドキュメントを元に戻すサポートされた方法はありません。 。ただし、レプリカセットを実行している場合、単一ノードのレプリカセットであっても、 oplog になります。ドキュメントがいつ挿入されたかをカバーするoplogを使用すると、それらを回復できる場合があります。

これを説明する最も簡単な方法は、例を使用することです。復元する必要のある100個の削除済みドキュメントを含む単純な例を使用します。これを超えるには(膨大な数のドキュメント、または選択的にのみ復元するなど)、コードを変更してカーソルを反復処理するか、MongoDBシェルの外部で選択した言語を使用してこれを記述します。基本的なロジックは同じままです。

まず、サンプルfooをデータベースdropTestに作成します。 nameフィールドのない100個のドキュメントと、同じnameフィールドのある100個のドキュメントを挿入して、後で誤って削除できるようにします。

use dropTest;
for(i=0; i < 100; i++){db.foo.insert({_id : i})};
for(i=100; i < 200; i++){db.foo.insert({_id : i, name : "some_x_name"})};

ここで、100個のnameドキュメントの偶発的な削除をシミュレートしましょう。

> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })

レプリカセットで実行しているため、oplog(挿入中)にはこれらのドキュメントの記録が残っていますが、ありがたいことに、これらの挿入はoplogの終わりから落ちていません。 (oplogキャップ​​付きコレクション 覚えています)。それらを見つけることができるかどうか見てみましょう:

use local;
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}).count();
100

カウントは正しいように見えますが、ドキュメントはまだ残っているようです。経験から、ここで必要なoplogエントリの唯一の部分はoフィールドであることがわかっているので、それを返すだけの投影を追加しましょう(簡潔にするために出力を省略しますが、考え):

db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1});
{ "o" : { "_id" : 100, "name" : "some_x_name" } }
{ "o" : { "_id" : 101, "name" : "some_x_name" } }
{ "o" : { "_id" : 102, "name" : "some_x_name" } }
{ "o" : { "_id" : 103, "name" : "some_x_name" } }
{ "o" : { "_id" : 104, "name" : "some_x_name" } }

これらのドキュメントを再挿入するには、それらを配列に保存し、配列を反復処理して関連する部分を挿入するだけです。まず、配列を作成しましょう。

var deletedDocs = db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1}).toArray();
> deletedDocs.length
100

次に、コレクションに含まれるドキュメントが100になったことを思い出し、100の挿入をループし、最後にカウントを再検証します。

use dropTest;
db.foo.count();
100
// simple for loop to re-insert the relevant elements
for (var i = 0; i < deletedDocs.length; i++) {
    db.foo.insert({_id : deletedDocs[i].o._id, name : deletedDocs[i].o.name});
}
// check total and name counts again
db.foo.count();
200
db.foo.count({name : "some_x_name"})
100

そこにはいくつかの注意事項があります:

  • これは、真の復元戦略を意図したものではなく、バックアップ(MMS、その他)を見て、そのためにセカンダリを遅延させた、コメントで述べたように
  • 大規模なビジーシステムでは、oplogからドキュメントをクエリするのはそれほど速くありません(oplogクエリはテーブルスキャンです)。
  • ドキュメントはいつでもoplogから期限切れになる可能性があります(もちろん、後で使用するためにoplogのコピーを作成してより多くの時間を与えることができます)
  • ワークロードによっては、結果を再挿入する前に重複排除が必要になる場合があります
  • 文書のより大きなセットは、示されているように配列には大きすぎるため、代わりにカーソルを反復処理する必要があります。
  • oplogの形式は内部と見なされ、いつでも(予告なしに)変更される可能性があるため、自己責任で使用してください
24
Adam Comerford

私はこれが少し古いことを理解していますが、私はこの分野で研究した何かを共有したいと思いました。

実際のところ、MongoDBはデータを物理的にすぐに削除するのではなく、削除対象としてマークするだけです。ただし、これはバージョン固有であり、現在、ドキュメントや標準化はありません-サードパーティのツール開発者(または必死に必要な人)がツールを構築したり、バージョン間で動作する単純なスクリプトを確実に記述できるようにする可能性があります。このチケットをオープンしました- https://jira.mongodb.org/browse/DOCS-5151

はるかに低いレベルにあり、使用するMongoDBのバージョンに基づいて微調整が必​​要になる可能性がある1つのオプションを検討しました。当然のことながら、ほとんどの人のリンクには低すぎるレベルですが、それは機能し、他のすべてが失敗したときに便利です。

私のアプローチでは、ファイル内のバイナリを直接操作し、Pythonスクリプト(またはコマンド)を使用して、削除されたデータを特定、読み取り、アンパック(BSON)します。

私のアプローチは this GitHubプロジェクトに触発されています(このプロジェクトの開発者ではありません)。 私のブログではこちら スクリプトを簡素化し、Raw MongoDBファイルから特定の削除済みレコードを抽出しようとしました。

現在、レコードは削除対象として「\xee "をレコードの先頭に追加します。これは、未加工のdbファイルで削除されたレコードの外観です。

‘\xee\xee\xee\xee\x07_id\x00U\x19\xa6g\x9f\xdf\x19\xc1\xads\xdb\xa8\x02name\x00\x04\x00\x00\x00AAA\x00\x01marks\x00\x00\x00\x00\x00\x00@\x9f@\x00′

最初のブロックを、他のレコードに基づいて以前に特定したレコードのサイズに置き換えました。

y=”3\x00\x00\x00″+x[20804:20800+51]

最後に、BSONパッケージ(pymongoに付属)を使用して、バイナリを読み取り可能なオブジェクトにデコードしました。

bson.decode_all(y)

[{u’_id': ObjectId(‘5519a6679fdf19c1ad73dba8′), u’name': u’AAA’, u’marks': 2000.0}]

このBSONはpythonオブジェクトになり、recoverコレクションにダンプするか、単にどこかに記録することができます。

言うまでもなく、これまたは他の回復手法は、データベースファイルのバックアップコピーのステージング領域で理想的に実行する必要があります。

10
Yazad