MongoDBのShardingを試してみた。その1

MongoDB勉強会で発表を聞いてきたので、早速Shardingの機能を試してみます。

環境構成

まずは、以下の構成をします。
本当は複数台のサーバを使用したいのですが、手元に自由に扱えるサーバがさくらVPSしかないため
1台に複数のmongodを立て、mongosとconfigも同居させています。
mongodbのバージョンは、1.8.0を使用しています。


shardサーバ起動

mongodをポート別に3つ起動します。

[matsuou1@testsvr mongodb]$ ./bin/mongod --shardsvr --port 10001 --dbpath /tmp/mongodb/shard1 --logpath /tmp/mongodb/log/shard1.log &
[1] 14269
[matsuou1@testsvr mongodb]$ ./bin/mongod --shardsvr --port 10002 --dbpath /tmp/mongodb/shard2 --logpath /tmp/mongodb/log/shard2.log &
[2] 14276
[matsuou1@testsvr mongodb]$ ./bin/mongod --shardsvr --port 10003 --dbpath /tmp/mongodb/shard3 --logpath /tmp/mongodb/log/shard3.log &
[3] 14287
configサーバ起動
[matsuou1@testsvr mongodb]$ ./bin/mongod --configsvr --port 10004 --dbpath /tmp/mongodb/config --logpath /tmp/mongodb/log/config.log &
[4] 14296
mongosルータ起動

chunkの動作も見たいので、chunk sizeを1MBに設定し起動する。

[matsuou1@testsvr mongodb]$ ./bin/mongos --configdb localhost:10004 --port 10000 --logpath /tmp/mongodb/log/mongos.log --chunkSize 1 &
[5] 14319
Shard構成

まずは、mongosのadminに接続し、Shardを追加する。

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10000/admin
MongoDB shell version: 1.8.0
connecting to: localhost:10000/admin
> db
admin
> db.runCommand( { addshard : "localhost:10001" } );
{ "shardAdded" : "shard0000", "ok" : 1 }
> db.runCommand( { addshard : "localhost:10002" } );
{ "shardAdded" : "shard0001", "ok" : 1 }
> db.runCommand( { addshard : "localhost:10003" } );
{ "shardAdded" : "shard0002", "ok" : 1 }

追加したshardが正しく追加されているかどうか、確認する。

> db.runCommand( { listshards : 1 } );
{
        "shards" : [
                {
                        "_id" : "shard0000",
                        "host" : "localhost:10001"
                },
                {
                        "_id" : "shard0001",
                        "host" : "localhost:10002"
                },
                {
                        "_id" : "shard0002",
                        "host" : "localhost:10003"
                }
        ],
        "ok" : 1
}

> db.printShardingStatus();
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:10001" }
      { "_id" : "shard0001", "host" : "localhost:10002" }
      { "_id" : "shard0002", "host" : "localhost:10003" }
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }
テストデータ登録

使用するテストデータは、某サーバのアクセスログを使用しようかと思います。
年月、ユーザID、タイムスタンプ、URI、デバイスコードぐらいを投入してみます。

[matsuou1@testsvr mongodb]$ ./bin/mongoimport -h localhost:10000 -d logdb -c logs --ignoreBlanks --type tsv --fields month,uid,timestamp,path,device /tmp/mongodb/work/access_20100301.log
connected to: localhost:10000
imported 28130 objects

sharding開始

ようやく環境の構成が完了したので、次はshardingの設定を行います。

コレクション単位でのshardingの開始

mongosに接続して、shardcollectionコマンドを発行する。今回のshard keyは勉強会で良い例として紹介されてたアクセスログ
年月とユーザIDの複合キーを採用。

いざ、shardcollectionコマンドを実行すると以下のエラーが。。。

> db.runCommand( { shardcollection : "logdb.logs" , key : { month : 1 , uid : 1 } } );
{ "ok" : 0, "errmsg" : "sharding not enabled for db" }

エラーメッセージに従い、先にデータベース単位でのshardingしないとだめそうなので、先にenableshardingコマンドを実行する。

> db.runCommand( { enablesharding : "logdb" });
{ "ok" : 1 }

> db.runCommand( { shardcollection : "logdb.logs" , key : { month : 1 , uid : 1 } } );
{
	"ok" : 0,
	"errmsg" : "please create an index over the sharding key before sharding."
}

今度はsharding keyに対して、sharding実施前にインデックスを作る必要があるとのこと。
chunkingプロセスの高速化のために必要であるとのことで、無ければ自動で作成しますと
マニュアルには記載されているが、どうも手動で作成する必要がありそう。

> use logdb
switched to db logdb
> db.logs.ensureIndex( { month : 1 , uid : 1 } );  

> use admin                                      
switched to db admin
> db.runCommand( { shardcollection : "logdb.logs" , key : { month : 1 , uid : 1 } } );
{ "collectionsharded" : "logdb.logs", "ok" : 1 }

> db.printShardingStatus();
--- Sharding Status --- 
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:10001" }
      { "_id" : "shard0001", "host" : "localhost:10002" }
      { "_id" : "shard0002", "host" : "localhost:10003" }
  databases:
	{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
	{ "_id" : "logdb", "partitioned" : true, "primary" : "shard0000" }
		logdb.logs chunks:
				shard0001	3
				shard0002	2
				shard0000	3
			{ "month" : { $minKey : 1 }, "uid" : { $minKey : 1 } } -->> { "month" : "2010/02", "uid" : "c2UGMAoXRXXXDgJ7" } on : shard0001 { "t" : 2000, "i" : 0 }
			{ "month" : "2010/02", "uid" : "c2UGMAoXRXXXDgJ7" } -->> { "month" : "2010/03", "uid" : "a10StyXhoXXX2nb2" } on : shard0002 { "t" : 3000, "i" : 0 }
			{ "month" : "2010/03", "uid" : "a10StyXhoXXX2nb2" } -->> { "month" : "2010/03", "uid" : "a34m3gIYWXXXH5xP" } on : shard0001 { "t" : 4000, "i" : 0 }
			{ "month" : "2010/03", "uid" : "a34m3gIYWXXXH5xP" } -->> { "month" : "2010/03", "uid" : "b2EwnvdeFXXXoKO8" } on : shard0002 { "t" : 5000, "i" : 0 }
			{ "month" : "2010/03", "uid" : "b2EwnvdeFXXXoKO8" } -->> { "month" : "2010/03", "uid" : "c23k2FVIiXXX86m3" } on : shard0001 { "t" : 6000, "i" : 0 }
			{ "month" : "2010/03", "uid" : "c23k2FVIiXXX86m3" } -->> { "month" : "2010/03", "uid" : "d2GIFKklCXXXpoKq" } on : shard0000 { "t" : 6000, "i" : 1 }
			{ "month" : "2010/03", "uid" : "d2GIFKklCXXXpoKq" } -->> { "month" : "2010/03", "uid" : "f3tMomXcKXXXwQMn" } on : shard0000 { "t" : 1000, "i" : 7 }
			{ "month" : "2010/03", "uid" : "f3tMomXcKXXXwQMn" } -->> { "month" : { $maxKey : 1 }, "uid" : { $maxKey : 1 } } on : shard0000 { "t" : 1000, "i" : 8 }

printShardingStatusを見てみると、chunkが8個に分割され、shard000に3つ、shard001に3つ、shard002に2つ配置されている。
※ 使ったデータは小さめのログなので、デフォルトのchunk size(200MB)だと分割されないので、chunksizeを1Mに設定している。
※ 上記のuidは携帯のuidなので一部マスクを掛けています。

本当にshardingされているか確認

まずはmongosに接続し、全体logsコレクションの件数を確認する。

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10000/logdb
MongoDB shell version: 1.8.0
connecting to: localhost:10000/logdb
> db.logs.count();
28130

次に各shardでlogsコレクションの件数を確認する。

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10001/logdb
> db.logs.count();
10296

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10002/logdb
> db.logs.count();
10696

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10003/logdb
> db.logs.count();
7138

各shardのlogsコレクションの件数を確認してみると、確かに分割され、件数もmongos経由した場合と同じ。
ドキュメントのサイズで分けているので、件数が同じにはならない。

データ追加

この状態でさらに7万件程データの追加を行って、chunkの偏りが発生しないかを確認してみる。

> db.printShardingStatus();
--- Sharding Status --- 
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:10001" }
      { "_id" : "shard0001", "host" : "localhost:10002" }
      { "_id" : "shard0002", "host" : "localhost:10003" }
  databases:
	{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
	{ "_id" : "logdb", "partitioned" : true, "primary" : "shard0000" }
		logdb.logs chunks:
				shard0001	6
				shard0002	6
				shard0000	5
			{ "month" : { $minKey : 1 }, "uid" : { $minKey : 1 } } -->> { "month" : "2010/02", "uid" : "c2UGMAoXRXXXDgJ7" } on : shard0001 { "t" : 2000, "i" : 0 }
			{ "month" : "2010/02", "uid" : "c2UGMAoXRXXXDgJ7" } -->> { "month" : "2010/03", "uid" : "a10StyXhoXXX2nb2" } on : shard0002 { "t" : 3000, "i" : 0 }
			{ "month" : "2010/03", "uid" : "a10StyXhoXXX2nb2" } -->> { "month" : "2010/03", "uid" : "a2QsXVOrKXXXOsQf" } on : shard0001 { "t" : 6000, "i" : 2 }
			{ "month" : "2010/03", "uid" : "a2QsXVOrKXXXOsQf" } -->> { "month" : "2010/03", "uid" : "a34m3gIYWXXXH5xP" } on : shard0001 { "t" : 6000, "i" : 3 }
			{ "month" : "2010/03", "uid" : "a34m3gIYWXXXH5xP" } -->> { "month" : "2010/03", "uid" : "a3aDeqciNXXXwVSs" } on : shard0002 { "t" : 7000, "i" : 8 }
			{ "month" : "2010/03", "uid" : "a3aDeqciNXXXwVSs" } -->> { "month" : "2010/03", "uid" : "b2EwnvdeFXXXoKO8" } on : shard0002 { "t" : 7000, "i" : 9 }
			{ "month" : "2010/03", "uid" : "b2EwnvdeFXXXoKO8" } -->> { "month" : "2010/03", "uid" : "b2zjWlq3xXXXufa4" } on : shard0001 { "t" : 6000, "i" : 6 }
			{ "month" : "2010/03", "uid" : "b2zjWlq3xXXXufa4" } -->> { "month" : "2010/03", "uid" : "b3vJ4e1bCXXXVHQS" } on : shard0001 { "t" : 7000, "i" : 10 }
			{ "month" : "2010/03", "uid" : "b3vJ4e1bCXXXVHQS" } -->> { "month" : "2010/03", "uid" : "c23k2FVIiXXX86m3" } on : shard0001 { "t" : 7000, "i" : 11 }
			{ "month" : "2010/03", "uid" : "c23k2FVIiXXX86m3" } -->> { "month" : "2010/03", "uid" : "c2egPLOFzXXXvHZd" } on : shard0002 { "t" : 7000, "i" : 2 }
			{ "month" : "2010/03", "uid" : "c2egPLOFzXXXvHZd" } -->> { "month" : "2010/03", "uid" : "c3N2vMU8ZXXXsCmT" } on : shard0002 { "t" : 7000, "i" : 4 }
			{ "month" : "2010/03", "uid" : "c3N2vMU8ZXXXsCmT" } -->> { "month" : "2010/03", "uid" : "d2GIFKklCXXXpoKq" } on : shard0002 { "t" : 7000, "i" : 5 }
			{ "month" : "2010/03", "uid" : "d2GIFKklCXXXpoKq" } -->> { "month" : "2010/03", "uid" : "f10StyXdrXXX4mb2" } on : shard0000 { "t" : 7000, "i" : 1 }
			{ "month" : "2010/03", "uid" : "f10StyXdrXXX4mb2" } -->> { "month" : "2010/03", "uid" : "f3tMomXcKXXXwQMn" } on : shard0000 { "t" : 6000, "i" : 5 }
			{ "month" : "2010/03", "uid" : "f3tMomXcKXXXwQMn" } -->> { "month" : "2010/03", "uid" : "g2XA4wGX5XXXFjqn" } on : shard0000 { "t" : 7000, "i" : 6 }
			{ "month" : "2010/03", "uid" : "g2XA4wGX5XXXFjqn" } -->> { "month" : "2010/03", "uid" : "i3zqETmH1XXXQeIX" } on : shard0000 { "t" : 7000, "i" : 7 }
			{ "month" : "2010/03", "uid" : "i3zqETmH1XXXQeIX" } -->> { "month" : { $maxKey : 1 }, "uid" : { $maxKey : 1 } } on : shard0000 { "t" : 6000, "i" : 9 }

chunk数を見ると17個に分割され、shard0000に5つ、shard0001に6つ、shard0002に6つ配置されているのが分かり、shard間の差はほぼない。

一応、各shardの件数も確認してみる。

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10000/logdb
> db.logs.count();
96974

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10001/logdb
> db.logs.count();
28353

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10002/logdb
> db.logs.count();
33500

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10003/logdb
> db.logs.count();
35121

件数についてもだいたい、均等に振られていることがわかる。

printShardingStatusで表示されるchunkの情報の最後の"t"と"i"は何の情報を示しているんだろうか?
ほんの少しだけ調べたけど分からなかったので、今度調べておくことにする。(分かる人がいたら教えてください><)

{ "month" : "2010/03", "uid" : "a34m3gIYWXXXH5xP" } -->> { "month" : "2010/03", "uid" : "a3aDeqciNXXXwVSs" } on : shard0002 { "t" : 7000, "i" : 8 }

まとめ

今回はmongodbのshardingの機能について、少しだけ使ってみました。
上記の通り、割と簡単に試せるので、やったことが無い人はやってみるとより理解が深まるかと思います。

次回以降は、shard keyを効率が悪いものに代えるとchunkの偏りがどのように発生するか、その場合に発生するMigrationの動作、failover時の動作、configサーバが落ちた場合の動作などを見ていけたらと考えています。
その前に、投入したアクセスログを集計してみるかもしれません。