How to Backup and Restore your MongoDB database?

06 February 2014

這篇要講如何Backup和Restore MongoDB,
MongoDB本身提供了2種不同的backup/restore solutions,

  • mongoexport/mongoimport
  • mongodump/mongorestore

還有一些進階的,像是MongoDB Management Service (MMS)
但這裡只介紹這2種比較基本的solutions,
那麼這兩種的差異在哪呢?
第一種的mongoexport/mongoimport其實不太算是backup/restore solution,
因為它只能用來backup collection中的data(collection可以想成relational database的table),
它無法backup整個db的狀態,
在官網中也說明了,
mongoexport/mongoimport在保存BSON上是不可靠的,
所以mongoexport/mongoimport比較適合用在保存簡單的data,
但也可以勉強算是比較簡易型的backup/restore solution,
但儘量別拿來作為backup/restore的方式.原因前面說過了,
把它方法當作data的export和import就好.
如果想要備份較完整的db,那麼就要靠第二種mongodump/mongorestore,
下面分別是這兩種的使用方式.

1. mongoexport/mongoimport

1-1.Backup
mongoexport -h <Host:Port> -d <DB_Name> -c <Collection_Name> -u <DB_User> -p <DB_Password> -o <Output_File_Name>

-h:你的db uri和port number
-d:你要備份的database名稱
-c:要備份哪一個collection(table)
-u:db帳號
-p:db密碼
-o:要備份出來的檔案名稱

1-2. Restore
備份成功以後,你可以試著drop那個collection,
接著在輸入下面的command,應該就會recover成功.
mongoimport -h <Host:Port> -d <DB_Name> -c <Collection_Name> -u <DB_User> -p <DB_Password> --file <File_Name>

參數大部分和backup一樣,
只有--file不同,
--file要接的是剛剛備份出來的檔案名稱,
輸入以後,看到下圖就代表recover成功了




2. mongodump/mongorestore

2-1.Backup
mongodump -h <Host:Port> -d <DB_Name> -c <Collection_Name> -u <DB_User> -p <DB_Password> -o <Directory>

參數也和上面一樣,
但-o是要接目錄位置,mongodump會在該目錄底下建立一個folder來存backup的檔案,

2-2. Restore
mongorestore -h <Host:Port> -d <DB_Name> -c <Collection_Name> -u <DB_User> -p <DB_Password> <Directory>

參數也一樣,
但少了-o,
請在後面直接接上存放backup db的directory.
輸入以後,
你會發現mongorestore不只會幫你recover data,連該collection原有的index也會幫你建好.



結論,
第一種儘量別拿來做backup/restore,其實就連MongoDB官網也沒有把mongoimport/mongoexport歸類在backup/restore的類別下,
就把第一種當作data的export吧!
第二種就是完整的backup/restore solution,就像mysqldump一樣!













read more »


Handle long-running operations in Node.js

27 January 2014

由於Node.js是Single thread的關係,
所以並不適合用來處理耗費CPU或者需要長時間處理的操作,
想要處理cpu-intensive或者long-running computation,
還是要靠一些job server來解決會比較好(像是gearman),
那如果你不想靠其他的job server來做,
硬要用node.js本身來處理cpu-intensive或者long-running computation,
就要靠worker來處理!
這篇就要講怎麼使用worker.
這裡我們要用的workder module是webworker-threads

1. install
npm install webworker-threads --save


2. implement a task.js

首先,我們要先建立一個javascript,
而這隻javascript就是用來處理需要長時間的操作,
這裡我們就以fibonacci來作為長時間處理的example,
// fibonacci function
function fibo (n){
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}

onmessage = function(event) {
postMessage('Result is : ' + fibo(event.data));
self.close();
};

這隻javascript有兩個function,
一個是fibo function,
另外一個就是onmessage,onmessage等等我們主程式會call到它,
然後onmessage裡面就會去call fibo function.


3. integrate with your main.js

在這步就在你的main.js中使用webworker-threads,以及呼叫我們剛剛寫的那隻task.js,
var express = require("express");
var app = express();
app.use(express.logger());

// import the module
var worker_thread = require('webworker-threads');
var Worker = worker_thread.Worker;

app.use(express.static(path.join(__dirname, 'view')));

app.get('/fibo', function(req, resp) {

var worker = new Worker('task.js');

worker.onmessage = function(event) {
console.log("The task said : " + event.data);
};
worker.postMessage(40);

resp.end('hello world ');
});

var port = process.env.PORT || 5000;
app.listen(port, function() {
console.log("Listening on " + port);
});

這個example,一樣有使用到express.js,
不知道怎麼使用的人可以去看這一篇:Deploying a Node.js application to Heroku
webworker-threads的使用方式很簡單,
只要先import這個module,
然後再呼叫Worker這個instance,
接著就可以去new一個worker出來,
然後就可以指定要worker做什麼事情,
這裡我們指定他去做task.js裡面的事情(new worker('task.js')).

4. test

那要怎麼測試呢?
首先先把我們的main.js叫起來,
node main.js
成功叫起來以後,應該會在你的terminal中看到下面的字樣,


接著就打開我們的browser,
然後輸入“http://0.0.0.0:5000/fibo”,
這時候browser應該很快的就show出hello world的字樣,
然後我們馬上回到terminal看,
你應該只會先看到request的log(如下圖),


接著過了幾秒鐘以後才會看到fibo的計算結果(如下圖),


如果這裡我們沒有使用worker的話,而是直接call hibo,
你會發現很久才可以在browser中看到hello world的字樣!
且甚至你可以試試看開兩個browser,然後都去打“http://0.0.0.0:5000/fibo”,
你會發現第二個打的request會被hang住很久,因為是single thread的關係,
所以thread都在處理第一個request的fibo,處理完才會去處理第二個request!
因此想要在node.js中處理cpu-intensive或者long-running computation,
就使用worker的概念吧!



read more »


[Heroku] Getting Started with MongoDB and Node.js

22 January 2014

上一篇已經講過怎麼在heroku上建立一個node.js的應用,
所以要開始進行這篇前,請先完成上一篇的步驟
而這一篇要講如何在heroku上整合node.js和MongoDB,會實作一個簡單的CRUD!
MongoDB是一種document based的database,
個人認為很適合用來做API Server,

這裡建議大家先去下載mogodb來local,
是為了方便測試,不用每次都deploy至heroku上才可以測試mongodb,
我用的是mac,大家可以到官網看看怎麼安裝mongodb(官網安裝說明)!

那麼怎麼在Heroku上建立一個自己的mongodb呢?
得透過Add-ons來建立!(如下圖)
由於heroku它只是一個PaaS平台,你無法在上面真正的安裝自己“想要”的database,
因此你只能透過add-ons,而這些add-ons都是來自其他third-party tool!

但!透過add-ons建立mongodb之前,
請務必先通過account verification,那怎麼通過呢?
就是新增一張信用卡摟!
在自己的account page最下方有一個Billing選項,
在Billing選項填寫自己的信用卡資訊就完成了.

完成verification以後,我們就可以到add-ons頁面去選擇自己要的db,
可以從data stores選項看到有哪些db可以選,
而我們想要在上面使用mongodb的話,heroku有兩個provider
     1. MongoLab
     2. MongoHQ
在這篇我們使用MongoLab,其實我個人認為兩者差異性不大,
想看兩者detail差異性的可以去看他們各自的Pricing plan.

接著我們就要開始進入主題了,


1. create mongodb

記得進入此步驟的前提是,你已經完成了上一篇的deployment!
接著就進到我們app的folder裡面去,
透過下指令的方式來create monogdb!
這個指令下完以後,會建立一個free的mongodb,所以別擔心會被charge!
heroku addons:add mongolab 


2. install necessary module

想要在node.js中connect mongodb,得透過其他的module,
就我所知有兩種選擇:
     1. mongoose
     2. mongodb
而我們先選擇mongoose,也沒為什麼,我單純覺得mongodb比較麻煩而已.
安裝指令,
npm install mongoose --save
為什麼會有--save?原因是下了--save以後,會自動幫我們把這個module加到package.json裡面!
不用再重新generate一次!


3. create db schema

在這步驟我們要建立我們的db schema,
所以先建立一個schema.js檔案,
vim schema.js

內容如下:
var mongoose = require( 'mongoose' );
var Schema = mongoose.Schema;

var Student = new Schema({
user_id : String,
name : String
});

mongoose.model('Student', Student);

這個schema.js會建立一個Student的schema!


4. development

在這一步驟就要實作CRUD了,
在開始之前,我要先講express.js的一個優點,
express.js這個module可以輕鬆的隔離出production server和development環境出來,
什麼意思呢?來看下面例子就知道!
app.configure('development', function(){
mongoose.connect( 'mongodb://localhost:27017' );
});
app.configure('production', function(){
mongoose.connect( 'mongodb://' + process.env.MONGOLAB_URI );
});

上面例子中有兩種configuration,分別為development, production.
你可以看到在development的時候,我們是去連local的database,
而在production時,就會去連remote的那台database.
基本上你在自己的電腦上永遠都會是development,
那你可能會好奇,該怎麼讓heroku上吃到的config是production?
恩,很簡單,透過下面指令就可以,
heroku config:set NODE_ENV=production 
下完這指令後,你在heroku上的application就會被restart了!

好了,回到主題來,
接著就要開始實作CRUD了,
mongoose提供了很多方便的API讓我們可以直接對mogondb做select, update, insert等操作,
大家可以去官網看看,
這裡我們用到了下面四個API,
     1. save:
          就是insert
     2. find: select all
     3. findOneAndUpdate:
          就是update,可以透過簡單的condition去做update,
          就像是傳統sql的where
          ex: update xxx set xxx=xxx where id = xxx;
     4. findByIdAndRemove
          透過id去delete

var express = require("express");
var mongoose= require('mongoose');
require( './shcema' );

var app = express();
app.use(express.logger());


app.configure('development', function(){
mongoose.connect( 'mongodb://localhost:27017' );
});
app.configure('production', function(){
mongoose.connect( 'mongodb://' + process.env.MONGOLAB_URI );
});

var Student = mongoose.model('Student');

// C: create
app.get('/insert', function(req, resp) {
var s = new Student( {name: req.query.name} );
s.save(function(err,student){
resp.redirect('/select');
});
});

// R: read
app.get('/select', function(req, resp) {
Student.find(function(err, students){
resp.send(students);
});
});

// U: update
app.get('/update', function(req, resp) {
var query = { name: req.query.old_name };
Student.findOneAndUpdate(query, { name: req.query.new_name }, function(err, students){
resp.redirect('/select');
});
});

// D: delete
app.get('/delete', function(req, resp) {
Student.findByIdAndRemove(req.query.id, function(err, students){
resp.redirect('/select?id='+req.query.id);
});
});

var port = process.env.PORT || 5000;
app.listen(port, function() {
console.log("Listening on " + port);
});


完成以後,接著就是下一步的deploy!

5. deployment

git add . 
git cm -m 'add db'
git push


6. test

完成deploy之後就可以測試了,
你也可以在local測試,但是記得在你自己local要安裝mongodb就是了.

新增:首先先試試看新增 http://your-app-name.herokuapp.com/insert?name=hello_world
查詢:新增完成以後,在去查詢看看 (http://your-app-name.herokuapp.com/select
          應該會看到一筆剛剛新增的那筆資料,先把id記起來,等等要刪除用的!
更新:http://your-app-name.herokuapp.com/update?old_name=hello_world&new_name=ken
          更新成功以後,再去前面的查詢頁面看,應該會發現該筆資料的名稱變成ken了!
刪除:http://your-app-name.herokuapp.com/delete?id=xxxxxxxxxxxxxx
          這時候我們再去查詢頁面看,應該就沒有剛剛那筆資料了!



這樣就完成了一個簡單的CRUD application,
這個例子為了快速做好POC,
所以簡略了一些,像是在做insert時,應該用post會比較好,
但因為在express.js之中要用post,還要額外的configuration,
且用get較容易demo,為了讓大家快速了解mongoDB怎麼操作,
所以就簡略了其他不必要的部分!






read more »