顯示具有 mongod 標籤的文章。 顯示所有文章
顯示具有 mongod 標籤的文章。 顯示所有文章

2014年12月28日 星期日

MongoDB 開發筆記 - 在 MapReduce 進行 debug 的一些心得

有點久沒寫 blog 了!最近有空時就是把玩 MongoDB 或是嘗試送一些 patch ,所以反而就少寫一些筆記,因為都改成用 Code 啦 :P

最近玩 Mongodb MapReduce 時,碰到幾個問題跟需求:
  • 碰到 mongod Out of memory
  • 想要在 Mapper 跟 Reducer 輸出一些 console 訊息
其中 out of memory 的部分,經過 trace 後發現是內部 Mapper 實作的架構採用 C++ Map Structure 紀錄結果,累計處理完一百筆資料會輸出,但如果單一個筆 input 會產生極大數量的結果時,就會出現 out of memory,已經送了個鳥鳥的 patch 出去,就看 MongoDB Reviewer 決定如何 :P 畢竟這種現象也可以歸屬在使用者資料設計問題,為何讓一筆 input 產生巨量資料 XD

至於現況怎樣解套?若碰到 mongod crash 問題,先試試把 jsMode 打開吧!因為 jsMode = true 時,將採用 v8 engine 處理 mapper function,並且資料是直接輸出到 temp collection 的,可避開 out of memory 問題。

對於前期開發時,想在 Mapper 跟 Reducer 印出一些訊息時,此時不能直接用 console.log(obj),要改用 print(JSON.stringify(obj)) ,並且去翻一下 

/var/log/mongodb/mongod.log 吧!

2014年11月3日 星期一

[Linux] 架設 MongoDB Sharded Cluster 與驗證資料分散性 @ Ubuntu 14.04

今年年初時,把玩過 MongoDB Replica Set ,也試過一些 Map-Reduce 計算等,最近終於又開始抽空實驗了 :P 當時看到不少 Sharded Cluster 搭建在 MongoDB Replica Set 身上,深感 process (server) 的數量不低,就遲遲沒跳下去玩,最近才發現就算只是單一個 MongoDB Server 也可以當作 shared Cluster 一員,就方便啦!

此次 MongoDB Sharded Cluster 架構:
  • 跑兩個 MongoDB Server,分別在 port 10001, 10002
  • 跑一個 MongoDB Config Server - port 20001
  • 跑一個 MongoDB Routing Server - port 30001
以下操作全部都在一台 ubuntu 14.04 server:

$ rm -rf /tmp/mongo && mkdir -p /tmp/mongo/db/1 /tmp/mongo/db/2 /tmp/mongo/db/c /tmp/mongo/log

$ mongod --port 10001 --dbpath /tmp/mongo/db/1 --logpath /tmp/mongo/log/1 &
$ mongod --port 10002 --dbpath /tmp/mongo/db/2 --logpath /tmp/mongo/log/2 &
$ mongod --port 20001 --configsvr --dbpath /tmp/mongo/db/c --logpath /tmp/mongo/log/c &

$ mongos --port 30001 --configdb 127.0.0.1:20001 &


設定 partition 用法:

$ mongo --port 30001
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("###############")
}
  shards:
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
mongos> sh.addShard('127.0.0.1:10001')
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard('127.0.0.1:10002')
{ "shardAdded" : "shard0001", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("#####################")
}
  shards:
        {  "_id" : "shard0000",  "host" : "127.0.0.1:10001" }
        {  "_id" : "shard0001",  "host" : "127.0.0.1:10002" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
mongos> sh.enableSharding('ydb')
{ "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("######################")
}
  shards:
        {  "_id" : "shard0000",  "host" : "127.0.0.1:10001" }
        {  "_id" : "shard0001",  "host" : "127.0.0.1:10002" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
        {  "_id" : "ydb",  "partitioned" : true,  "primary" : "shard0000" }
mongos> use ydb
switched to db ydb
mongos> db.ycollection.ensureIndex( { _id : "hashed" } )
{
        "raw" : {
                "127.0.0.1:10001" : {
                        "createdCollectionAutomatically" : true,
                        "numIndexesBefore" : 1,
                        "numIndexesAfter" : 2,
                        "ok" : 1
                }
        },
        "ok" : 1
}
mongos> sh.shardCollection("ydb.ycollection", { "_id": "hashed" } )
{ "collectionsharded" : "ydb.ycollection", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("#####################")
}
  shards:
        {  "_id" : "shard0000",  "host" : "127.0.0.1:10001" }
        {  "_id" : "shard0001",  "host" : "127.0.0.1:10002" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
        {  "_id" : "ydb",  "partitioned" : true,  "primary" : "shard0000" }
                ydb.ycollection
                        shard key: { "_id" : "hashed" }
                        chunks:
                                shard0000       2
                                shard0001       2
                        { "_id" : { "$minKey" : 1 } } -->> { "_id" : NumberLong("-4611686018427387902") } on : shard0000 Timestamp(2, 2)
                        { "_id" : NumberLong("-4611686018427387902") } -->> { "_id" : NumberLong(0) } on : shard0000 Timestamp(2, 3)
                        { "_id" : NumberLong(0) } -->> { "_id" : NumberLong("4611686018427387902") } on : shard0001 Timestamp(2, 4)
                        { "_id" : NumberLong("4611686018427387902") } -->> { "_id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(2, 5)


簡易驗證資料分散性:

mongos>

db.ycollection.insert({user:"a", item: "1", rate: 0.3})
db.ycollection.insert({user:"a", item: "3", rate: 0.5})
db.ycollection.insert({user:"a", item: "4", rate: 0.9})
db.ycollection.insert({user:"b", item: "2", rate: 0.1})
db.ycollection.insert({user:"b", item: "3", rate: 0.6})
db.ycollection.insert({user:"b", item: "5", rate: 0.2})
db.ycollection.insert({user:"c", item: "3", rate: 0.2})
db.ycollection.insert({user:"c", item: "4", rate: 0.7})

mongos> db.ycollection.find()
{ "_id" : ObjectId("#################"), "user" : "a", "item" : "3", "rate" : 0.5 }
{ "_id" : ObjectId("#################"), "user" : "a", "item" : "1", "rate" : 0.3 }
{ "_id" : ObjectId("#################"), "user" : "a", "item" : "4", "rate" : 0.9 }
{ "_id" : ObjectId("#################"), "user" : "b", "item" : "2", "rate" : 0.1 }
{ "_id" : ObjectId("#################"), "user" : "b", "item" : "5", "rate" : 0.2 }
{ "_id" : ObjectId("#################"), "user" : "b", "item" : "3", "rate" : 0.6 }
{ "_id" : ObjectId("#################"), "user" : "c", "item" : "3", "rate" : 0.2 }
{ "_id" : ObjectId("#################"), "user" : "c", "item" : "4", "rate" : 0.7 }


接著,想要驗證資料的確是儲存在不同台 mongodb server ,作法就是把資料透過 mongodump 出來(bson),再用 bsondump 印出來驗證:

$ cd /tmp
$ mongodump --port 10001 -d ydb -c ycollection
$ bsondump dump/ydb/ycollection.bson
{ "_id" : ObjectId( "#############" ), "user" : "a", "item" : "1", "rate" : 0.3 }
{ "_id" : ObjectId( "#############" ), "user" : "b", "item" : "2", "rate" : 0.1 }
{ "_id" : ObjectId( "#############" ), "user" : "b", "item" : "3", "rate" : 0.6 }
{ "_id" : ObjectId( "#############" ), "user" : "c", "item" : "4", "rate" : 0.7 }
4 objects found

$ mongodump --port 10002 -d ydb -c ycollection
$ bsondump dump/ydb/ycollection.bson
{ "_id" : ObjectId( "#############" ), "user" : "a", "item" : "3", "rate" : 0.5 }
{ "_id" : ObjectId( "#############" ), "user" : "a", "item" : "4", "rate" : 0.9 }
{ "_id" : ObjectId( "#############" ), "user" : "b", "item" : "5", "rate" : 0.2 }
{ "_id" : ObjectId( "#############" ), "user" : "c", "item" : "3", "rate" : 0.2 }
4 objects found


因此可完成驗證,資料的確分在不同區!有興趣還可以用 MongoDB Aggregate (Map-Reduce) 實作共現矩陣(Co-Occurrence Matrix)及簡易的推薦系統 來驗證 partition 演算法的正確性。

2014年2月18日 星期二

[Linux] MongoDB Replica Set with Auth & KeyFile @ Ubuntu 12.04

mongod shutdown error

建 cluster 不見得需要帳號管控,因為多數處在保護的網路環境,而 MongoDB 預設沒有帳密管控且帳密管控做的很簡單,分成 read-only 跟 read-write 模式。而使用認證的主因是為了遠端關掉機器,沒有認證機制的服務僅能透過 localhost 關掉服務,有一派說是透過 service mongodb stop 指令,又有一派說直接 kill process 即可,但有文章說如此容易造成問題?還是要用正式的關機流程比較妥( mongo> db.shutdownServer() )。

參考文件:
架設 Replica Set 的流程:
  1. 啟動多台 mongod --replSet set_name
  2. 挑選一台 mongod 登入
  3. 使用 rs.init() 初始化後,再用 rs.add() 加入其他 mongod 或是使用 db.runCommand({'replSetInitiate':{'members':[]}}) 一次新增
架設需要認證的 Replica Set 的流程:
  1. 用 default 模式啟動一台 mongod
  2. 建立帳號
  3. 建立 keyFile (檔案內容隨意,但需要多過6個字元)
  4. 關掉後以 --replSet set_name 與 --keyFile key_file_path 模式啟動多支 mongod
  5. 登入一台 mongod 以及完成帳號認證
  6. 使用 rs.init() 初始化後,再用 rs.add() 加入其他 mongod 或是使用 db.runCommand({'replSetInitiate':{'members':[]}}) 一次新增
上述兩者流程的差別在於後者要先建帳號後,再用 --replSet 跟 --keyFile 模式啟動。而後,為了測試就寫了隻 script 筆記一下:

未使用認證模式:

$ python replset_init.py --reset

Init DB(0), Port: 30000, Path: /home/id/data/mongodb-study/cluster/db/db-0
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-0 --port 30000 --oplogSize 700 --logpath /home/id/data/mongodb-study/cluster/log/db-0.log --rest --replSet firstset

Init DB(1), Port: 30001, Path: /home/id/data/mongodb-study/cluster/db/db-1
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-1 --port 30001 --oplogSize 700 --logpath /home/id/data/mongodb-study/cluster/log/db-1.log --rest --replSet firstset

Init DB(2), Port: 30002, Path: /home/id/data/mongodb-study/cluster/db/db-2
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-2 --port 30002 --oplogSize 700 --logpath /home/id/data/mongodb-study/cluster/log/db-2.log --rest --replSet firstset

nohup: ignoring input and appending output to `nohup.out'
nohup: ignoring input and appending output to `nohup.out'
nohup: ignoring input and appending output to `nohup.out'

Waiting...
        localhost:30000: .....OK
        localhost:30001: OK
        localhost:30002: OK

Connect to localhost:30000

Initialize the First Replica Set:

$ monogo localhost:30000/admin
mongo> db.runCommand( {'replSetInitiate': {'_id': 'firstset', 'members': [{'host': 'localhost:30000', '_id': 1}, {'host': 'localhost:30001', '_id': 2}, {'host': 'localhost:30002', '_id': 3}]}} )

Result:
{u'info': u'Config now saved locally.  Should come online in about a minute.', u'ok': 1.0}

All is done.

server info:
 $ mongo localhost:30000/admin --eval 'printjson(rs.status())'

shutdown servers:
 $ mongo localhost:30000/admin --eval 'db.shutdownServer()'
 $ mongo localhost:30001/admin --eval 'db.shutdownServer()'
 $ mongo localhost:30002/admin --eval 'db.shutdownServer()'


使用認證模式:

$ python replset_init.py --reset --auth-key-file keyfile --auth-user account --auth-pass password

Init DB(0), Port: 30000, Path: /home/id/data/mongodb-study/cluster/db/db-0
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-0 --port 30000 --oplogSize 700 --logpath /home/id/data/mongodb-study/cluster/log/db-0.log --rest
nohup: ignoring input and appending output to `nohup.out'

Waiting...
        localhost:30000: ..OK

Connect to localhost:30000

Initialize the First Replica Set:

$ monogo localhost:30000/admin
mongo> db.addUser( {user: "account", pwd:"password", roles:["userAdminAnyDatabase"] })

Add account done.

Restart the mongod:

Init DB(0), Port: 30000, Path: /home/id/data/mongodb-study/cluster/db/db-0
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-0 --port 30000 --oplogSize 700 --logpath /home/id/data/mongodb-study/cluster/log/db-0.log --rest --replSet firstset --keyFile keyfile

Init DB(1), Port: 30001, Path: /home/id/data/mongodb-study/cluster/db/db-1
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-1 --port 30001 --oplogSize 700 --logpath /home/id/data/mongodb-
study/cluster/log/db-1.log --rest --replSet firstset --keyFile keyfile

nohup: ignoring input and appending output to `nohup.out'
Init DB(2), Port: 30002, Path: /home/id/data/mongodb-study/cluster/db/db-2
 $ mongod --dbpath /home/id/data/mongodb-study/cluster/db/db-2 --port 30002 --oplogSize 700 --logpath /home/id/data/mongodb-
study/cluster/log/db-2.log --rest --replSet firstset --keyFile keyfile

nohup: ignoring input and appending output to `nohup.out'
nohup: ignoring input and appending output to `nohup.out'
nohup: ignoring input and appending output to `nohup.out'

Waiting...
        localhost:30000: .......OK
        localhost:30001: ...................................OK
        localhost:30002: .OK

Connect to localhost:30000

Initialize the First Replica Set:

$ monogo localhost:30000/admin
mongo> db.runCommand( {'replSetInitiate': {'_id': 'firstset', 'members': [{'host': 'localhost:30000', '_id': 1}, {'host': 'localhost:30001', '_id': 2}, {'host': 'localhost:30002', '_id': 3}]}} )

Result:
{u'info': u'Config now saved locally.  Should come online in about a minute.', u'ok': 1.0}

All is done.

server info:
 $ mongo localhost:30000/admin -u account -p password --eval 'printjson(rs.status())'

shutdown servers:
 $ mongo localhost:30000/admin -u account -p password --eval 'db.shutdownServer()'
 $ mongo localhost:30001/admin -u account -p password --eval 'db.shutdownServer()'
 $ mongo localhost:30002/admin -u account -p password --eval 'db.shutdownServer()'