MongoDBでゆるふわDB体験

第7回GridFS─大容量のファイルをMongoDBに保存する仕組み

GridFSの概要

MongoDBに保存できるドキュメントのサイズは、16Mバイトまでという制限があります。一般的なテキストデータを保存するには十分なサイズですが、巨大なテキストデータや動画などのバイナリデータを保存する用途では、16Mバイトを超える場合が出てきます。MongoDBに16Mバイト以上のファイルを保存したい場合、GridFSというインターフェースを使用します。GridFSを使用することにより、データを複数に分割[1]して保存することが可能となります。

今回はMongoDBでサイズの大きなファイルを扱う仕組みである、GridFSについて説明します。

図1 GridFSの概要図
図1 GridFSの概要図

ファイルをデータベースで管理するメリット

ところで、ファイルをデータベースで管理することでどのようなメリットがあるのでしょうか。

多くのシステムでは、画像/音声/動画などサイズの大きなバイナリファイルは、OSのファイルシステムを使って保存されています。ファイルシステムを使用すると慣れ親しんだインターフェースでファイルにアクセスできます。しかし場合によってはファイルをデータベースに保存し、管理した方が効率的なことがあります。以下にデータベースでファイルを管理するメリットを整理してみました。

メタデータの管理がしやすい

ファイルだけではなく、ファイルに関連するメタデータも一緒に管理する場合が多くあります。たとえば、ファイルサイズや作成者、作成日時などです。動画ファイルなら再生回数を管理するケースも考えられます。データベースではファイルと関連したメタデータの管理がしやすく、拡張性があります。また、メタデータを含めたバックアップも容易です。

ファイルシステムの作成数制限を受けない

OS全体でファイル数やディレクトリ数の作成数には制限があります。たとえば、ext3の場合、ディレクトリ内のサブディレクトリやファイルの数は32,000という制限が設定されています。

簡単にGridFSを使ってみる

MongoDBでGridFSを使うのに特別な設定・使用方法は必要ありません。レプリカセット・シャーディングで使用することも可能です。さっそくGridFSを使ってファイルをMongoDBに保存してみましょう。

今回は、MongoDBに標準で用意されているコマンドラインツールであるmongofilesを使用します。コマンドのオプションや詳細は次ページで説明します。

まず、1MB.fileというファイル名で保存するファイルを作成します。下の例ではファイルサイズ1MBで作成しています。

$ dd if=/dev/zero of=1MB.file bs=1M count=1

mongofiles[2]を使用して、gridtestというデータベースに先ほど作成した1MB.fileを保存します。

$ mongofiles -v -d gridtest put 1MB.file
Wed Feb 20 15:23:27 creating new connection to:127.0.0.1:27017
Wed Feb 20 15:23:27 BackgroundJob starting: ConnectBG
Wed Feb 20 15:23:27 connected connection!
connected to: 127.0.0.1
added file: { _id: ObjectId('51246bdfa3264162c8e99716'), filename: "1MB.file", chunkSize: 262144, uploadDate: new Date(1361341409698), md5: "b6d81b360a5672d80c27430f39153e2c", length: 1048576 }
done!

上記のように表示されますと保存成功です。

コレクション構造について

Mongo ShellからGridFSのコレクションを確認してみましょう。MongoDBに接続し、gridtestデータベースを選択します。

$ mongo
> use gridtest
コレクションを確認します。
> show collections
fs.chunks
fs.files
system.indexes

インデックスの他に、fs.chunksとfs.filesというコレクションができています。それぞれの役割は以下のようになっています。

fs.chunks

GridFSでは、ファイルをチャンクとして一定サイズに分割して、fs.chunksコレクションに保存します。分割するファイルサイズはデフォルトでは256KBです。

fs.files

fs.filesコレクションには、ファイル名、アップロード日時、MD5ハッシュ情報、ファイルサイズなどのメタデータが保存されています。任意のメタデータを追加することも可能です。

保存されているメタデータに関しては公式ドキュメントにより詳しい情報があります。

次のページでは、GridFSの操作方法としてmongofilesとrubyドライバの2つを説明します。

mongofilesを使ったGridFSの操作

mongofilesはMongoDBに標準で用意されているコマンドラインツールです。MongoDBをインストールしたディレクトリのbin以下にあります。このmongofilesを使って、GridFSを操作してみましょう。

各コマンド共通のコマンドラインオプション

-vオプションは詳細を出力します。

ファイルの追加

ファイルの追加にはputを使います。ここではgridtestデータベースに1MB.fileというファイルを追加します。

$ mongofiles -v -d gridtest put 1MB.file

同じファイル名で追加しても、上書きされずに別オブジェクトとして追加されます。上書きする場合は、-rオプションを指定してください。

$ mongofiles -v -d gridtest -r put 1MB.file

ファイルの取得

ファイルの取得にはgetを使います。

$ mongofiles -v -d gridtest get 1MB.file

同じファイル名があった場合は、後から追加されたものが取得されます。

ファイルのリスト表示

ファイルをリスト表示するにはlistを使います。ファイル名とファイルサイズが表示されます。同じファイル名がある場合は、複数表示されます。下の例では、1MB.fileという名前のファイルを2つ登録しています。

$ mongofiles -v -d gridtest list
Wed Feb 20 16:08:01 creating new connection to:127.0.0.1:27017
Wed Feb 20 16:08:01 BackgroundJob starting: ConnectBG
Wed Feb 20 16:08:01 connected connection!
connected to: 127.0.0.1
1MB.file        1048576
1MB.file        1048576
image01.png     524288
image02.png     262144

ファイルの削除

ファイルの削除にはdeleteを使います。

[注意]

同じファイル名のデータはすべて削除されるので注意してください。
$ mongofiles -v -d gridtest delete 1MB.file

ファイルの検索

ファイルの検索にはsearchを使います。指定した文字列がファイル名に含まれている場合は表示されます。

$ mongofiles -v -d gridtest search 1MB

Rubyを使ったGridFSの操作

Ruby用のMongoDB公式ドライバを使用することにより、RubyからGridFSを操作できます。ドライバはgemからインストールできます。

$ gem install mongo

必須ではありませんが、同様にgemでbson_extをインストールするとBSONの処理が高速化されます。

$ gem install bson_ext

まずは準備を行います。RubyからGridFSを扱うには、fs.chunksにインデックスが作成されている必要があります。mongofilesでファイルを登録することで、自動的にインデックスが作成されます。

$ dd if=/dev/zero of=1MB.file bs=1M count=1
$ mongofiles -d gridtest put 1MB.file

以下はgridtestデータベースに対して1MB.fileを保存と取得をし、最後に削除するスクリプトです。ファイル追加時に任意のメタデータを登録できます。

require  'mongo'

db_name = "gridtest"
@con = Mongo::Connection.new
@db = @con[db_name]
@grid = Mongo::Grid.new(@db)
@collection = @db["fs.files"]

# 任意のメタデータを追加可能
file_id = @grid.put(File.binread("1MB.file"),
                    :filename => "1MB.file",
                    :tags => ["mongo","database","book"],
                    :memo => "sample file",
                    :owner => "mongonouchi")

puts "get file_id=#{file_id}"
puts "filename = #{@grid.get(file_id).filename}"
puts ""

@collection.find({:_id => BSON::ObjectId(file_id.to_s)}).each{ |doc|
  puts doc.inspect
}

puts ""
puts "delete file_id=#{file_id}"
@grid.delete(file_id)

上記をgrid_sample.rbとして保存し、実行します。以下は出力のサンプルです。見やすいように改行を入れています。出力されるfile_idは環境によって変わります。

$ ruby grid_sample.rb
get file_id=512ac8a2af1fe66a0e000001
filename = 1MB.file

{"_id"=>BSON::ObjectId('512ac8a2af1fe66a0e000001'), 
 "filename"=>"1MB.file", 
 "contentType"=>"binary/octet-stream", 
 "length"=>1048576, 
 "chunkSize"=>262144, 
 "uploadDate"=>2013-02-25 02:12:50 UTC, 
 "md5"=>"b6d81b360a5672d80c27430f39153e2c", 
 "tags"=>["mongo", "database", "book"], 
 "memo"=>"sample file", 
 "owner"=>"mongonouchi"
}

delete file_id=512ac8a2af1fe66a0e000001

その他のツール

mongofiles、公式ドライバの他にもGridFSを操作するツールは開発されています。詳細な解説は省略しますが、Nginxから直接GridFSを操作するnginx-gridfsがあります。

次回のテーマ

今回はMongoDBでサイズの大きなファイルを扱うGridFSを紹介いたしました。GridFSを使うことにより、画像や動画などのファイルサイズの大きなデータをMongoDBで管理することができます。

次回はリリース間近となっているMongoDB v2.4の新機能の紹介をする予定です。MongoDB v2.4には全文検索、GeoJSON対応、ハッシュドシャードキーなど先進的な機能が追加されています。お楽しみに!

おすすめ記事

記事・ニュース一覧