2012年3月28日水曜日

MongoDB(4) レプリケーション

概要

CRUDはいい加減おもしろくないですが、MongoDBのレプリケーションは簡単で面白いらしいよ。ということで、やってみる。

まず、レプリケーションのアーキテクチャにMaster/SlaveReplica Sets(+Replica Pair)がある。

新しいバージョンのMongoDBが導入できるならReplica Sets。

とくちょう

・Data Redundancy
・Automated Failover
・High Availability
・Distributing read load
・Simplify maintenance (compared to "normal" master-slave)
・Disaster recovery
日本語訳と違う(><)


レプリカ用ディレクトリ作成
D:\xampp\mongodb\data
┗rep1
┗rep2
┗rep3



レプリカセット起動
mongod --replSet rep_test --port 27017 --dbpath ../data/rep1
mongod --replSet rep_test --port 27018 --dbpath ../data/rep2
mongod --replSet rep_test --port 27019 --dbpath ../data/rep3

rep_testがレプリカセットの名前。

起動したけど、初期化してないから警告が出てる
Wed Mar 28 07:48:44 [rsStart] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)



レプリカセット初期化
D:\xampp\mongodb\bin>mongo

MongoDB shell version: 2.0.3
connecting to: test
> config = {_id: 'rep_test', members: [
{_id: 0, host: '127.0.0.1:27017'},
{_id: 1, host: '127.0.0.1:27018'},
{_id: 2, host: '127.0.0.1:27019'}]
};

rs.initiate(config);
{
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}
>


initiate()すると、初期化がはじまってPrimary/Secondaryが選択される
[rsStart] replSet STARTUP2
[rsSync] ******
[rsSync] creating replication oplog of size: 47MB...
[rsSync] ******
[rsSync] replSet initial sync pending
[rsSync] replSet initial sync need a member to be primary or second initial sync
[rsHealthPoll] replSet member 127.0.0.1:27017 is up
[rsHealthPoll] replSet member 127.0.0.1:27017 is now in state SECONDARY
[rsHealthPoll] replSet member 127.0.0.1:27018 is up
[rsHealthPoll] replSet member 127.0.0.1:27018 is now in state SECONDARY
[rsSync] replSet initial sync finishing up


なぜか27019がマスタに。



状態確認
PRIMARY> rs.status()
{
        "set" : "rep_test",
        "date" : ISODate("2012-03-27T23:03:25Z"),
        "myState" : 1,
        "syncingTo" : "127.0.0.1:27019",
        "members" : [
                {
                        "_id" : 0,
                        "name" : "127.0.0.1:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "SECONDARY",
                        "optime" : {
                                "t" : 1332888980000,
                                "i" : 1
                        },
                        "optimeDate" : ISODate("2012-03-27T22:56:20Z"),
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "127.0.0.1:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 422,
                        "optime" : {
                                "t" : 1332888980000,
                                "i" : 1
                        },
                        "optimeDate" : ISODate("2012-03-27T22:56:20Z"),
                        "lastHeartbeat" : ISODate("2012-03-27T23:03:25Z"),
                        "pingMs" : 0
                },
                {
                        "_id" : 2,
                        "name" : "127.0.0.1:27019",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "PRIMARY",
                        "uptime" : 159,
                        "optime" : {
                                "t" : 1332888980000,
                                "i" : 1
                        },
                        "optimeDate" : ISODate("2012-03-27T22:56:20Z"),
                        "lastHeartbeat" : ISODate("2012-03-27T23:03:24Z"),
                        "pingMs" : 0
                }
        ],
        "ok" : 1
}


自動フェイルオーバーの確認

PRIMARYを落としてみる
[rsHealthPoll] replSet member 127.0.0.1:27019 is now in state DOWN


状態確認
PRIMARY> rs.status()
{
        (略)
        "members" : [
                {
                        "_id" : 0,
                        "name" : "127.0.0.1:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        (略)
                },
                {
                        "_id" : 1,
                        "name" : "127.0.0.1:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        (略)
                },
                {
                        "_id" : 2,
                        "name" : "127.0.0.1:27019",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        (略)
                }
        ],
        "ok" : 1
}

27017がPRIMARYになった。

27019復活
mongod --replSet rep_test --port 27019 --dbpath ../data/rep3


状態確認
PRIMARY> rs.status()
{
        (略)
        "members" : [
                {
                        "_id" : 0,
                        "name" : "127.0.0.1:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        (略)
                },
                {
                        "_id" : 1,
                        "name" : "127.0.0.1:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        (略)
                },
                {
                        "_id" : 2,
                        "name" : "127.0.0.1:27019",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        (略)
                }
        ],
        "ok" : 1
}

27019はSECONDARYとして復活。

master/secondaryノードの決定は、各サーバの多数決で決定される。


というわけで、うわさどおり簡単で面白かったです。

かねこ(ゝω・)



2012年3月26日月曜日

MongoDB(3) クエリ速習

クエリに挑戦!

サンプルデータ
> db.example.remove()
> for(var i =0; i<6; i++) {
... var doc = {name:'user'+i, point: i*1000};
... db.example.save(doc);
... }
>

全件検索
> db.example.find()
{"_id":ObjectId("4f69766ea84ad157af92c6ab"),"name":"user0","point":0}
{"_id":ObjectId("4f69766ea84ad157af92c6ac"),"name":"user1","point":1000}
{"_id":ObjectId("4f69766ea84ad157af92c6ad"),"name":"user2","point":2000}
{"_id":ObjectId("4f69766ea84ad157af92c6ae"),"name":"user3","point":3000}
{"_id":ObjectId("4f69766ea84ad157af92c6af"),"name":"user4","point":4000}
{"_id":ObjectId("4f69766ea84ad157af92c6b0"),"name":"user5","point":5000}

条件検索
> db.example.find({point:2000})
{ "_id" : ObjectId("4f69766ea84ad157af92c6ad"), "name" : "user2", "point" : 2000 }

ソート(昇順)
> db.example.find().sort({point:1})
{ "_id" : ObjectId("4f69766ea84ad157af92c6ab"), "name" : "user0", "point" : 0 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ac"), "name" : "user1", "point" : 1000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ad"), "name" : "user2", "point" : 2000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ae"), "name" : "user3", "point" : 3000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6af"), "name" : "user4", "point" : 4000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6b0"), "name" : "user5", "point" : 5000 }

ソート(降順)
> db.example.find().sort({point:-1})
{ "_id" : ObjectId("4f69766ea84ad157af92c6b0"), "name" : "user5", "point" : 5000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6af"), "name" : "user4", "point" : 4000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ae"), "name" : "user3", "point" : 3000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ad"), "name" : "user2", "point" : 2000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ac"), "name" : "user1", "point" : 1000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ab"), "name" : "user0", "point" : 0 }

リミット
>db.example.find().limit(2)
{ "_id" : ObjectId("4f69766ea84ad157af92c6ab"), "name" : "user0", "point" : 0 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ac"), "name" : "user1", "point" : 1000 }

オフセット
> db.example.find().skip(4)
{ "_id" : ObjectId("4f69766ea84ad157af92c6af"), "name" : "user4", "point" : 4000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6b0"), "name" : "user5", "point" : 5000 }

リミット&オフセット(ページング)
> db.example.find().skip(2).limit(2)
{ "_id" : ObjectId("4f69766ea84ad157af92c6ad"), "name" : "user2", "point" : 2000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6ae"), "name" : "user3", "point" : 3000 }

条件演算子(条件演算子一覧はここ
> db.example.find({point:{$gte:3000}})
{ "_id" : ObjectId("4f69766ea84ad157af92c6ae"), "name" : "user3", "point" : 3000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6af"), "name" : "user4", "point" : 4000 }
{ "_id" : ObjectId("4f69766ea84ad157af92c6b0"), "name" : "user5", "point" : 5000 }


カーソル
> var cursor = db.example.find();

> cursor.hasNext()
true

> cursor.next()
{
        "_id" : ObjectId("4f69766ea84ad157af92c6ab"),
        "name" : "user0",
        "point" : 0
}

>while (cursor.hasNext()){printjson(cursor.next());}
{
        "_id" : ObjectId("4f69766ea84ad157af92c6ac"),
        "name" : "user1",
        "point" : 1000
}
{
        "_id" : ObjectId("4f69766ea84ad157af92c6ad"),
        "name" : "user2",
        "point" : 2000
}
{
        "_id" : ObjectId("4f69766ea84ad157af92c6ae"),
        "name" : "user3",
        "point" : 3000
}
{
        "_id" : ObjectId("4f69766ea84ad157af92c6af"),
        "name" : "user4",
        "point" : 4000
}
{
        "_id" : ObjectId("4f69766ea84ad157af92c6b0"),
        "name" : "user5",
        "point" : 5000
}

まとめ

MongoDBのクエリー単純で簡単。

かねこ(~_~)

2012年3月22日木曜日

MongoDB(2) はろーわーるど

つづき。MongoDBをつかってみます。

mongoシェルログイン
> mongo
MongoDB shell version: 2.0.3
connecting to: test
>

testデータベース(?)にログインしたっぽい。

create tableどーやるんだ。
ヘルプで確認。
> help
  db.help()                    help on db methods
  db.mycoll.help()             help on collection methods
  rs.help()                    help on replica set methods
  help admin                   administrative help
  help connect                 connecting to a db help
  help keys                    key shortcuts
  help misc                    misc things to know
  help mr                      mapreduce
  show dbs                     show database names
  show collections             show collections in current database
  show users                   show users in current database
  show profile                 show most recent system.profile ....
  show logs                    show the accessible logger names
  show log [name]              prints out the last segment of log in ....
   use                 set current database
   db.foo.find()                list objects in collection foo
  db.foo.find( { a : 1 } )     list objects in foo where a == 1
  it                           result of the last line evaluated; use to further iterate
  DBQuery.shellBatchSize = x   set default number of items to....
  exit                         quit the mongo shell

create table ない。

もういっこのヘルプ参照
>db.mycoll.help()
DBCollection help
  db.mycoll.find().help() - show DBCursor help
  db.mycoll.count()
  db.mycoll.dataSize()
  db.mycoll.distinct( key ) - eg. db.mycoll.distinct( 'x' )
  db.mycoll.drop() drop the collection
  db.mycoll.dropIndex(name)
  db.mycoll.dropIndexes()
  db.mycoll.ensureIndex(keypattern[,options]) - options is an object withthese ....
  db.mycoll.reIndex()
  db.mycoll.find([query],[fields]) - query is an optional query filter. fields is op....
  db.mycoll.find(...).count()
  db.mycoll.find(...).limit(n)
  db.mycoll.find(...).skip(n)
  db.mycoll.find(...).sort(...)
  db.mycoll.findOne([query])
  db.mycoll.findAndModify( { update : ... , remove : bool [, query: {}, sort: {}, 'new': false] } )
  db.mycoll.getDB() get DB object associated with collection
  db.mycoll.getIndexes()
  db.mycoll.group( { key : ..., initial: ..., reduce : ...[, cond: ...] })
  db.mycoll.mapReduce( mapFunction , reduceFunction ,  )
  db.mycoll.remove(query)
  db.mycoll.renameCollection( newName ,  ) renames the collection.
  db.mycoll.runCommand( name ,  ) runs a db command with the gi....
  db.mycoll.save(obj)
  db.mycoll.stats()
  db.mycoll.storageSize() - includes free space allocated to this collection
  db.mycoll.totalIndexSize() - size in bytes of all the indexes
  db.mycoll.totalSize() - storage allocated for all data and indexes
  db.mycoll.update(query, object[, upsert_bool, multi_bool])
  db.mycoll.validate(  ) - SLOW
  db.mycoll.getShardVersion() - only for use with sharding
  db.mycoll.getShardDistribution() - prints statistics about data distribution in the cluster

みつからず。

mycollってのはコレクションのことっぽい。

ていうか、もしかして、
>db.example.save({name:'myname'})
>db.example.find();
{ "_id" : ObjectId("4f69656db3855bee2fdca9f2"), "name" : "myname" }

スキーマレスってそういうことかー。 すごい。
>db.example.save({age: 50})
>db.example.find()
{ "_id" : ObjectId("4f69656db3855bee2fdca9f2"), "name" : "myname" }
{ "_id" : ObjectId("4f696610b3855bee2fdca9f3"), "age" : 50 }

カオス!
> db.example.find({age: 50})
{ "_id" : ObjectId("4f696610b3855bee2fdca9f3"), "age" : 50 }

おもしろい!


まとめ

MongoDBはFacebookのデータ構造に近い。

かねこ(*´σ`)



2012年3月21日水曜日

MongoDB(1) 導入編

MongoDBやります。いい加減、やっとかないとまずい状況です。

特長
・ドキュメント志向(JSON)
・スキーマレス
・簡単なレプリケーション
・自動シャーディング
・高速
・トランザクション機能なし
とか。

とりあえず、環境準備めんどくさいからWindows環境(+XAMPP)で使えるようにしてみる。
どうせクロスプラットフォームだし。


1.ダウンロード

公式サイトよりダウンロード


2.展開と設置

展開して、置けばおしまい。

ディレクトリ構成例
D:\xampp\mongodb
┗bin
┗data
┗log


3.きどう

コマンドプロンプトで、デーモン起動
D:\xampp\mongodb\bin>mongod --logpath=D:\xampp\mongodb\log\mongodb_log.txt --dbpath=D:\xampp\mongodb\data

ログを見ると、
Mon Mar 19 17:18:58
Mon Mar 19 17:18:58 warning: 32-bit servers don't have journaling enabled by default. Please use --journal if you want durability.
Mon Mar 19 17:18:58
Mon Mar 19 17:18:58 [initandlisten] MongoDB starting : pid=4688 port=27017 dbpath=D:\xampp\mongodb\data 32-bit host=e2info9
Mon Mar 19 17:18:58 [initandlisten]
Mon Mar 19 17:18:58 [initandlisten] ** NOTE: when using MongoDB 32 bit, you are limited to about 2 gigabytes of data
Mon Mar 19 17:18:58 [initandlisten] **       see http://blog.mongodb.org/post/137788967/32-bit-limitations
Mon Mar 19 17:18:58 [initandlisten] **       with --journal, the limit is lower
Mon Mar 19 17:18:58 [initandlisten]
Mon Mar 19 17:18:58 [initandlisten] db version v2.0.3, pdfile version 4.5
Mon Mar 19 17:18:58 [initandlisten] git version: 05bb8aa793660af8fce7e36b510ad48c27439697
Mon Mar 19 17:18:58 [initandlisten] build info: windows sys.getwindowsversion(major=6, minor=0, build=6002, platform=2, service_pack='Service Pack 2') BOOST_LIB_VERSION=1_42
Mon Mar 19 17:18:58 [initandlisten] options: { dbpath: "D:\xampp\mongodb\data", logpath: "D:\xampp\mongodb\log\mongodb_log.txt" }
Mon Mar 19 17:18:59 [initandlisten] waiting for connections on port 27017
Mon Mar 19 17:18:59 [websvr] admin web console waiting for connections on port 28017
Mon Mar 19 17:19:59 [clientcursormon] mem (MB) res:14 virt:44 mapped:0
Mon Mar 19 17:19:59 [PeriodicTask::Runner] task: WriteBackManager::cleaner took: 31ms

ちゃんと動いてるっぽい(64bit推奨)。
27017が標準ポート。

Windowsのサービスとして登録する場合は、「--install」
D:\xampp\mongodb\bin>mongod  --install --logpath=D:\xampp\mongodb\log\mongodb_log.txt --dbpath=D:\xampp\mongodb\data

以上。
MongoDBの導入超簡単。


4.PHPとの連携

続いて、PHPにドライバを設定してPHPと連携させてみる。

MongoDBネイティブドライバのインストール説明ページのとおり。
該当のdllダウンロードして、php.iniに設定。
extension=php_mongo.dll




以上。
MongoDBドライバの導入も超簡単。

まとめ

MongoDB超簡単。

かねこ( `д´)